# ============================================================================== # Author: Simon-Pierre Boucher # File: tests/test_ethics.py # Desc: Contrôle éthique automatisé (§18) — aucune donnée privée en base, # régime des mineurs, opt-out persistant et respecté # ============================================================================== import json from creaka import db, ethics from creaka.identity import account from creaka.schema import Creator def _creator(name, accounts, **kw): return Creator(source="test", external_id="", display_name=name, platforms=accounts, **kw).finalize() def test_scrub_donnees_privees(): cr = _creator("Nom Public", [account("instagram", "nompublic", "listes_medias")], bio="Rejoins-moi au 514 555-1234 ou perso@gmail.com, " "123, rue Exemple app. 4") ethics.scrub(cr) assert "514" not in cr.bio assert "@gmail" not in cr.bio assert "rue Exemple" not in cr.bio def test_courriel_perso_jamais_conserve(): cr = _creator("Nom Public", [account("instagram", "nompublic", "listes_medias")], business_contact="perso@hotmail.com") ethics.scrub(cr) assert cr.business_contact is None cr2 = _creator("Nom Public", [account("instagram", "nompublic", "listes_medias")], business_contact="booking@agence-exemple.ca") ethics.scrub(cr2) assert cr2.business_contact == "booking@agence-exemple.ca" # pro affiché → OK def test_regime_mineur(): cr = _creator("Jeune Créateur", [account("tiktok", "jeune", "listes_medias")], is_minor=True, region="Montréal", city="Montréal", business_contact="pro@agence.ca") ethics.scrub(cr) assert cr.business_contact is None assert cr.region is None and cr.city is None def test_optout_persistant_et_exclu(tmp_path, monkeypatch): monkeypatch.setattr(ethics, "OPTOUT_PATH", tmp_path / "optout.json") monkeypatch.setattr(ethics, "OPTOUT_REQUESTS_PATH", tmp_path / "req.jsonl") con = db.connect(tmp_path / "test.db") cr = _creator("Créatrice Retrait", [account("instagram", "retrait.exemple", "listes_medias")]) db.sync_source(con, "test", [cr]) assert db.search(con, q="retrait")["total"] == 1 # demande de retrait → masquée immédiatement ethics.add_optout(account="instagram:retrait.exemple", reason="test") assert db.apply_optout(con, account="instagram:retrait.exemple") == 1 assert db.search(con, q="retrait")["total"] == 0 # ré-agrégation future → JAMAIS réintégrée (liste de suppression, §15) cr2 = _creator("Créatrice Retrait", [account("instagram", "retrait.exemple", "link_in_bio")]) stats = db.sync_source(con, "test", [cr2]) assert stats["optout_skipped"] == 1 assert db.search(con, q="retrait")["total"] == 0 persisted = json.loads((tmp_path / "optout.json").read_text()) assert "instagram:retrait.exemple" in persisted["accounts"] def test_mineurs_exclus_de_lannuaire_public(tmp_path): con = db.connect(tmp_path / "test2.db") cr = _creator("Jeune Vedette", [account("tiktok", "jeunevedette", "listes_medias")], is_minor=True) db.sync_source(con, "test", [cr]) assert db.search(con)["total"] == 0 # prudence extrême §15