187 lines
5.5 KiB
Python
187 lines
5.5 KiB
Python
import os
|
|
import tempfile
|
|
import unittest
|
|
from datetime import date
|
|
|
|
from werkzeug.security import generate_password_hash
|
|
|
|
from app import create_app
|
|
from app.database import get_db, init_db
|
|
|
|
|
|
class AppTestCase(unittest.TestCase):
|
|
def setUp(self):
|
|
self.db_fd, self.db_path = tempfile.mkstemp()
|
|
self.app = create_app(
|
|
{
|
|
"TESTING": True,
|
|
"DATABASE": self.db_path,
|
|
"SECRET_KEY": "test-secret",
|
|
}
|
|
)
|
|
|
|
with self.app.app_context():
|
|
init_db()
|
|
db = get_db()
|
|
db.execute(
|
|
"""
|
|
INSERT INTO admins (username, password_hash)
|
|
VALUES (?, ?)
|
|
""",
|
|
("admin", generate_password_hash("password")),
|
|
)
|
|
db.commit()
|
|
|
|
self.client = self.app.test_client()
|
|
|
|
def tearDown(self):
|
|
os.close(self.db_fd)
|
|
os.unlink(self.db_path)
|
|
|
|
def seed_question(self, question_date=None):
|
|
if question_date is None:
|
|
question_date = date.today().isoformat()
|
|
|
|
with self.app.app_context():
|
|
db = get_db()
|
|
db.execute(
|
|
"""
|
|
INSERT INTO questions (question, option_a, option_b, date)
|
|
VALUES (?, ?, ?, ?)
|
|
""",
|
|
("Would you rather test A or test B?", "Test A", "Test B", question_date),
|
|
)
|
|
db.commit()
|
|
|
|
def login(self):
|
|
return self.client.post(
|
|
"/admin/login",
|
|
data={"username": "admin", "password": "password"},
|
|
follow_redirects=True,
|
|
)
|
|
|
|
def test_public_empty_state(self):
|
|
response = self.client.get("/")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(b"No question is scheduled yet.", response.data)
|
|
|
|
def test_voting_flow_blocks_duplicate_vote(self):
|
|
self.seed_question()
|
|
|
|
first = self.client.post("/vote", data={"qid": "1", "vote": "A"})
|
|
second = self.client.post("/vote", data={"qid": "1", "vote": "A"})
|
|
|
|
self.assertEqual(first.status_code, 302)
|
|
self.assertEqual(second.status_code, 302)
|
|
|
|
with self.app.app_context():
|
|
count = get_db().execute("SELECT COUNT(*) FROM votes").fetchone()[0]
|
|
|
|
self.assertEqual(count, 1)
|
|
|
|
def test_homepage_uses_todays_question_only(self):
|
|
self.seed_question("2030-01-01")
|
|
response = self.client.get("/")
|
|
|
|
self.assertIn(b"No question is scheduled yet.", response.data)
|
|
|
|
self.seed_question()
|
|
response = self.client.get("/")
|
|
|
|
self.assertIn(b"Would you rather test A or test B?", response.data)
|
|
|
|
def test_vote_rejects_non_today_question(self):
|
|
self.seed_question("2030-01-01")
|
|
|
|
response = self.client.post("/vote", data={"qid": "1", "vote": "A"})
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
|
|
with self.app.app_context():
|
|
count = get_db().execute("SELECT COUNT(*) FROM votes").fetchone()[0]
|
|
|
|
self.assertEqual(count, 0)
|
|
|
|
def test_admin_login_success_and_failure(self):
|
|
failed = self.client.post(
|
|
"/admin/login",
|
|
data={"username": "admin", "password": "wrong"},
|
|
)
|
|
self.assertIn(b"Invalid username or password.", failed.data)
|
|
|
|
success = self.login()
|
|
self.assertIn(b"Questions", success.data)
|
|
|
|
def test_protected_route_redirects_to_login(self):
|
|
response = self.client.get("/admin/dashboard")
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIn("/admin/login", response.headers["Location"])
|
|
|
|
def test_question_create_duplicate_date_failure(self):
|
|
self.seed_question()
|
|
self.login()
|
|
|
|
response = self.client.post(
|
|
"/admin/question/new",
|
|
data={
|
|
"date": date.today().isoformat(),
|
|
"question": "Another question?",
|
|
"option_a": "A",
|
|
"option_b": "B",
|
|
},
|
|
)
|
|
|
|
self.assertIn(b"Date already has a question.", response.data)
|
|
|
|
def test_admin_can_change_credentials(self):
|
|
self.login()
|
|
|
|
response = self.client.post(
|
|
"/admin/account",
|
|
data={
|
|
"username": "newadmin",
|
|
"current_password": "password",
|
|
"new_password": "new-password",
|
|
"confirm_password": "new-password",
|
|
},
|
|
follow_redirects=True,
|
|
)
|
|
|
|
self.assertIn(b"Admin credentials updated.", response.data)
|
|
|
|
self.client.get("/admin/logout")
|
|
|
|
old_login = self.client.post(
|
|
"/admin/login",
|
|
data={"username": "admin", "password": "password"},
|
|
)
|
|
self.assertIn(b"Invalid username or password.", old_login.data)
|
|
|
|
new_login = self.client.post(
|
|
"/admin/login",
|
|
data={"username": "newadmin", "password": "new-password"},
|
|
follow_redirects=True,
|
|
)
|
|
self.assertIn(b"Questions", new_login.data)
|
|
|
|
def test_admin_account_requires_current_password(self):
|
|
self.login()
|
|
|
|
response = self.client.post(
|
|
"/admin/account",
|
|
data={
|
|
"username": "newadmin",
|
|
"current_password": "wrong",
|
|
"new_password": "new-password",
|
|
"confirm_password": "new-password",
|
|
},
|
|
)
|
|
|
|
self.assertIn(b"Current password is incorrect.", response.data)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|