Créa·Ka — annuaire public cross-plateforme des créateurs de contenu québécois (crea-ka.com)
Python 73.6%
HTML 13.2%
TypeScript 6%
JavaScript 4.5%
CSS 1.7%
Dockerfile 0.6%
1# ==============================================================================2# Author: Simon-Pierre Boucher <contact@spboucher.ai>3# File: tests/test_perimetre_qc.py4# Desc: Périmètre québécois (§1-2) — garde QC de la boule de neige (les5# profils reliés IG ramènent des vedettes mondiales sans filtre) et6# rapprochement par compte identique dans sync_source (anti-doublons)7# ==============================================================================8"""Régressions du 2026-08-20 : vedettes américaines + fiches en double.9101. snowball-ig émettait TOUS les profils reliés Instagram (cristiano,11 selenagomez, ferrari, nasa… — 3 767 fiches hors-périmètre en un passage).122. `_find_existing` exigeait une confiance forte des DEUX côtés : une fiche à13 signal `mention` (0.60) n'était jamais retrouvée → sync_source créait une14 seconde fiche et l'upsert des comptes lui réassignait le compte, laissant15 l'originale orpheline (691 doublons).16"""17from creaka import db18from creaka.connectors.snowball import SnowballConnector19from creaka.identity import account20from creaka.schema import Creator212223def _creator(name, accounts, **kw):24 return Creator(source="test", external_id="", display_name=name,25 platforms=accounts, **kw).finalize()262728def _source_avec_relies(related):29 acc = account("instagram", "createur.qc", "profil_source").finalize()30 acc.metrics["related_profiles"] = related31 return _creator("Créateur QC", [acc])323334def test_snowball_ecarte_les_vedettes_mondiales():35 src = _source_avec_relies([36 {"username": "cristiano", "full_name": "Cristiano Ronaldo"},37 {"username": "selenagomez", "full_name": "Selena Gomez"},38 {"username": "ferrari", "full_name": "Ferrari"},39 {"username": "nasa", "full_name": "NASA"},40 ])41 assert SnowballConnector().enrich([src]) == []424344def test_marqueur_queb_exige_fin_de_mot():45 # faux positifs constatés le 2026-08-20 : « queb » nu matchait dans46 # « quebra » (portugais) et « jacquebrazil »47 from creaka.connectors.youtube_recherche import is_quebec48 assert not is_quebec("tatiquebrabarraco Tati Quebra Barraco")49 assert not is_quebec("jacquebrazil")50 assert is_quebec("onlyqueb") # queb en fin de mot51 assert is_quebec("grainsduquebec") # quebec collé dans un handle52 assert is_quebec("Créatrice québécoise à Montréal")535455def test_snowball_ecarte_les_organismes_qc():56 # revue du 2026-08-20 : villes, tourisme, ministères, partis, festivals…57 # portent des marqueurs QC mais ne sont pas des créateurs58 src = _source_avec_relies([59 {"username": "villequebec", "full_name": "Ville de Québec"},60 {"username": "tourismecharlevoix", "full_name": "Tourisme Charlevoix"},61 {"username": "transports_quebec", "full_name": "Transports Québec"},62 {"username": "justepourrirequebec", "full_name": "Juste pour rire"},63 {"username": "humoriste.mtl", "full_name": "Humoriste MTL"},64 ])65 handles = {c.platforms[0].handle66 for c in SnowballConnector().enrich([src])}67 assert handles == {"humoriste.mtl"}686970def test_snowball_garde_les_profils_marques_qc():71 src = _source_avec_relies([72 {"username": "humoriste.mtl", "full_name": "Humoriste MTL"},73 {"username": "chroniques.quebec", "full_name": "Chroniques"},74 {"username": "katyperry", "full_name": "Katy Perry"},75 ])76 handles = {c.platforms[0].handle77 for c in SnowballConnector().enrich([src])}78 assert handles == {"humoriste.mtl", "chroniques.quebec"}798081def test_compte_identique_fusionne_jamais_de_doublon(tmp_path):82 con = db.connect(tmp_path / "t.db")83 faible = _creator("misslavoie",84 [account("instagram", "misslavoie", "mention")])85 s1 = db.sync_source(con, "snowball-ig", [faible])86 assert s1["added"] == 187 # ré-observation du MÊME compte avec signal fort (ex. enrichissement) :88 # la fiche existante est réutilisée, jamais de seconde fiche89 fort = _creator("Jade Lavoie",90 [account("instagram", "misslavoie", "profil_source")])91 s2 = db.sync_source(con, "autre-source", [fort])92 assert s2["added"] == 0 and s2["updated"] == 193 assert con.execute("SELECT count(*) FROM creators").fetchone()[0] == 194 # aucun orphelin : chaque fiche possède encore son compte95 orphans = con.execute(96 "SELECT count(*) FROM creators WHERE id NOT IN "97 "(SELECT creator_id FROM accounts)").fetchone()[0]98 assert orphans == 099