"""Tests for the 6 legal pages blueprint (Task B-2.9). All 6 markdown-rendered pages plus the index must: - Return HTTP 200 with DictIA branding - Be publicly indexable (no X-Robots-Tag noindex header — Loi 25 transparency) - Share the same _layout.html structure (extends marketing/base.html) - Be marked DRAFT pending legal review by Allison Rioux - The privacy policy must satisfy the 12 mandatory Loi 25 sections - LEGAL_VERSION constant must match SIGNUP_LEGAL_VERSION used by the signup route """ import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) os.environ.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:') os.environ.setdefault('SECRET_KEY', 'test-secret-key') from src.app import app, db # noqa: E402 VALID_PAGES = ('conditions', 'confidentialite', 'cookies', 'remboursement', 'accessibilite', 'mentions') def test_legal_index_returns_200_with_all_6_pages_listed(): with app.app_context(): db.create_all() try: client = app.test_client() resp = client.get('/legal/') assert resp.status_code == 200 body = resp.data.decode('utf-8') for page in VALID_PAGES: assert f'/legal/{page}' in body assert 'Documents légaux' in body finally: db.session.rollback(); db.drop_all() def test_each_legal_page_returns_200_with_dictia_branding(): with app.app_context(): db.create_all() try: client = app.test_client() for page in VALID_PAGES: resp = client.get(f'/legal/{page}') assert resp.status_code == 200, f'/legal/{page} returned {resp.status_code}' body = resp.data.decode('utf-8') assert 'DictIA' in body assert 'rprp@dictia.ca' in body or 'info@dictia.ca' in body finally: db.session.rollback(); db.drop_all() def test_unknown_legal_page_returns_404(): with app.app_context(): db.create_all() try: client = app.test_client() resp = client.get('/legal/unknown-page') assert resp.status_code == 404 finally: db.session.rollback(); db.drop_all() def test_confidentialite_has_all_12_loi25_sections(): """LPRPSP (Loi 25) requires 12 mandatory sections in privacy policy.""" with app.app_context(): db.create_all() try: client = app.test_client() resp = client.get('/legal/confidentialite') assert resp.status_code == 200 body = resp.data.decode('utf-8').lower() required_topics = [ 'identité du responsable', 'rprp', # responsable de la protection 'renseignements personnels collectés', 'finalités', 'base légale', # base légale et consentement 'destinataires', # destinataires et sous-traitants 'transferts hors québec', # canonical PDC §11 wording (no hyphen, plural) 'durée de conservation', 'droits', # droits de l'utilisateur 'plainte', # procédure de plainte CAI 'cookies', # cookies et traceurs 'biométriques', # données biométriques (LCCJTI 44-45) — ajout 2026-04-27 'décisions automatisées', # ajout 2026-04-27 (PDC §10) 'date de mise à jour', ] for topic in required_topics: assert topic in body, f'Missing Loi 25 mandatory section: {topic!r}' finally: db.session.rollback(); db.drop_all() def test_legal_pages_use_layout_template_with_shared_layout(): """All 6 pages should share the same _layout.html structure.""" with app.app_context(): db.create_all() try: client = app.test_client() for page in VALID_PAGES: resp = client.get(f'/legal/{page}') body = resp.data.decode('utf-8') assert 'Document légal DictIA' in body, f'_layout.html header missing on /legal/{page}' assert 'Index des documents légaux' in body, f'_layout.html footer link missing on /legal/{page}' finally: db.session.rollback(); db.drop_all() def test_legal_pages_publicly_indexable(): """legal.* endpoints must NOT have X-Robots-Tag noindex header (Loi 25 transparency).""" with app.app_context(): db.create_all() try: client = app.test_client() for page in VALID_PAGES: resp = client.get(f'/legal/{page}') tag = resp.headers.get('X-Robots-Tag', '') assert 'noindex' not in tag, f'/legal/{page} has noindex header: {tag!r}' # Also test the index resp = client.get('/legal/') tag = resp.headers.get('X-Robots-Tag', '') assert 'noindex' not in tag finally: db.session.rollback(); db.drop_all() def test_legal_version_constant_matches_signup(): """LEGAL_VERSION in src/legal must equal SIGNUP_LEGAL_VERSION used by signup route.""" from src.legal import LEGAL_VERSION from src.api.auth import SIGNUP_LEGAL_VERSION assert LEGAL_VERSION == SIGNUP_LEGAL_VERSION, ( f'LEGAL_VERSION ({LEGAL_VERSION!r}) must match SIGNUP_LEGAL_VERSION ({SIGNUP_LEGAL_VERSION!r})' ) def test_legal_pages_extend_marketing_base_template(): """All 6 pages extend marketing/base.html (verify by looking for header markers).""" with app.app_context(): db.create_all() try: client = app.test_client() for page in VALID_PAGES: resp = client.get(f'/legal/{page}') body = resp.data.decode('utf-8') # marketing/base.html has the glassmorphism header at the top assert 'class="fixed top-0' in body, f'/legal/{page} missing marketing/base.html header' finally: db.session.rollback(); db.drop_all() def test_legal_pages_have_loi25_draft_callout(): """All 6 pages should be marked DRAFT pending legal review by Allison.""" with app.app_context(): db.create_all() try: client = app.test_client() for page in VALID_PAGES: resp = client.get(f'/legal/{page}') body = resp.data.decode('utf-8').lower() assert 'draft' in body or 'allison rioux' in body, ( f'/legal/{page} missing draft+legal-review callout' ) finally: db.session.rollback(); db.drop_all()