Files
colibre/tests/admin/test_pages.py
T
Colin Maudry 7cc1fadf0f test(admin): add end-to-end Selenium coverage for the admin panel
Covers anonymous → 404, non-admin → 404, and the full admin flow
(list, detail, status change, journal) through a real login and a
real running app. tests/users.test.sqlite is committed and shared by
the whole Selenium session, so the test cleanup also resets the
sqlite_sequence high-water marks that plain DELETEs don't roll back,
keeping the file byte-stable across runs. This run additionally bakes
in migration 0006_create_admin_actions (new admin_actions table), the
first time any Selenium test has booted the real app since that
migration was added — the same one-time process by which migrations
0001-0005 already ended up committed in this fixture.
2026-07-03 10:34:35 +02:00

119 lines
4.4 KiB
Python

import uuid
from dash.testing.composite import DashComposite
from selenium.webdriver.support.ui import Select
from werkzeug.security import generate_password_hash
from src.auth import db as auth_db
from src.subscriptions import db as sub_db
PASSWORD = "s3cretpass!"
def _unique_email(prefix: str) -> str:
return f"{prefix}-{uuid.uuid4().hex[:8]}@ex.fr"
def _make_verified_user(email: str) -> int:
auth_db.init_schema()
uid = auth_db.create_user(email, generate_password_hash(PASSWORD))
auth_db.set_email_verified(uid)
return uid
def _cleanup_user(user_id: int) -> None:
conn = auth_db.get_conn()
conn.execute("DELETE FROM admin_actions WHERE target_user_id = ?", (user_id,))
conn.execute("DELETE FROM subscriptions WHERE user_id = ?", (user_id,))
conn.execute("DELETE FROM subscriber_state WHERE user_id = ?", (user_id,))
conn.execute("DELETE FROM users WHERE id = ?", (user_id,))
# tests/users.test.sqlite is committed to git and shared by the whole
# Selenium session (USERS_DB_PATH is set globally in pyproject.toml).
# Row DELETEs alone leave the file byte-diffed: AUTOINCREMENT tables
# record their high-water mark in sqlite_sequence, and that table is
# never rolled back by DELETE (by SQLite design). Reset it explicitly so
# a full test run leaves the committed file untouched (`git status`
# clean), matching the pre-existing convention for this file where
# users/subscriptions already sit at seq=0.
conn.execute(
"UPDATE sqlite_sequence SET seq = 0 "
"WHERE name IN ('users', 'subscriptions', 'admin_actions')"
)
def _login(dash_duo: DashComposite, email: str):
dash_duo.driver.get(dash_duo.server_url + "/connexion")
dash_duo.wait_for_element("input[name=email]", timeout=8).send_keys(email)
dash_duo.driver.find_element("css selector", "input[name=password]").send_keys(
PASSWORD
)
dash_duo.driver.find_element("css selector", "button[type=submit]").click()
def test_admin_anonymous_gets_404(dash_duo: DashComposite):
from src.app import app
dash_duo.start_server(app)
dash_duo.driver.get(dash_duo.server_url + "/admin")
dash_duo.wait_for_text_to_equal("#admin-404-heading", "404", timeout=8)
def test_admin_non_admin_gets_404(dash_duo: DashComposite, monkeypatch):
from src.app import app
monkeypatch.setenv("ADMIN_EMAIL", "admin-only@ex.fr")
email = _unique_email("regular")
uid = _make_verified_user(email)
try:
dash_duo.start_server(app)
_login(dash_duo, email)
dash_duo.driver.get(dash_duo.server_url + "/admin")
dash_duo.wait_for_text_to_equal("#admin-404-heading", "404", timeout=8)
finally:
_cleanup_user(uid)
def test_admin_full_flow(dash_duo: DashComposite, monkeypatch):
from src.app import app
admin_email = _unique_email("admin")
monkeypatch.setenv("ADMIN_EMAIL", admin_email)
admin_uid = _make_verified_user(admin_email)
target_email = _unique_email("target")
target_uid = _make_verified_user(target_email)
sub_db.init_schema()
_handle, sub_id = sub_db.create_pending(target_uid, "cust-e2e", "simple", 20.0)
sub_db.set_status(sub_id, "active")
try:
dash_duo.start_server(app)
_login(dash_duo, admin_email)
dash_duo.driver.get(dash_duo.server_url + "/admin")
dash_duo.wait_for_text_to_equal("h2", "Panneau admin", timeout=8)
assert target_email in dash_duo.driver.page_source
dash_duo.driver.get(f"{dash_duo.server_url}/admin/user/{target_uid}")
dash_duo.wait_for_text_to_equal("h2", "Panneau admin", timeout=8)
assert target_email in dash_duo.driver.page_source
select = Select(
dash_duo.driver.find_element("css selector", "select[name=status]")
)
select.select_by_value("cancelled")
dash_duo.driver.find_element("css selector", "button[type=submit]").click()
dash_duo.wait_for_text_to_equal(
".alert-success", "Statut de l'abonnement mis à jour.", timeout=8
)
assert "cancelled" in dash_duo.driver.page_source
dash_duo.driver.get(dash_duo.server_url + "/admin/journal")
dash_duo.wait_for_text_to_equal("h2", "Journal des actions admin", timeout=8)
assert "subscription_status_change" in dash_duo.driver.page_source
assert "active → cancelled" in dash_duo.driver.page_source
finally:
_cleanup_user(target_uid)
_cleanup_user(admin_uid)