spb/traite-evaluation-qc
Public
1"""Chapitre 10 — vérification des chiffres cités dans le texte, les tableaux et les solutions."""2import sys, pathlib, datetime as dt3import pytest4sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))5import ch10_controle_rapport as cr6import ch10_matrice_normes as mn78@pytest.fixture(scope="module")9def out():10 return cr.main(verbose=False)1112@pytest.fixture(scope="module")13def mat():14 return mn.main(verbose=False)1516# --- contrôle lexical des douze éléments (tableau 10.7, figure grille) -------------------------------------------17def test_douze_elements_definis():18 assert [e.no for e in cr.ELEMENTS] == list(range(1, 13))19 assert len({e.cle for e in cr.ELEMENTS}) == 122021def test_extrait_complet_et_lacunaires(out):22 assert out["manquants"][("D", "strict")] == [] and out["taux"][("D", "strict")] == 1.023 assert out["manquants"][("A", "strict")] == [1, 2, 4, 5, 6, 7, 9, 10, 11, 12]24 assert out["manquants"][("A", "souple")] == [2, 4, 5, 7, 9, 10, 11]25 assert out["manquants"][("B", "strict")] == [6, 10]26 assert out["manquants"][("C", "strict")] == [2, 3, 4, 9, 11, 12]27 assert out["taux"][("A", "strict")] == pytest.approx(2 / 12)28 assert out["taux"][("B", "strict")] == pytest.approx(10 / 12)29 assert out["taux"][("C", "strict")] == pytest.approx(0.5)3031def test_lexique_souple_jamais_plus_severe(out):32 for nom in cr.EXTRAITS:33 s, l = out["manquants"][(nom, "strict")], out["manquants"][(nom, "souple")]34 assert set(l) <= set(s)3536def test_taux_conformite_identite():37 res = {e.cle: (e.no % 2 == 0, None) for e in cr.ELEMENTS}38 assert cr.taux_conformite(res) == pytest.approx(0.5)39 assert cr.manquants(res) == [1, 3, 5, 7, 9, 11]4041def test_controle_insensible_aux_accents_et_casse():42 r1 = cr.controler("DATE D'ÉVALUATION : 1er septembre 2026. J'ATTESTE par la présente.")43 r2 = cr.controler("date d'evaluation : 1er septembre 2026. j'atteste par la presente.")44 assert r1["date"][0] and r1["attestation"][0] and r1 == r24546# --- probabilité de détection par révisions indépendantes (tableau 10.8) ---------------------------------------47def test_p_detection(out):48 assert cr.p_detection(0.5, 1) == 0.5 and cr.p_detection(0.5, 2) == 0.75 and cr.p_detection(0.5, 3) == 0.87549 assert cr.p_detection(0.7, 2) == pytest.approx(0.91) and cr.p_detection(0.7, 3) == pytest.approx(0.973)50 assert cr.p_detection(0.9, 2) == pytest.approx(0.99) and cr.p_detection(0.9, 3) == pytest.approx(0.999)51 assert cr.p_detection(0.0, 5) == 0.0 and cr.p_detection(1.0, 1) == 1.052 vals = [cr.p_detection(0.7, n) for n in range(1, 6)]53 assert all(b > a for a, b in zip(vals, vals[1:]))5455# --- hypothèse extraordinaire : incidence et espérance (section 10.2, exercice 10.5) ---------------------------56def test_incidence_hypothese(out):57 dv, dr = out["incidence_B"]58 assert dv == 37000 and dr == pytest.approx(0.0643, abs=0.0001)59 assert out["esperance_B"] == pytest.approx(597200)60 assert cr.esperance_sous_incertitude(1.0, 612000, 575000) == 61200061 assert cr.esperance_sous_incertitude(0.0, 612000, 575000) == 5750006263# --- échéances réglementaires (exercice 10.2) -----------------------------------------------------------------64def test_conservation_et_formation(out):65 assert out["conservation"] == dt.date(2031, 11, 15)66 f = out["formation"]67 assert f["deficit_total"] == 0 and f["deficit_ethique"] == 1 and f["report_suivant"] == 1 and not f["conforme"]68 g = out["formation_b"]69 assert g["conforme"] and g["report_suivant"] == 370 h = cr.bilan_formation(heures_total=20, heures_ethique=3, report_anterieur=8) # report plafonné à 5 h71 assert h["deficit_total"] == 5 and not h["conforme"]7273# --- statistiques de la matrice des dispositions (tableau 10.9, figure matrice) ---------------------------------74def test_matrice_normes(mat):75 st = mat["stats"]76 assert st["n"] == 100 and st["n_domaines"] == 17 and st["n_chapitres_couverts"] == 2877 assert st["par_domaine"]["OEAQ-NPP"] == 15 and st["par_domaine"]["OEAQ-Déontologie"] == 14 and st["par_domaine"]["OEAQ-Règlements"] == 1278 assert st["par_domaine"]["IVS"] == 11 and st["par_domaine"]["CUSPAP"] == 8 and st["par_domaine"]["IAAO"] == 679 assert st["par_famille"]["Cadres hors Québec"] == 27 and st["par_famille"]["Finance et prudentiel"] == 2080 assert st["n_oeaq"] == 43 and st["n_ch10"] == 5181 assert st["par_chapitre"]["ch10"] == 51 and st["par_chapitre"]["ch31"] == 15 and st["par_chapitre"]["ch20"] == 1482 assert st["moy_chapitres_par_disposition"] == pytest.approx(1.70) and st["max_chapitres_par_disposition"] == 483 assert st["part_multi_chapitres"] == pytest.approx(0.51)84 assert st["par_famille"].sum() == 1008586def test_chronologie(mat):87 ch = mat["chronologie"].set_index("cadre")88 assert ch.loc["CUSPAP 2024 (Canada)", "en_vigueur_2026_10_31"] == False89 assert ch.loc["IVS 2028 — projet (exposure draft)", "en_vigueur_2026_10_31"] == False90 assert ch["en_vigueur_2026_10_31"].sum() == 891 assert ch.loc["IVS 2025 (IVSC)", "delai_publication_effet_jours"] == 36692 assert ch.loc["RICS Red Book Global (2025)", "delai_publication_effet_jours"] == 5193 assert ch.loc["NPP de l'OEAQ (Québec)", "effet"] == dt.date(2024, 2, 1)94 assert ch.loc["CUSPAP 2026 (Canada)", "effet"] == dt.date(2026, 4, 1)95