Download tests/test_decisive_card.py from Beemer0/CanLex: direct link, hf CLI and curl.
- Browser
- Download file 33.9 kB
-
https://huggingface.co/spaces/Beemer0/CanLex/resolve/main/tests/test_decisive_card.py
- Command line
-
hf download hf://spaces/Beemer0/CanLex/tests/test_decisive_card.py
-
curl -L -o test_decisive_card.py https://huggingface.co/spaces/Beemer0/CanLex/resolve/main/tests/test_decisive_card.py
33.9 kB
| """The pure chain, the decisive facts, the confidence block, the result card | |
| and completion IF SERVED AS IMPOSED (roadmap T4.1, T4.2, T4.7, T4.5; track A | |
| step A14, 2026-09-15). | |
| The chain is a refactor with no verdict change of its own (proved against | |
| the tree before the step: every screening case and a 60-record set gave the | |
| same CANLEX_RESULT fields and the same memo text once the new blocks are | |
| removed -- scratchpad a14_diff.py). These tests hold what the step adds: | |
| - the decisive facts list only facts whose values change the branch, the | |
| allegation paragraph or the rehabilitation status, and fold into ASSUMED; | |
| - every screening tool's CANLEX_RESULT carries 'confidence'; | |
| - detail 'card' gives about ten lines and the same result line; | |
| - the IF SERVED EXACTLY AS IMPOSED footing never gives a status, and a fine | |
| blocks it; its Guide 5312 rules are verbatim in the corpus; | |
| - conviction_date bounds the Tran maximum; | |
| - all of it runs on the APPROVED curated JSON (a composed release). | |
| Offline over the real curated files and a stub index.""" | |
| import datetime as dt | |
| import json | |
| import subprocess | |
| import unittest | |
| from pathlib import Path | |
| from unittest import mock | |
| ROOT = Path(__file__).resolve().parent.parent | |
| PROCESSED = ROOT / "data" / "processed" | |
| T = "2026-09-15" | |
| def result_of(out): | |
| lines = [l for l in out.splitlines() if l.startswith("CANLEX_RESULT: ")] | |
| return json.loads(lines[-1][len("CANLEX_RESULT: "):]) | |
| def _chunks(name): | |
| path = PROCESSED / name | |
| if not path.exists(): | |
| return None | |
| data = json.loads(path.read_text(encoding="utf-8")) | |
| return data["chunks"] if isinstance(data, dict) else data | |
| class _Base(unittest.TestCase): | |
| def setUpClass(cls): | |
| from canlex import server | |
| cls.s = server | |
| cls._saved = server._INDEX | |
| server._INDEX = object.__new__(type("Stub", (), {"semantic": False})) | |
| def tearDownClass(cls): | |
| cls.s._INDEX = cls._saved | |
| def record(self, **kw): | |
| kw.setdefault("today", T) | |
| return self.s.canlex_screen_record(**kw) | |
| def history(self, entries, **kw): | |
| kw.setdefault("today", T) | |
| return self.s.canlex_screen_history(json.dumps(entries), **kw) | |
| def decisive(r): | |
| return {d["fact"]: d for d in r["decisive_missing"]} | |
| class ChainTests(_Base): | |
| def test_the_chain_without_text_gives_the_tools_answer(self): | |
| """render=False (the decisive re-runs) and the memo agree.""" | |
| from canlex.screening import parse_date | |
| cases = [dict(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2016-01-01"), | |
| dict(offense="theft", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", sentence_completed="2018-06-01"), | |
| dict(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01"), | |
| dict(offense="assault", disposition="withheld adjudication", state="FL", | |
| offence_date="2016-01-01"), | |
| dict(offense="attempted burglary", disposition="convicted", state="CA", | |
| offence_date="2005-01-01", sentence_completed="2007-01-01")] | |
| for c in cases: | |
| r = result_of(self.record(**c)) | |
| j = self.s._jurisdiction(c.get("state"), None) | |
| ch = self.s._chain_entry(c["offense"], c["disposition"], j, | |
| parse_date(c.get("offence_date")), | |
| parse_date(c.get("sentence_completed")), | |
| "foreign-national", dt.date.fromisoformat(T)) | |
| eq = ch["eq"] or {} | |
| self.assertEqual((eq.get("pairing"), eq.get("branch"), eq.get("max_years")), | |
| (r["pairing"], r["branch"], r["max_years"]), c) | |
| self.assertEqual((ch["rehab"] or {}).get("status"), r.get("rehab_status"), c) | |
| def test_allegation_paragraphs_are_the_ones_drafted(self): | |
| p = {"canadian_offence": "X"} | |
| for branch in ("serious-criminality", "criminality", "criminality-two-plus-only"): | |
| for conv, unknown in ((True, False), (False, False), (False, True)): | |
| for count in ("one", "two-plus-summary-only"): | |
| lines = " ".join(self.s._allegation_lines({"branch": branch}, p, conv, | |
| unknown, count)) | |
| paras = [x for x, _ in self.s._allegation_paras(branch, conv, unknown, count)] | |
| for para in paras: | |
| self.assertIn(f"IRPA paragraph {para} ", lines, (branch, conv, count)) | |
| self.assertEqual(lines.count("Draft allegation paragraph"), len(paras), | |
| (branch, conv, unknown, count)) | |
| class DecisiveFactsTests(_Base): | |
| def test_a_straddled_maximum_lists_the_offence_date(self): | |
| r = result_of(self.record(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01")) | |
| d = self.decisive(r)["offence_date"] | |
| self.assertEqual(sorted(v["branch"] for v in d["values"]), | |
| ["criminality", "serious-criminality"]) | |
| self.assertEqual({tuple(v["paragraphs"]) for v in d["values"]}, | |
| {("36(2)(b)",), ("36(1)(b)",)}) | |
| def test_a_tie_lists_its_deciding_fact(self): | |
| r = result_of(self.record(offense="theft", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", sentence_completed="2018-06-01")) | |
| d = self.decisive(r)["value_over_5000"] | |
| self.assertEqual(sorted(v["branch"] for v in d["values"]), | |
| ["criminality", "serious-criminality"]) | |
| # the value given: the tie is settled, the fact is not asked again | |
| r2 = result_of(self.record(offense="theft", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", sentence_completed="2018-06-01", | |
| value_over_5000=False)) | |
| self.assertNotIn("value_over_5000", self.decisive(r2)) | |
| def test_status_other_record_and_relief_fold_into_assumed(self): | |
| out = self.record(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2016-01-01") | |
| r = result_of(out) | |
| dec = self.decisive(r) | |
| self.assertEqual({v["value"]: v["branch"] for v in dec["status"]["values"]}, | |
| {"foreign-national": "criminality", | |
| "permanent-resident": "not-inadmissible-pr"}) | |
| self.assertEqual(dec["status"]["assumed"], "status-foreign-national") | |
| self.assertEqual({v["value"]: v["rehab_status"] for v in dec["other_record"]["values"]}, | |
| {False: ["deemed-rehabilitated"], True: ["apply-conditional"]}) | |
| self.assertIn("relief", dec) | |
| # the block sits at the top, and the ASSUMED lines it replaces say so | |
| top = out.split("## Step 1")[0] | |
| self.assertIn(self.s.DECISIVE_HEAD, top) | |
| self.assertLess(top.index(self.s.DECISIVE_HEAD), top.index("ASSUMED (not given")) | |
| for code in ("status-foreign-national", "clean-record", "no-foreign-relief"): | |
| self.assertIn(self.s._ASSUMED_TEXT[code] + self.s.DECISIVE_SUFFIX, top) | |
| # 'assumed' is unchanged: the decisive facts are added, nothing removed | |
| self.assertEqual(r["assumed"], ["status-foreign-national", "application-unknown", | |
| "adult-at-offence", "no-foreign-relief", | |
| "clean-record"]) | |
| def test_a_fact_given_is_not_re_run(self): | |
| r = result_of(self.record(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2016-01-01", | |
| status="permanent-resident", other_record=True)) | |
| self.assertNotIn("status", r["decisive_checked"]) | |
| self.assertNotIn("other_record", r["decisive_checked"]) | |
| def test_the_age_is_re_run_and_never_decisive_for_a_foreign_record(self): | |
| r = result_of(self.record(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2016-01-01")) | |
| self.assertIn("age", r["decisive_checked"]) | |
| self.assertNotIn("age", self.decisive(r)) | |
| def test_no_decisive_fact_is_said(self): | |
| out = self.record(offense="trespass", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", sentence_completed="2018-02-01", | |
| status="foreign-national", other_record=True, | |
| date_of_birth="1980-01-01") | |
| r = result_of(out) | |
| if not r["decisive_missing"]: | |
| self.assertIn("THE ANSWER CHANGES WITH THESE FACTS: none found", out) | |
| def test_only_changing_facts_are_kept(self): | |
| for kw in (dict(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01"), | |
| dict(offense="assault", disposition="convicted", state="NY", | |
| offence_date="2016-01-01", sentence_completed="2017-01-01")): | |
| r = result_of(self.record(**kw)) | |
| for d in r["decisive_missing"]: | |
| answers = {(v["branch"], tuple(v["paragraphs"]), tuple(v["rehab_status"])) | |
| for v in d["values"]} | |
| self.assertGreater(len(answers), 1, d) | |
| def test_a_statute_codes_specification_is_re_run_with_its_answer(self): | |
| """The step memo keys a match on the record context too: the former | |
| UCMJ Art. 90's limbs are read from the specification answer, so a | |
| match made without it must never be reused for a reading that has it | |
| (found by the before/after diff, 2026-09-15). The Art. 90 element | |
| split is held for review (85802df): skipped where it is absent, as a | |
| composed release on the approved baseline is.""" | |
| alias = ((self.s._load_equivalency() or {}).get("aliases") or {}).get("article 90") or {} | |
| if not any(isinstance(r, dict) and r.get("elements") | |
| for r in [alias] + list(alias.get("dated") or [])): | |
| self.skipTest("held content absent: the UCMJ Art. 90 element split") | |
| out = self.record(offense="Article 90", disposition="court-martial conviction", | |
| offence_date="2016-01-01", sentence_completed="2017-01-01") | |
| r = result_of(out) | |
| self.assertEqual({e["answer"]: e["pairing"] for e in r["element_readings"]}, | |
| {"assault": "assault-simple", "disobedience": "military-offences"}) | |
| dec = self.decisive(r)["ucmj-specification"] | |
| self.assertEqual({v["value"]: v["branch"] for v in dec["values"]}, | |
| {"ucmj-specification: assault": "tie", | |
| "ucmj-specification: disobedience": "none"}) | |
| def test_a_canadian_record_re_runs_status(self): | |
| r = result_of(self.record(offense="impaired driving", disposition="convicted", | |
| country="Canada", offence_date="2019-06-01", | |
| sentence_completed="2020-01-01", canada_sentence="fine")) | |
| self.assertIn("status", r["decisive_checked"]) | |
| self.assertIn("confidence", r) | |
| def test_history_lists_an_entrys_offence_date_and_the_status(self): | |
| r = result_of(self.history([ | |
| dict(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01"), | |
| dict(offense="theft", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2015-06-01", | |
| value_over_5000=False)])) | |
| dec = self.decisive(r) | |
| self.assertIn("#1 offence_date", dec) | |
| self.assertEqual({v["count"] for v in dec["#1 offence_date"]["values"]}, | |
| {"multiple"}) | |
| self.assertIn("#1 relief", r["decisive_checked"]) | |
| self.assertIn("status", r["decisive_checked"]) | |
| for d in r["decisive_missing"]: | |
| self.assertGreater(len({json.dumps([v["entries"], v["count"], v["rehab_status"]]) | |
| for v in d["values"]}), 1, d) | |
| class ConfidenceTests(_Base): | |
| KEYS = {"step1", "step2", "step3"} | |
| def test_every_screening_tool_carries_it(self): | |
| outs = { | |
| "canlex_us_disposition": self.s.canlex_us_disposition(disposition="deferred adjudication", | |
| state="TX"), | |
| "canlex_us_equivalency": self.s.canlex_us_equivalency(offense="DUI", | |
| offence_date="2015-01-01"), | |
| "canlex_rehabilitation": self.s.canlex_rehabilitation( | |
| basis="foreign-conviction", max_penalty_lt_10=True, count="one", | |
| clean_record=True, sentence_completed="2016-01-01", today=T), | |
| "canlex_screen_record": self.record(offense="DUI", disposition="convicted", | |
| state="TX", offence_date="2015-01-01", | |
| sentence_completed="2016-01-01"), | |
| "canlex_screen_history": self.history([dict(offense="DUI", disposition="convicted", | |
| state="TX", offence_date="2015-01-01", | |
| sentence_completed="2016-01-01")]), | |
| "canlex_transborder": self.s.canlex_transborder(act="Criminal Code", section="95", | |
| offence="loaded handgun"), | |
| } | |
| for tool, out in outs.items(): | |
| r = result_of(out) | |
| self.assertEqual(r["tool"], tool) | |
| self.assertEqual(set(r["confidence"]), self.KEYS, tool) | |
| def test_the_values(self): | |
| c = result_of(self.record(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", | |
| sentence_completed="2016-01-01"))["confidence"] | |
| self.assertEqual(c["step1"][0]["entry"], "conviction") | |
| self.assertIn(c["step1"][0]["entry_status"], | |
| ("settled", "judicially-considered", "guidance-only", "no-authority")) | |
| s2 = c["step2"][0] | |
| self.assertEqual((s2["pairing"], s2["maximum_source"]), ("dui", "history")) | |
| self.assertRegex(s2["authorities_in_corpus"] or "0/0", r"^\d+/\d+$") | |
| self.assertEqual(c["step3"], {"limbs_tested": False, "limbs_unknown": None}) | |
| c2 = result_of(self.record(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2020-06-01", | |
| sentence_completed="2021-06-01"))["confidence"] | |
| self.assertEqual(c2["step2"][0]["maximum_source"], "current") | |
| tie = result_of(self.record(offense="theft", disposition="convicted", state="TX", | |
| offence_date="2018-01-01"))["confidence"] | |
| self.assertEqual(sorted(x["pairing"] for x in tie["step2"]), ["theft-over", "theft-under"]) | |
| h = result_of(self.history([dict(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", | |
| sentence_completed="2016-01-01")]))["confidence"] | |
| self.assertEqual(h["step1"][0]["n"], 1) | |
| self.assertTrue(h["step3"]["limbs_tested"]) | |
| self.assertIsInstance(h["step3"]["limbs_unknown"], int) | |
| def test_a_verified_state_row_says_so(self): | |
| from canlex.screening import step1_confidence | |
| self.assertTrue(step1_confidence({"id": "x"}, {"verified": "2026-09-01"})["row_verified"]) | |
| self.assertFalse(step1_confidence({"id": "x"}, {"unverifiable": "why"})["row_verified"]) | |
| self.assertIsNone(step1_confidence({"id": "x"}, None)["row_verified"]) | |
| class CardTests(_Base): | |
| def body(self, out): | |
| return [l for l in out.splitlines() if not l.startswith("CANLEX_RESULT: ")] | |
| def test_the_card_is_short_and_carries_the_same_result(self): | |
| kw = dict(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2016-01-01") | |
| card = self.record(detail="card", **kw) | |
| full = self.record(**kw) | |
| body = self.body(card) | |
| self.assertLessEqual(len(body), 13) | |
| for head in ("# Result card", "Result:", "Step 1:", "Dates:", "Action:"): | |
| self.assertTrue(any(l.startswith(head) for l in body), head) | |
| self.assertEqual(body[-1], self.s.CARD_FULL) | |
| rc, rf = result_of(card), result_of(full) | |
| self.assertEqual(rc.pop("detail"), "card") | |
| self.assertEqual(rc, rf) | |
| self.assertNotIn("detail", rf) | |
| def test_not_settled_names_the_deciding_fact(self): | |
| card = self.record(detail="card", offense="theft", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", sentence_completed="2018-06-01") | |
| result = next(l for l in self.body(card) if l.startswith("Result:")) | |
| self.assertIn("NOT SETTLED", result) | |
| self.assertIn("CAD 5,000", result) | |
| self.assertIn(self.s.CARD_NOT_SETTLED_ACTION, card) | |
| def test_an_open_result_says_the_assumed_facts_were_not_compared_per_reading(self): | |
| """Wave-4 verifier (2026-09-15): on a tie or an open limb the status and | |
| relief re-runs compare the whole answer, not each reading -- the memo, | |
| the card and CANLEX_RESULT say so rather than implying they cannot | |
| matter.""" | |
| kw = dict(offense="assault causing bodily injury", disposition="convicted", | |
| state="TX", offence_date="2016-01-01", sentence_completed="2016-06-01") | |
| full, card = self.record(**kw), self.record(detail="card", **kw) | |
| r = result_of(full) | |
| self.assertEqual(r["branch"], "tie") | |
| self.assertIn("status", r["decisive_whole_answer_only"]) | |
| self.assertIn("relief", r["decisive_whole_answer_only"]) | |
| self.assertIn("NOTE -- NOT SETTLED at step 2: the re-runs of status", full) | |
| self.assertIn("compared on the whole answer only, not reading by reading", card) | |
| settled = result_of(self.record(offense="robbery", disposition="convicted", state="IL", | |
| offence_date="2001-01-01", | |
| sentence_completed="2004-01-01")) | |
| self.assertNotIn("decisive_whole_answer_only", settled) | |
| def test_the_actions(self): | |
| fn = self.record(detail="card", offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2020-06-01", sentence_completed="2021-06-01", | |
| status="foreign-national") | |
| self.assertIn("s. 44(1) report on IRPA s. 36(1)(b)", fn) | |
| self.assertIn("IRPR s. 42(1)", fn) | |
| # wave-4 verifier: s. 42(1) 'shall allow' (subject to s. 42(2)); s. 44(2) | |
| # 'may refer' -- the card neither softens the one nor hardens the other | |
| self.assertIn("the officer 'shall allow' it -- mandatory unless s. 42(2) applies", fn) | |
| pr = self.record(detail="card", offense="robbery", disposition="convicted", state="IL", | |
| offence_date="2001-01-01", sentence_completed="2004-01-01", | |
| status="permanent-resident") | |
| self.assertIn("the Minister may refer it to the Immigration Division (IRPA s. 44(2))", | |
| pr) | |
| self.assertNotIn("is referred to the Immigration Division", pr) | |
| none = self.record(detail="card", offense="trespass", disposition="convicted", | |
| state="TX", offence_date="2018-01-01", | |
| sentence_completed="2018-02-01") | |
| self.assertIn(self.s.CARD_NO_GROUND_ACTION, none) | |
| deemed = self.record(detail="card", offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2015-01-01", sentence_completed="2016-01-01") | |
| self.assertIn(self.s.CARD_DEEMED_ACTION, deemed) | |
| def test_history_card(self): | |
| ents = [dict(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2016-03-01", sentence_completed="2017-03-01"), | |
| dict(offense="disorderly conduct", disposition="pled guilty", state="TX", | |
| offence_date="2019-05-05", sentence_completed="2019-06-01")] | |
| card = self.history(ents, detail="card") | |
| body = self.body(card) | |
| self.assertLessEqual(len(body), 12) | |
| self.assertTrue(body[0].startswith("# Result card -- a record of 2 entries")) | |
| self.assertTrue(any(l.startswith("Entries: #1") for l in body)) | |
| rc, rf = result_of(card), result_of(self.history(ents)) | |
| rc.pop("detail") | |
| self.assertEqual(rc, rf) | |
| def test_a_bad_detail_is_refused(self): | |
| self.assertTrue(self.record(offense="DUI", disposition="convicted", | |
| detail="short").startswith("Error: detail")) | |
| class SentenceTests(unittest.TestCase): | |
| def test_the_common_forms(self): | |
| from canlex.screening import parse_sentence | |
| got = {t: [(p["kind"], p["n"], p["unit"], p["suspended"]) | |
| for p in parse_sentence(t)["parts"]] | |
| for t in ("3 YRS PROB", "30 DAYS JAIL SUSP", "$500 FINE", "12 MOS DL SUSP")} | |
| self.assertEqual(got, {"3 YRS PROB": [("probation", 3.0, "years", False)], | |
| "30 DAYS JAIL SUSP": [("imprisonment", 30.0, "days", True)], | |
| "$500 FINE": [("fine", None, None, False)], | |
| "12 MOS DL SUSP": [("driving", 12.0, "months", False)]}) | |
| self.assertEqual(parse_sentence("$500 FINE")["parts"][0]["amount"], 500.0) | |
| self.assertEqual(parse_sentence("40 HRS COMMUNITY SERVICE")["unread"], | |
| ["40 HRS COMMUNITY SERVICE"]) | |
| def test_a_thousands_comma_in_a_fine_is_one_part(self): | |
| """The code audit of 2026-09-21 (HELD): '$1,000 fine' split at the comma into | |
| a $1 fine and an unread '500 fine'. Before/after on '2 yrs prob, $1,500 fine': | |
| blocked ('cannot read: 500 fine') -> blocked (the fine rule, amount $1,500). | |
| The readings the audit proposed for '6 mos jail, 3 mos susp', '1 yr 6 mos | |
| jail' and '5 years tdcj' are NOT added here: each is a new reading of a | |
| record's words for the maintainer to approve one by one, so they stay | |
| unread (pinned as such).""" | |
| from canlex.screening import parse_sentence, projected_completion | |
| p = parse_sentence("$1,000 fine") | |
| self.assertEqual(([x["kind"] for x in p["parts"]], p["parts"][0]["amount"], p["unread"]), | |
| (["fine"], 1000.0, [])) | |
| p = parse_sentence("2 yrs prob, $1,500 fine") | |
| self.assertEqual([(x["kind"], x["amount"]) for x in p["parts"]], | |
| [("probation", None), ("fine", 1500.0)]) | |
| self.assertEqual(p["unread"], []) | |
| self.assertEqual(parse_sentence("$1,000,000 fine")["parts"][0]["amount"], 1000000.0) | |
| # a comma with no space still splits ('2 yrs prob,180 days jail' is two parts) | |
| self.assertEqual([x["kind"] for x in parse_sentence("2 yrs prob,180 days jail")["parts"]], | |
| ["probation", "imprisonment"]) | |
| blocked = projected_completion("2 yrs prob, $1,500 fine", dt.date(2020, 1, 1))["blocked"] | |
| self.assertIn("$1,500 fine", blocked) | |
| self.assertIn("Guide 5312", blocked) | |
| self.assertNotIn("cannot read", blocked) | |
| # the optional readings stay unread for now | |
| for s, unread in (("6 mos jail, 3 mos susp", ["3 mos susp"]), | |
| ("1 yr 6 mos jail", ["1 yr 6 mos jail"]), | |
| ("5 years tdcj", ["5 years tdcj"])): | |
| self.assertEqual(parse_sentence(s)["unread"], unread, s) | |
| def test_the_projection(self): | |
| from canlex.screening import projected_completion | |
| d = dt.date(2019, 3, 1) | |
| p = projected_completion("3 YRS PROB, 30 DAYS JAIL SUSP", d) | |
| self.assertEqual((p["date"], p["rule"], p["blocked"]), (dt.date(2022, 3, 1), "probation", None)) | |
| self.assertEqual(projected_completion("30 DAYS JAIL SUSP", d)["date"], d) | |
| self.assertEqual(projected_completion("180 DAYS JAIL, 170 SUSP", d)["date"], | |
| dt.date(2019, 3, 11)) | |
| self.assertEqual(projected_completion("3 MONTHS JAIL", dt.date(2002, 12, 13))["date"], | |
| dt.date(2003, 3, 13)) # Guide 5312 example 1 | |
| dl = projected_completion("3 MOS JAIL; 12 MOS DL SUSP", d) | |
| self.assertEqual((dl["date"], dl["if_administrative"]), | |
| (dt.date(2020, 3, 1), dt.date(2019, 6, 1))) | |
| self.assertIn("fine", projected_completion("2 years probation, $500 fine", d)["blocked"]) | |
| self.assertIn("cannot read", projected_completion("40 HRS COMMUNITY SERVICE", d)["blocked"]) | |
| def test_months_end_on_the_last_day(self): | |
| from canlex.screening import add_months | |
| self.assertEqual(add_months(dt.date(2019, 1, 31), 1), dt.date(2019, 2, 28)) | |
| self.assertEqual(add_months(dt.date(2019, 11, 30), 3), dt.date(2020, 2, 29)) | |
| def test_guide_5312_rules_are_verbatim(self): | |
| from canlex.screening import GUIDE_5312_CHUNK, GUIDE_5312_COURT, GUIDE_5312_RULES | |
| chunks = _chunks("policy.json") | |
| if chunks is None: | |
| self.skipTest("corpus not ingested") | |
| text = next(c["text"] for c in chunks if c["id"] == GUIDE_5312_CHUNK) | |
| for rule in list(GUIDE_5312_RULES.values()) + [GUIDE_5312_COURT]: | |
| self.assertIn(rule, text) | |
| def test_the_statute_phrases_the_card_and_questions_rely_on(self): | |
| irpr, irpa = _chunks("SOR-2002-227.json"), _chunks("I-2.5.json") | |
| if irpr is None or irpa is None: | |
| self.skipTest("corpus not ingested") | |
| s42 = next(c["text"] for c in irpr if c["id"] == "SOR-2002-227-s42") | |
| self.assertIn("shall allow the foreign national to withdraw their application and " | |
| "leave Canada", s42) | |
| self.assertIn("If a report is being prepared or has been prepared under subsection " | |
| "44(1) of the Act", s42) | |
| s44 = next(c["text"] for c in irpa if c["id"] == "I-2.5-s44") | |
| self.assertIn("the Minister may refer the report to the Immigration Division", s44) | |
| irpa_text = "\n".join(c["text"] for c in irpa) | |
| self.assertIn("A foreign national is inadmissible on grounds of criminality for", | |
| irpa_text) | |
| class ProjectionMemoTests(_Base): | |
| KW = dict(offense="DUI", disposition="convicted", state="TX", offence_date="2019-06-01") | |
| def test_no_verdict_on_the_footing(self): | |
| out = self.record(sentence_imposed="3 YRS PROB, 30 DAYS JAIL SUSP", | |
| sentence_date="2020-01-10", **self.KW) | |
| r = result_of(out) | |
| self.assertEqual(r["completion_basis"], "if-served-as-imposed") | |
| self.assertIsNone(r.get("rehab_status")) | |
| p = r["if_served_as_imposed"] | |
| self.assertEqual((p["completion"], p["earliest_apply"], p["source"]), | |
| ("2023-01-10", "2028-01-10", "policy-imm-5312-12")) | |
| from canlex.screening import IF_SERVED_LABEL | |
| self.assertIn(IF_SERVED_LABEL, out) | |
| self.assertIn("No verdict is given on this footing", out) | |
| def test_a_fine_blocks_it(self): | |
| r = result_of(self.record(sentence_imposed="2 years probation, $500 fine", | |
| sentence_date="2020-01-10", **self.KW)) | |
| self.assertEqual(r["completion_basis"], "not-projected") | |
| self.assertIn("fine", r["if_served_as_imposed"]["blocked"]) | |
| def test_a_given_completion_wins(self): | |
| r = result_of(self.record(sentence_imposed="3 YRS PROB", sentence_date="2020-01-10", | |
| sentence_completed="2023-01-10", **self.KW)) | |
| self.assertEqual(r["completion_basis"], "sentence_completed") | |
| self.assertNotIn("if_served_as_imposed", r) | |
| def test_the_margin(self): | |
| r = result_of(self.record(sentence_imposed="2 YRS PROB", sentence_date="2019-12-01", | |
| offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2019-06-01")) | |
| # completion 2021-12-01; application from 2026-12-01, within 12 months of 2026-09-15 | |
| self.assertTrue(r["if_served_as_imposed"]["margin"]) | |
| class ConvictionDateTests(_Base): | |
| def test_it_bounds_the_tran_maximum(self): | |
| base = dict(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01") | |
| self.assertEqual(result_of(self.record(**base))["branch"], "tran-open") | |
| out = self.record(conviction_date="2018-06-01", **base) | |
| r = result_of(out) | |
| self.assertEqual((r["branch"], r["max_years"], r["assessed_basis"]), | |
| ("criminality", 5, "inferred-from-conviction")) | |
| self.assertIn("inferred from the conviction date", out) | |
| # a conviction after the amendment settles nothing | |
| self.assertEqual(result_of(self.record(conviction_date="2019-01-10", **base))["branch"], | |
| "tran-open") | |
| def test_out_of_order_dates_are_said(self): | |
| out = self.record(offense="DUI", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", conviction_date="2017-06-01", | |
| sentence_date="2017-01-01") | |
| self.assertIn("WARNING: the conviction date 2017-06-01 precedes the offence date", out) | |
| self.assertIn("WARNING: the sentence date 2017-01-01 precedes", out) | |
| def test_history_reads_it_too(self): | |
| out = self.history([dict(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01", | |
| conviction_date="2018-06-01")]) | |
| self.assertIn("maximum inferred from the conviction date (2018-06-01)", out) | |
| def _git_json(commit, path): | |
| try: | |
| out = subprocess.run(["git", "-C", str(ROOT), "show", f"{commit}:{path}"], | |
| capture_output=True, timeout=60) | |
| except (OSError, subprocess.SubprocessError): | |
| return None | |
| return json.loads(out.stdout.decode("utf-8")) if out.returncode == 0 else None | |
| class ApprovedBaselineTests(_Base): | |
| """main's code on the approved curated JSON (what a composed release | |
| ships): every new block runs, and says nothing it cannot.""" | |
| def setUpClass(cls): | |
| super().setUpClass() | |
| try: | |
| state = json.loads((ROOT / "data" / "curated" / "review_state.json") | |
| .read_text(encoding="utf-8")) | |
| commit = state["approved"]["commit"] | |
| except (OSError, ValueError, KeyError): | |
| raise unittest.SkipTest("no review state") | |
| cls.eq = _git_json(commit, "data/curated/us_equivalency.json") | |
| cls.disp = _git_json(commit, "data/curated/us_dispositions.json") | |
| if cls.eq is None or cls.disp is None: | |
| raise unittest.SkipTest("approved baseline not readable with git here") | |
| def tearDownClass(cls): | |
| cls.s._load_equivalency() # the working tree's alias table again | |
| super().tearDownClass() | |
| def test_the_new_blocks_run_on_the_approved_data(self): | |
| from canlex.screening import load_aliases | |
| def eq(): | |
| load_aliases(self.eq.get("aliases")) | |
| return self.eq | |
| with mock.patch.object(self.s, "_load_equivalency", eq), \ | |
| mock.patch.object(self.s, "_load_dispositions", lambda: self.disp): | |
| for detail in ("full", "card"): | |
| for kw in (dict(offense="DUI", disposition="convicted", state="TX", | |
| sentence_completed="2019-06-01", conviction_date="2018-06-01"), | |
| dict(offense="theft", disposition="convicted", state="TX", | |
| offence_date="2018-01-01", sentence_imposed="3 YRS PROB", | |
| sentence_date="2018-03-01"), | |
| dict(offense="assault", disposition="deferred adjudication", | |
| state="TX", offence_date="2014-01-01"), | |
| dict(offense="Article 90", disposition="court-martial conviction"), | |
| dict(offense="impaired driving", disposition="convicted", | |
| country="Canada", offence_date="2019-06-01")): | |
| r = result_of(self.record(detail=detail, **kw)) | |
| self.assertIn("confidence", r, kw) | |
| self.assertIn("decisive_missing", r, kw) | |
| r = result_of(self.history([dict(offense="DUI", disposition="convicted", | |
| state="TX", sentence_completed="2019-06-01"), | |
| dict(offense="theft", disposition="convicted", | |
| state="TX", offence_date="2016-01-01", | |
| sentence_completed="2016-02-01")], | |
| detail=detail)) | |
| self.assertIn("confidence", r) | |
| if __name__ == "__main__": | |
| unittest.main() | |