..............................F...F..................................... [ 3%] ........................................................................ [ 6%] .........FF..............................................F.............. [ 10%] .................F...................................................... [ 13%] .........................................FFFFFFFF...............F....... [ 17%] ........................................................................ [ 20%] ........................................................................ [ 24%] ........................................................................ [ 27%] ........................................................................ [ 30%] ........................................................................ [ 34%] ..................FFF.FF.FFFF.FFF.F.FFF...FF.FFFF.F.FFF.. [ 37%] F.F..FFF.FFFF....F...FFF.F..FFFF.FFF.F.FFF.F.FF.FFFF.F.FFF..F.F..FFF.FFF [ 40%] F....F..F....F.......................................................... [ 43%] ......................................F................................. [ 47%] .................................FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [ 50%] F..FF.........................FFF....................................... [ 54%] ........................................................................ [ 57%] .................sss..s................................................. [ 61%] ........................................................................ [ 64%] ........................................................................ [ 67%] ......................................................F................. [ 71%] ......................FF............................................. [ 74%] ........................................................................ [ 78%] ...............ss....................................................... [ 81%] .......FFF.............................................................. [ 85%] ...................................sssssssssssssssssssssssss............ [ 88%] .........................................FFFF.FF....FF...F....F......... [ 91%] ........................................................................ [ 95%] .................................sss.................................... [ 98%] ....................s..F.. [100%] =================================== FAILURES =================================== _____________ test_edges_cite_journal_records_across_chosen_chats ______________ store = (, {'pay': {'seq': 1, 'observed_at': '2026-11-14T07:28:44.989+00:00', ...ward': {'seq': 4, 'observed_at': '2026-11-14T07:28:45.014+00:00', 'source_at': None, 'kind': 'message.new', ...}, ...}) def test_edges_cite_journal_records_across_chosen_chats(store): value, records = store model = graphs.collect(extensions.JournalReader(lambda: value), {CHAT, OTHER}) reply = edge(model, "account:22", "account:11", "reply") assert reply["weight"] == 1 and reply["refs"] == [{"chat": CHAT, "message": 2, "seq": records["reply"]["seq"], "hash": records["reply"]["hash"]}] assert edge(model, "account:11", "account:22", "reply")["refs"][0]["seq"] == records["back"]["seq"] assert edge(model, "account:33", "source:-1000000000555", "forward")["refs"][0]["message"] == 4 assert edge(model, "account:22", "mention:@helper_bot", "mention")["weight"] == 1 wallet = "indicator:wallet:" + WALLET assert {e["source"] for e in model["edges"] if e["target"] == wallet} == {"account:11", "account:33"} assert edge(model, "account:33", wallet, "posted")["refs"][0]["chat"] == OTHER assert model["totals"]["indicators_shared_by_several"] == 1 and model["format"] == graphs.FORMAT nodes = {n["id"]: n for n in model["nodes"]} assert nodes["account:11"]["label"] == "Alice &" and nodes["account:11"]["messages"] == 2 assert nodes["account:11"]["community"] == nodes["account:22"]["community"] assert all(len(e["refs"]) <= graphs.MAX_REFS for e in model["edges"]) and not model["truncated"] > assert any("does not mean there was no contact" in line for line in model["limits"]) E assert False E + where False = any(. at 0x70b6f88d5e50>) tools/test_analyzer_graphs.py:71: AssertionError _______ test_rotated_account_ranks_first_with_explanation_and_citations ________ reader = def test_rotated_account_ranks_first_with_explanation_and_citations(reader): result = linking.candidates(reader, {CHAT}, offset_minutes=180) assert result["format"] == linking.FORMAT and result["accounts"] == 5 and result["compared"] == 10 assert result["basis"] == "absolute" top = result["pairs"][0] assert top["key"] == "101|202" and top["tier"] == "strong" assert all(top["score"] > pair["score"] for pair in result["pairs"][1:]) assert {"lexical", "hours", "handoff", "shared"} <= set(top["standout"]) assert top["handoff"]["gone"] == 101 and top["handoff"]["arrived"] == 202 assert any(line.startswith("Same vocabulary") for line in top["explanation"]) assert any(line.startswith("One stops, other starts: Payout Desk (ID 101)") for line in top["explanation"]) [shared] = top["shared_indicators"] assert shared["kind"] == "wallet" and shared["value"] == WALLET assert all(len(ref["hash"]) == 64 and ref["chat"] == CHAT for ref in shared["refs"] + top["a"]["refs"]) assert top["a"]["first_seen"].endswith("+03:00") > assert any("not identification" in line for line in result["limits"]) E assert False E + where False = any(. at 0x70b708b5a810>) tools/test_analyzer_linking.py:81: AssertionError ______ test_package_contains_readable_report_files_and_explanations[tsr] _______ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_package_contains_readable0') receipt_kind = 'tsr' @pytest.mark.parametrize("receipt_kind", ["tsr", "ots"]) def test_package_contains_readable_report_files_and_explanations(tmp_path, receipt_kind): vault, store = Vault(tmp_path / "workspace"), Store(tmp_path / "workspace") store.bind(vault) media = tmp_path / "workspace" / "media" media.mkdir(exist_ok=True) for data in (PHOTO, SECRET): (media / sha(data)).write_bytes(data) try: session = documentation.create(store, Start(title="Fictional case"), "admin") store.append("interaction.requested", {"request": {"action": "send", "chat_id": BOT, "text": "hi"}}, BOT) store.append("message.new", {**incoming("hi"), "outgoing": True, "source": "panel_action"}, BOT, 1) store.append("message.new", incoming("Pay to bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"), BOT, 2) store.append("media.saved", {"media_sha256": sha(PHOTO), "mime_type": "image/jpeg"}, BOT, 2) hidden = store.append("message.new", incoming("explicit"), BOT, 3) store.append("media.saved", {"media_sha256": sha(SECRET), "mime_type": "image/jpeg"}, BOT, 3) store.append("capture.opened", {"requested_url": "https://pay.example.invalid/", "http_status": 200, "artifacts": [{"sha256": sha(SCREEN), "size": len(SCREEN), "role": "viewport", "media_type": "image/png"}], "errors": []}) documentation.add_mark(store, session["id"], Mark(text="Explicit", labels=["adult"], target_seq=hidden["seq"])) with pytest.raises(ValueError, match="Fix the document"): case_package.build(documentation.load(store, session["id"]), documentation.action_protocol.plain_say, {}, media) fixed = documentation.fix(store, vault, session["id"])["fixed"] checkpoints = tmp_path / "workspace" / "checkpoints" (checkpoints / "anchors").mkdir(parents=True, exist_ok=True) name = "checkpoint-" + str(fixed["event_seq"] + 1).zfill(12) receipt_name = name + ".fictional-tsa." + receipt_kind (checkpoints / (name + ".json")).write_text('{"statement": {}}') (checkpoints / "anchors" / receipt_name).write_bytes(b"synthetic-reply") loaded = documentation.load(store, session["id"]) path = case_package.build(loaded, documentation.action_protocol.plain_say, {sha(SCREEN): SCREEN}, media, tmp_path, checkpoints) root = "tgwitness-case-" + session["id"][:8] + "/" with zipfile.ZipFile(path) as archive: names = set(archive.namelist()) slides = archive.read(root + "data/presentation.html").decode() assert slides.count('
assert "Timestamp receipts (not verified)" in certificate and "have not been verified" in certificate E assert ('Timestamp receipts (not verified)' in '

Date:

Place:

') tools/test_case_package.py:82: AssertionError ______ test_package_contains_readable_report_files_and_explanations[ots] _______ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_package_contains_readable1') receipt_kind = 'ots' @pytest.mark.parametrize("receipt_kind", ["tsr", "ots"]) def test_package_contains_readable_report_files_and_explanations(tmp_path, receipt_kind): vault, store = Vault(tmp_path / "workspace"), Store(tmp_path / "workspace") store.bind(vault) media = tmp_path / "workspace" / "media" media.mkdir(exist_ok=True) for data in (PHOTO, SECRET): (media / sha(data)).write_bytes(data) try: session = documentation.create(store, Start(title="Fictional case"), "admin") store.append("interaction.requested", {"request": {"action": "send", "chat_id": BOT, "text": "hi"}}, BOT) store.append("message.new", {**incoming("hi"), "outgoing": True, "source": "panel_action"}, BOT, 1) store.append("message.new", incoming("Pay to bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"), BOT, 2) store.append("media.saved", {"media_sha256": sha(PHOTO), "mime_type": "image/jpeg"}, BOT, 2) hidden = store.append("message.new", incoming("explicit"), BOT, 3) store.append("media.saved", {"media_sha256": sha(SECRET), "mime_type": "image/jpeg"}, BOT, 3) store.append("capture.opened", {"requested_url": "https://pay.example.invalid/", "http_status": 200, "artifacts": [{"sha256": sha(SCREEN), "size": len(SCREEN), "role": "viewport", "media_type": "image/png"}], "errors": []}) documentation.add_mark(store, session["id"], Mark(text="Explicit", labels=["adult"], target_seq=hidden["seq"])) with pytest.raises(ValueError, match="Fix the document"): case_package.build(documentation.load(store, session["id"]), documentation.action_protocol.plain_say, {}, media) fixed = documentation.fix(store, vault, session["id"])["fixed"] checkpoints = tmp_path / "workspace" / "checkpoints" (checkpoints / "anchors").mkdir(parents=True, exist_ok=True) name = "checkpoint-" + str(fixed["event_seq"] + 1).zfill(12) receipt_name = name + ".fictional-tsa." + receipt_kind (checkpoints / (name + ".json")).write_text('{"statement": {}}') (checkpoints / "anchors" / receipt_name).write_bytes(b"synthetic-reply") loaded = documentation.load(store, session["id"]) path = case_package.build(loaded, documentation.action_protocol.plain_say, {sha(SCREEN): SCREEN}, media, tmp_path, checkpoints) root = "tgwitness-case-" + session["id"][:8] + "/" with zipfile.ZipFile(path) as archive: names = set(archive.namelist()) slides = archive.read(root + "data/presentation.html").decode() assert slides.count('
assert "Timestamp receipts (not verified)" in certificate and "have not been verified" in certificate E assert ('Timestamp receipts (not verified)' in '

Date:

Place:

') tools/test_case_package.py:82: AssertionError __________ test_sparse_message_numbers_are_counted_without_expansion ___________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_sparse_message_numbers_ar0') def test_sparse_message_numbers_are_counted_without_expansion(tmp_path): scratch = tmp_path / "sparse.jsonl" scratch.write_text('{"id":1}\n{"id":2000000000}\n', encoding="utf-8") missing = absent_numbers(scratch) > assert len(missing) == 1999999998 E assert 2 == 1999999998 E + where 2 = len((, 1)) tools/test_chat_export_integrity.py:100: AssertionError _____________________________ test_local_commands ______________________________ workspace = PosixPath('/tmp/pytest-of-root/pytest-8/test_local_commands0') capsys = <_pytest.capture.CaptureFixture object at 0x70b6e54a9250> def test_local_commands(workspace, capsys): base = ("--data-dir", str(workspace)) found = document(capsys, "search", "перевод", *base) assert found["ranked"] and {item["source"]["message_id"] for item in found["items"]} == {1, 2} assert all(item["source"]["chat_id"] == CHAT and item["untrusted_content"] for item in found["items"]) exact = document(capsys, "search", "Оплата", "--exact", *base) assert not exact.get("ranked") and {item["kind"] for item in exact["items"]} == {"message.new", "message.edited"} near = document(capsys, "context", str(CHAT), "2", *base) assert {item["source"]["message_id"] for item in near["items"]} == {1, 2, 3, 4} chats = document(capsys, "messages", *base) assert {item["chat_id"] for item in chats["items"]} == {CHAT, OTHER} names = document(capsys, "messages", "--workspaces", *base) > assert names["items"] == [{"workspace": "default", "name": "Main account"}] E AssertionError: assert [{'workspace'...n workspace'}] == [{'workspace'...ain account'}] E E At index 0 diff: {'workspace': 'default', 'name': 'Main workspace'} != {'workspace': 'default', 'name': 'Main account'} E Use -v to get more diff tools/test_cli.py:147: AssertionError ____ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[2-55123] ____ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_0') chat_id = 55123, count = 2 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError _ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[2--1000000000555] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_1') chat_id = -1000000000555, count = 2 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError ____ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[9-55123] ____ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_2') chat_id = 55123, count = 9 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError _ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[9--1000000000555] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_3') chat_id = -1000000000555, count = 9 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError ___ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[10-55123] ____ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_4') chat_id = 55123, count = 10 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError _ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[10--1000000000555] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_5') chat_id = -1000000000555, count = 10 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError ___ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[15-55123] ____ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_6') chat_id = 55123, count = 15 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError _ test_deletion_of_subset_keeps_actor_and_whole_history_unknown[15--1000000000555] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_deletion_of_subset_keeps_7') chat_id = -1000000000555, count = 15 @pytest.mark.parametrize("chat_id", [55123, -1000000000555]) @pytest.mark.parametrize("count", [2, 9, 10, 15]) def test_deletion_of_subset_keeps_actor_and_whole_history_unknown(tmp_path, chat_id, count): async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] assert "does not identify who deleted them" in summary["payload"]["meaning"] deleted = [e for e in events if e["kind"] == "message.deleted"] assert len(deleted) == count assert all(e["payload"]["deleted_by"] is None for e in deleted) assert all(e["payload"]["deletion_time"] is None for e in deleted) assert all(e["payload"]["batch"]["hash"] == summary["hash"] for e in deleted) store.verify(vault.public_key) finally: store.close() > asyncio.run(scenario()) tools/test_deletion_semantics.py:52: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def scenario(): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) store.set_target(chat_id, "Synthetic evidence review", "private" if chat_id > 0 else "supergroup", True) collector = Collector(store, vault) collector.running = True ids = list(range(1, count + 1)) try: for mid in [*ids, 100]: store.append("message.new", {"text": f"Synthetic message {mid}", "outgoing": mid == 1, "sent_at": "2026-11-05T12:00:00+00:00", "edited_at": None}, chat_id, mid) raw = (types.UpdateDeleteMessages(messages=ids, pts=1, pts_count=count) if chat_id > 0 else types.UpdateDeleteChannelMessages(channel_id=555, messages=ids, pts=1, pts_count=count)) await collector.on_delete(SimpleNamespace(chat_id=None if chat_id > 0 else chat_id, deleted_ids=ids, original_update=raw)) assert store.message_state(chat_id, 100)["deleted"] is False events = list(store.all_events()) summaries = [e for e in events if e["kind"] in ("deletion.batch", "history.cleared")] assert len(summaries) == 1 summary = summaries[0] assert summary["kind"] == "deletion.batch", "A subset deletion is not a whole-history clear" assert summary["payload"]["outgoing_saved"] == 1 assert summary["payload"]["raw"] == raw.to_dict() assert "other side cleared" not in summary["payload"]["meaning"] > assert "does not identify who deleted them" in summary["payload"]["meaning"] E AssertionError: assert 'does not identify who deleted them' in 'Telegram reported several message IDs as deleted in one update.' tools/test_deletion_semantics.py:43: AssertionError _______ test_the_dossier_counts_clients_sticker_sets_and_prepared_media ________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_the_dossier_counts_client0') def test_the_dossier_counts_clients_sticker_sets_and_prepared_media(tmp_path): vault, store = Vault(tmp_path), Store(tmp_path) store.bind(vault) try: store.set_target(CHAT, "Fictional payouts", "group", True) for number, raw in enumerate([video("round_1.mp4", (384, 384)), video(None, (384, 384)), video(None, (384, 384), sent="2026-11-02T12:00:00+00:00", uploaded="2026-11-02T06:00:00+00:00")], 1): store.append("message.new", {"text": "", "sender_id": PERSON, "sent_at": raw["date"], "sender_observation": {"id": PERSON, "display_name": "Admin"}, "raw": raw}, CHAT, number) built = dossier.build(store, CHAT) person = next(p for p in built["people"] if p["id"] == PERSON) assert {item["value"] for item in person["clients"]["observed"]} == {"Telegram Desktop", "Telegram for Android"} > assert "Metadata hypothesis about the client that encoded the file" in person["clients"]["meaning"] E assert 'Metadata hypothesis about the client that encoded the file' in "Client that encoded the file, read from the file's own metadata. A forwarded or re-uploaded file carries the encoder of whoever made it first." tools/test_device_attribution.py:104: AssertionError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-AT] _ profile = {'code': 'AT', 'name': 'Austria', 'language': 'en', 'scope': 'Austria. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-BD] _ profile = {'code': 'BD', 'name': 'Bangladesh', 'language': 'en', 'scope': 'Bangladesh. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-BE] _ profile = {'code': 'BE', 'name': 'Belgium', 'language': 'en', 'scope': 'Belgium. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-BG] _ profile = {'code': 'BG', 'name': 'Bulgaria', 'language': 'en', 'scope': 'Bulgaria. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CA] _ profile = {'code': 'CA', 'name': 'Canada', 'scope': 'Federal scope; provincial rules need separate assessment', 'language': 'en', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'CA', 'procedure': 'civil', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, Choose a procedure_scope for CA: civil-ontario, civil-federal [type=value_error, input_value={'jurisdiction': 'CA', 'p...il', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-HR] _ profile = {'code': 'HR', 'name': 'Croatia', 'language': 'en', 'scope': 'Croatia. Expert examination guidance. Civil expert-evide...dated publication. Later amendments were not fully traced; no criminal route or oath formula has been validated.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CY] _ profile = {'code': 'CY', 'name': 'Cyprus', 'language': 'en', 'scope': 'Cyprus. Expert examination guidance. Republic of Cyprus c...023 Rules, Part 34. Transitional rules, later amendments and criminal proceedings require separate verification.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CZ] _ profile = {'code': 'CZ', 'name': 'Czechia', 'language': 'en', 'scope': 'Czechia. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-DK] _ profile = {'code': 'DK', 'name': 'Denmark', 'language': 'en', 'scope': 'Denmark. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-EE] _ profile = {'code': 'EE', 'name': 'Estonia', 'language': 'en', 'scope': 'Estonia. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-ET] _ profile = {'code': 'ET', 'name': 'Ethiopia', 'language': 'en', 'scope': 'Ethiopia. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'ET', 'procedure': 'civil', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No civil examination route is available for ET [type=value_error, input_value={'jurisdiction': 'ET', 'p...il', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-FI] _ profile = {'code': 'FI', 'name': 'Finland', 'language': 'en', 'scope': 'Finland. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-GE] _ profile = {'code': 'GE', 'name': 'Georgia', 'language': 'en', 'scope': 'Georgia. Expert examination guidance. Selected civil exp...al Georgian trust-services text were consulted; this is not an audit of all amendments or procedural exceptions.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-GR] _ profile = {'code': 'GR', 'name': 'Greece', 'language': 'en', 'scope': 'Greece. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'GR', 'procedure': 'civil', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No civil examination route is available for GR [type=value_error, input_value={'jurisdiction': 'GR', 'p...il', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-HK] _ profile = {'code': 'HK', 'name': 'Hong Kong SAR', 'language': 'en', 'scope': 'Hong Kong SAR. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-HU] _ profile = {'code': 'HU', 'name': 'Hungary', 'language': 'en', 'scope': 'Hungary. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-IE] _ profile = {'code': 'IE', 'name': 'Ireland', 'language': 'en', 'scope': 'Ireland. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-IL] _ profile = {'code': 'IL', 'name': 'Israel', 'language': 'en', 'scope': 'Israel. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-JP] _ profile = {'code': 'JP', 'name': 'Japan', 'language': 'en', 'scope': 'Japan. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-LV] _ profile = {'code': 'LV', 'name': 'Latvia', 'language': 'en', 'scope': 'Latvia. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-LT] _ profile = {'code': 'LT', 'name': 'Lithuania', 'language': 'en', 'scope': 'Lithuania. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-LU] _ profile = {'code': 'LU', 'name': 'Luxembourg', 'language': 'en', 'scope': 'Luxembourg. Expert examination guidance. Civil subset...s, with judicial-language legislation. The complete current code and criminal expert rules were not revalidated.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-MT] _ profile = {'code': 'MT', 'name': 'Malta', 'language': 'en', 'scope': 'Malta. Expert examination guidance. Civil: selected Cap. 1...ractice corroborated by the e-Justice factsheet updated 22 June 2026. This is not a complete current-code audit.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-MD] _ profile = {'code': 'MD', 'name': 'Moldova', 'language': 'en', 'scope': 'Moldova. Expert examination guidance. Common judicial-ex...s across proceedings; route-specific appointment powers and admissibility rules are not represented as verified.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-NL] _ profile = {'code': 'NL', 'name': 'Netherlands', 'language': 'en', 'scope': 'Netherlands. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'NL', 'procedure': 'civil', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No civil examination route is available for NL [type=value_error, input_value={'jurisdiction': 'NL', 'p...il', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-NO] _ profile = {'code': 'NO', 'name': 'Norway', 'language': 'en', 'scope': 'Norway. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-PL] _ profile = {'code': 'PL', 'name': 'Poland', 'language': 'en', 'scope': 'Poland. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-RO] _ profile = {'code': 'RO', 'name': 'Romania', 'language': 'en', 'scope': 'Romania. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-SK] _ profile = {'code': 'SK', 'name': 'Slovakia', 'language': 'en', 'scope': 'Slovakia. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-SI] _ profile = {'code': 'SI', 'name': 'Slovenia', 'language': 'en', 'scope': 'Slovenia. Expert examination guidance. Civil expert evi...working consolidation. Criminal procedure and current professional reporting guidelines are outside this subset.', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-KR] _ profile = {'code': 'KR', 'name': 'South Korea', 'language': 'en', 'scope': 'South Korea. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-SE] _ profile = {'code': 'SE', 'name': 'Sweden', 'language': 'en', 'scope': 'Sweden. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CH] _ profile = {'code': 'CH', 'name': 'Switzerland (federal law)', 'language': 'en', 'scope': 'Switzerland (federal law). Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-TW] _ profile = {'code': 'TW', 'name': 'Taiwan', 'language': 'en', 'scope': 'Taiwan. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-TH] _ profile = {'code': 'TH', 'name': 'Thailand', 'language': 'en', 'scope': 'Thailand. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'TH', 'procedure': 'civil', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No civil examination route is available for TH [type=value_error, input_value={'jurisdiction': 'TH', 'p...il', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[civil-UZ] _ profile = {'code': 'UZ', 'name': 'Uzbekistan', 'language': 'en', 'scope': 'Uzbekistan. Expert examination guidance. ', ...} procedure = 'civil' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-AT] _ profile = {'code': 'AT', 'name': 'Austria', 'language': 'en', 'scope': 'Austria. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-BD] _ profile = {'code': 'BD', 'name': 'Bangladesh', 'language': 'en', 'scope': 'Bangladesh. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-BE] _ profile = {'code': 'BE', 'name': 'Belgium', 'language': 'en', 'scope': 'Belgium. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-BG] _ profile = {'code': 'BG', 'name': 'Bulgaria', 'language': 'en', 'scope': 'Bulgaria. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-HR] _ profile = {'code': 'HR', 'name': 'Croatia', 'language': 'en', 'scope': 'Croatia. Expert examination guidance. Civil expert-evide...dated publication. Later amendments were not fully traced; no criminal route or oath formula has been validated.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'HR', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No criminal examination route is available for HR [type=value_error, input_value={'jurisdiction': 'HR', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-CY] _ profile = {'code': 'CY', 'name': 'Cyprus', 'language': 'en', 'scope': 'Cyprus. Expert examination guidance. Republic of Cyprus c...023 Rules, Part 34. Transitional rules, later amendments and criminal proceedings require separate verification.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'CY', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No criminal examination route is available for CY [type=value_error, input_value={'jurisdiction': 'CY', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-CZ] _ profile = {'code': 'CZ', 'name': 'Czechia', 'language': 'en', 'scope': 'Czechia. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-DK] _ profile = {'code': 'DK', 'name': 'Denmark', 'language': 'en', 'scope': 'Denmark. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-EE] _ profile = {'code': 'EE', 'name': 'Estonia', 'language': 'en', 'scope': 'Estonia. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-ET] _ profile = {'code': 'ET', 'name': 'Ethiopia', 'language': 'en', 'scope': 'Ethiopia. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-FI] _ profile = {'code': 'FI', 'name': 'Finland', 'language': 'en', 'scope': 'Finland. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-GE] _ profile = {'code': 'GE', 'name': 'Georgia', 'language': 'en', 'scope': 'Georgia. Expert examination guidance. Selected civil exp...al Georgian trust-services text were consulted; this is not an audit of all amendments or procedural exceptions.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-GR] _ profile = {'code': 'GR', 'name': 'Greece', 'language': 'en', 'scope': 'Greece. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'GR', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No criminal examination route is available for GR [type=value_error, input_value={'jurisdiction': 'GR', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-HK] _ profile = {'code': 'HK', 'name': 'Hong Kong SAR', 'language': 'en', 'scope': 'Hong Kong SAR. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-HU] _ profile = {'code': 'HU', 'name': 'Hungary', 'language': 'en', 'scope': 'Hungary. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-ID] _ profile = {'code': 'ID', 'name': 'Indonesia', 'scope': 'ITE legislation; KUHAP 20/2025, subject to the Article 361 transitional rules', 'language': 'id', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'ID', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, Choose a procedure_scope for ID: criminal-kuhap-2025, criminal-kuhap-1981-transitional [type=value_error, input_value={'jurisdiction': 'ID', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-IE] _ profile = {'code': 'IE', 'name': 'Ireland', 'language': 'en', 'scope': 'Ireland. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-IL] _ profile = {'code': 'IL', 'name': 'Israel', 'language': 'en', 'scope': 'Israel. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-JP] _ profile = {'code': 'JP', 'name': 'Japan', 'language': 'en', 'scope': 'Japan. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-LV] _ profile = {'code': 'LV', 'name': 'Latvia', 'language': 'en', 'scope': 'Latvia. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-LT] _ profile = {'code': 'LT', 'name': 'Lithuania', 'language': 'en', 'scope': 'Lithuania. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-LU] _ profile = {'code': 'LU', 'name': 'Luxembourg', 'language': 'en', 'scope': 'Luxembourg. Expert examination guidance. Civil subset...s, with judicial-language legislation. The complete current code and criminal expert rules were not revalidated.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'LU', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No criminal examination route is available for LU [type=value_error, input_value={'jurisdiction': 'LU', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-MT] _ profile = {'code': 'MT', 'name': 'Malta', 'language': 'en', 'scope': 'Malta. Expert examination guidance. Civil: selected Cap. 1...ractice corroborated by the e-Justice factsheet updated 22 June 2026. This is not a complete current-code audit.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-MD] _ profile = {'code': 'MD', 'name': 'Moldova', 'language': 'en', 'scope': 'Moldova. Expert examination guidance. Common judicial-ex...s across proceedings; route-specific appointment powers and admissibility rules are not represented as verified.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-NL] _ profile = {'code': 'NL', 'name': 'Netherlands', 'language': 'en', 'scope': 'Netherlands. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-NO] _ profile = {'code': 'NO', 'name': 'Norway', 'language': 'en', 'scope': 'Norway. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-PL] _ profile = {'code': 'PL', 'name': 'Poland', 'language': 'en', 'scope': 'Poland. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-RO] _ profile = {'code': 'RO', 'name': 'Romania', 'language': 'en', 'scope': 'Romania. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-SK] _ profile = {'code': 'SK', 'name': 'Slovakia', 'language': 'en', 'scope': 'Slovakia. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-SI] _ profile = {'code': 'SI', 'name': 'Slovenia', 'language': 'en', 'scope': 'Slovenia. Expert examination guidance. Civil expert evi...working consolidation. Criminal procedure and current professional reporting guidelines are outside this subset.', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'SI', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No criminal examination route is available for SI [type=value_error, input_value={'jurisdiction': 'SI', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-KR] _ profile = {'code': 'KR', 'name': 'South Korea', 'language': 'en', 'scope': 'South Korea. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-SE] _ profile = {'code': 'SE', 'name': 'Sweden', 'language': 'en', 'scope': 'Sweden. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-CH] _ profile = {'code': 'CH', 'name': 'Switzerland (federal law)', 'language': 'en', 'scope': 'Switzerland (federal law). Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-TW] _ profile = {'code': 'TW', 'name': 'Taiwan', 'language': 'en', 'scope': 'Taiwan. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-TH] _ profile = {'code': 'TH', 'name': 'Thailand', 'language': 'en', 'scope': 'Thailand. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' > value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tools/test_jurisdiction_guidance.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ options = {'jurisdiction': 'TH', 'procedure': 'criminal', 'local_note': True} assessment = None def build(options=None, *, assessment=None): > choice = Options.model_validate(options or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E pydantic_core._pydantic_core.ValidationError: 1 validation error for Options E Value error, No criminal examination route is available for TH [type=value_error, input_value={'jurisdiction': 'TH', 'p...al', 'local_note': True}, input_type=dict] E For further information visit https://errors.pydantic.dev/2.14/v/value_error tgwitness/jurisdiction_guidance.py:176: ValidationError _ test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-UZ] _ profile = {'code': 'UZ', 'name': 'Uzbekistan', 'language': 'en', 'scope': 'Uzbekistan. Expert examination guidance. ', ...} procedure = 'criminal' @pytest.mark.parametrize('profile', guide.catalog()['profiles'], ids=lambda p: p['code']) @pytest.mark.parametrize('procedure', ['civil', 'criminal']) def test_catalog_routes_keep_sources_local_summary_and_research_status(profile, procedure): criminal = procedure == 'criminal' value = guide.build({'jurisdiction': profile['code'], 'procedure': procedure, 'local_note': True}) assert value['status'] in ('research_draft', 'needs_local_review') assert value['law'] == profile['criminal_law' if criminal else 'law'] assert value['sources'] == profile['criminal_sources' if criminal else 'sources'] > assert value['local_summary']['text'] == profile['local_criminal' if criminal else 'local'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E TypeError: 'NoneType' object is not subscriptable tools/test_jurisdiction_guidance.py:39: TypeError _______ test_choices_do_not_upgrade_facts_or_claim_safeguards_were_used ________ def test_choices_do_not_upgrade_facts_or_claim_safeguards_were_used(): context = {'event_count': 5, 'digest': {'trust_signals': {'tdx': 1}}} expected = case_assessment.journal(context, {}) choice = {'jurisdiction': 'US', 'include_methods': True} actual = case_assessment.journal({**context, 'options': {'jurisdiction_guidance': choice}}, {}) assert actual['checks'] == expected['checks'] assert actual['independent_review'] == 'not_performed' data = actual['jurisdiction_guidance'] assert len(data['safeguards']) == 7 > assert 'does not mean a measure was used or verified' in guide.html(data) E assert 'does not mean a measure was used or verified' in '

Jurisdiction guidance<...ed by a separate party takes the host and hypervisor out of the operator's control.

' E + where '

Jurisdiction guidance<...ed by a separate party takes the host and hypervisor out of the operator's control.

' = ({'format': 'tgwitness-jurisdiction-guidance-v1', 'language': 'en', 'catalog_version': '2026-11-14.1', 'catalog_sha256': '59572096a5989d7ab31cfc484040d6dba5b7372bf8425bee446555f365cbee90', ...}) E + where = guide.html tools/test_jurisdiction_guidance.py:55: AssertionError _______ test_api_permissions_workspace_isolation_and_export_inheritance ________ monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x70b6cc0e0590> def test_api_permissions_workspace_isolation_and_export_inheritance(monkeypatch): from tools.check_prepared_export_access import grant_session from tools.check_session_reservations import workspace async def run(): async with workspace() as (app, client): _, reader = grant_session(app, permissions=['view']) response = await client.get('/api/report-guidance', headers=reader) assert response.status_code == 200 and len(response.json()['profiles']) == 25 assert (await client.get('/api/report-guidance', headers={'Authorization': 'Bearer invalid'})).status_code in (401, 403) options = {'jurisdiction_guidance': {'jurisdiction': 'CA', 'procedure': 'criminal'}} assert (await client.post('/api/report-options', json=options, headers=reader)).status_code == 403 assert (await client.post('/api/report-options', json=options)).status_code == 200 other = (await client.post('/api/profiles', json={'label': 'Separate synthetic case'})).json()['id'] isolated = await client.get('/profiles/'+other+'/api/report-options') assert isolated.json()['jurisdiction_guidance']['jurisdiction'] == '' async def start(values): return values monkeypatch.setattr(app.state.account_export, 'start', start) monkeypatch.setattr(app.state.disclosures, 'preview', lambda export_id, values, owner: values) account = await client.post('/api/account-export/start', json={}) assert account.status_code == 200, account.text assert account.json()['report_presentation']['jurisdiction_guidance']['jurisdiction'] == 'CA' sharing = await client.post('/api/account-export/synthetic/disclosure/preview', json={'purpose': 'Synthetic disclosure review'}) assert sharing.status_code == 200, sharing.text assert sharing.json()['report_presentation']['jurisdiction_guidance']['jurisdiction'] == '' > asyncio.run(run()) tools/test_jurisdiction_guidance.py:155: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/asyncio/runners.py:194: in run return runner.run(main) ^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/runners.py:118: in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /usr/lib/python3.12/asyncio/base_events.py:687: in run_until_complete return future.result() ^^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ async def run(): async with workspace() as (app, client): _, reader = grant_session(app, permissions=['view']) response = await client.get('/api/report-guidance', headers=reader) > assert response.status_code == 200 and len(response.json()['profiles']) == 25 E AssertionError: assert (200 == 200 and 60 == 25) E + where 200 = .status_code E + and 60 = len([{'code': 'AU', 'name': 'Australia', 'scope': 'Commonwealth model; states and territories vary', 'language': 'en', .......}, {'code': 'BG', 'name': 'Bulgaria', 'language': 'en', 'scope': 'Bulgaria. Expert examination guidance. ', ...}, ...]) tools/test_jurisdiction_guidance.py:137: AssertionError ------------------------------ Captured log call ------------------------------- INFO tgwitness.journal:access.py:770 Main workspace · #1 process.started INFO mcp.server.streamable_http_manager:streamable_http_manager.py:162 StreamableHTTP session manager started INFO tgwitness.journal:access.py:770 Main workspace · #2 access.granted INFO tgwitness.access:access.py:678 Access Synthetic reviewer issued (view) INFO tgwitness.journal:access.py:770 Main workspace · #3 access.session_started INFO tgwitness.access:access.py:592 Session 74220911dc96f599 started for Synthetic reviewer from 127.0.0.1 INFO mcp.server.streamable_http_manager:streamable_http_manager.py:166 StreamableHTTP session manager shutting down INFO tgwitness.journal:access.py:770 Main workspace · #4 process.stopped · reason graceful shutdown ____________ test_outbox_read_watermark_does_not_identify_a_person _____________ def test_outbox_read_watermark_does_not_identify_a_person(): class UpdateReadHistoryOutbox: pass update = UpdateReadHistoryOutbox() update.peer, update.max_id = NS(user_id=7000000001), 1500 record = raw_updates.record_for(update, now=2.0) assert record["kind"] == "forensics.outbox_read_watermark" assert record["payload"]["max_id"] == 1500 > assert "does not identify" in record["payload"]["meaning"] E AssertionError: assert 'does not identify' in 'Observed outgoing read watermark.' tools/test_mtproto_forensics.py:290: AssertionError ______________________ test_pdf_html_and_svg_outputs[en] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_0') lang = 'en' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[de] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_1') lang = 'de' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[fr] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_2') lang = 'fr' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[es] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_3') lang = 'es' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[it] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_4') lang = 'it' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[pt] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_5') lang = 'pt' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[nl] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_6') lang = 'nl' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[pl] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_7') lang = 'pl' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[cs] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_8') lang = 'cs' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[sk] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_9') lang = 'sk' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[uk] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_10') lang = 'uk' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ro] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_11') lang = 'ro' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[bg] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_12') lang = 'bg' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[hu] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_13') lang = 'hu' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[el] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_14') lang = 'el' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[sv] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_15') lang = 'sv' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[da] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_16') lang = 'da' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[fi] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_17') lang = 'fi' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[no] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_18') lang = 'no' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[lt] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_19') lang = 'lt' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[lv] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_20') lang = 'lv' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[et] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_21') lang = 'et' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[tr] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_22') lang = 'tr' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ja] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_23') lang = 'ja' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[zh] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_24') lang = 'zh' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ____________________ test_pdf_html_and_svg_outputs[zh-Hant] ____________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_25') lang = 'zh-Hant' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ko] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_26') lang = 'ko' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ar] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_27') lang = 'ar' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[he] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_28') lang = 'he' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[fa] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_29') lang = 'fa' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[hi] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_30') lang = 'hi' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ur] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_31') lang = 'ur' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[bn] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_32') lang = 'bn' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[id] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_33') lang = 'id' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ms] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_34') lang = 'ms' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[vi] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_35') lang = 'vi' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[th] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_36') lang = 'th' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[uz] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_37') lang = 'uz' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError ______________________ test_pdf_html_and_svg_outputs[ru] _______________________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_pdf_html_and_svg_outputs_38') lang = 'ru' @pytest.mark.parametrize('lang', LANGUAGES) def test_pdf_html_and_svg_outputs(tmp_path, lang): package, events = fixture(tmp_path) out = tmp_path / ('narrative-' + lang) model = generate(package, out, lang=lang) assert (out / 'narrative.pdf').read_bytes().startswith(b'%PDF-') assert len(list((out / 'diagrams').glob('*.svg'))) == 4 assert json.loads((out / 'narrative.json').read_text(encoding='utf-8')) == model html = (out / 'narrative.html').read_text(encoding='utf-8') assert events[-1]['hash'] in html > assert 'machine-assisted, not reviewed' == model['translation_review'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E KeyError: 'translation_review' tools/test_narrative.py:83: KeyError _______________ test_jurisdictions_change_only_the_terms_section _______________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_jurisdictions_change_only0') def test_jurisdictions_change_only_the_terms_section(tmp_path): package, events = fixture(tmp_path) model = build(events, verify_bundle(package)) model['package_sha256'] = 'a' * 64 text = Texts('de') pictures = diagrams(model, text) rendered = [html_report(model, text, pictures, jurisdiction(code)) for code in ('DE', 'AT', 'CH')] assert len(set(rendered)) == 3 stripped = [re.sub(r'
.*?
', '', h) for h in rendered] > assert len(set(stripped)) == 1 E assert 3 == 1 E + where 3 = len({' return io.open(self, mode, buffering, encoding, errors, newline) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E FileNotFoundError: [Errno 2] No such file or directory: '/workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tgwitness/narrative/review-policy.json' /usr/lib/python3.12/pathlib.py:1015: FileNotFoundError __________________ test_golden_model_is_language_independent ___________________ def test_golden_model_is_language_independent(): source = Path(__file__).parent / 'fixtures/narrative-events.json' events = json.loads(source.read_text(encoding='utf-8')) result = {'fingerprint': 'synthetic-key', 'events': len(events), 'head_hash': events[-1]['hash']} actual = build(events, result) expected = json.loads(source.with_name('narrative-golden.json').read_text(encoding='utf-8')) > assert actual == expected E AssertionError: assert {'format': 't...e': None, ...} == {'format': 't...e': None, ...} E E Omitting 6 identical items, use -vv to show E Differing items: E {'limits': [{'template_key': 'limit.truth', 'params': {}}]} != {'limits': [{'template_key': 'limit.truth', 'params': {}}, {'template_key': 'limit.review', 'params': {}}]} E Right contains 1 more item: E {'translation_review': 'machine-assisted, not reviewed'} E Use -v to get more diff tools/test_narrative.py:150: AssertionError _ test_optional_diagrams_cannot_hide_assurance_limits_or_references[selected0] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_optional_diagrams_cannot_0') selected = [] @pytest.mark.parametrize('selected', [[], ['timeline'], ['ladder', 'files']]) def test_optional_diagrams_cannot_hide_assurance_limits_or_references(tmp_path, selected): package, events = fixture(tmp_path) out = tmp_path / 'html' model = cli.generate(package, out, outputs=['html'], diagrams=selected) html = (out / 'narrative.html').read_text(encoding='utf-8') assert html.count(' assert text('heading.limits') in html and text('limit.review') in html ^^^^^^^^^^^^^^^^^^^^ tools/test_narrative_config.py:111: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = key = 'limit.review', params = {} def __call__(self, key, **params): > nodes = parse(self.values[key]) ^^^^^^^^^^^^^^^^ E KeyError: 'limit.review' tgwitness/narrative/text.py:138: KeyError _ test_optional_diagrams_cannot_hide_assurance_limits_or_references[selected1] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_optional_diagrams_cannot_1') selected = ['timeline'] @pytest.mark.parametrize('selected', [[], ['timeline'], ['ladder', 'files']]) def test_optional_diagrams_cannot_hide_assurance_limits_or_references(tmp_path, selected): package, events = fixture(tmp_path) out = tmp_path / 'html' model = cli.generate(package, out, outputs=['html'], diagrams=selected) html = (out / 'narrative.html').read_text(encoding='utf-8') assert html.count(' assert text('heading.limits') in html and text('limit.review') in html ^^^^^^^^^^^^^^^^^^^^ tools/test_narrative_config.py:111: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = key = 'limit.review', params = {} def __call__(self, key, **params): > nodes = parse(self.values[key]) ^^^^^^^^^^^^^^^^ E KeyError: 'limit.review' tgwitness/narrative/text.py:138: KeyError _ test_optional_diagrams_cannot_hide_assurance_limits_or_references[selected2] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_optional_diagrams_cannot_2') selected = ['ladder', 'files'] @pytest.mark.parametrize('selected', [[], ['timeline'], ['ladder', 'files']]) def test_optional_diagrams_cannot_hide_assurance_limits_or_references(tmp_path, selected): package, events = fixture(tmp_path) out = tmp_path / 'html' model = cli.generate(package, out, outputs=['html'], diagrams=selected) html = (out / 'narrative.html').read_text(encoding='utf-8') > assert html.count('(' = ' raise ValueError("Connect this account to Telegram first.") E ValueError: Connect this account to Telegram first. tgwitness/secret_chats.py:294: ValueError ____________________ test_independent_login_browser[False] _____________________ workspace = tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_independent_login_browser0') password = False @pytest.mark.parametrize("password", [False, True]) def test_independent_login_browser(workspace, tmp_path, password): from playwright.sync_api import expect, sync_playwright from tools.check_browser import RunningApp, unlock, navigate_panel from tools.browser_helpers import launch_browser, check_layout DonorClient.password = password raw = json.dumps({"session_str": DONOR, "api_id": 4, "api_hash": "a" * 32, "id": 44, **PROFILE, **({"twoFA": "wrong"} if password else {})}).encode() with RunningApp(tmp_path / "panel", client_factory=DonorClient) as server, sync_playwright() as p: browser = launch_browser(p) try: page = browser.new_page(viewport={"width": 1280, "height": 900}) errors = [] page.on("pageerror", lambda error: errors.append(str(error))) unlock(page, server) > navigate_panel(page, "connection") tools/test_session_import.py:339: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tools/check_browser.py:157: in navigate_panel page.locator('.other-sign-in').evaluate('el=>el.open=true') ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/sync_api/_generated.py:11491: in evaluate self._sync( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_locator.py:204: in evaluate return await self._with_element( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_locator.py:123: in _with_element handle = await self.element_handle(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_locator.py:334: in element_handle handle = await self._frame.wait_for_selector( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_frame.py:395: in wait_for_selector await self._channel.send( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_connection.py:78: in send return await self._connection.wrap_api_call( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = cb = . at 0x70b6bd9c2660> is_internal = False, title = None async def wrap_api_call( self, cb: Callable[[], Any], is_internal: bool = False, title: str = None ) -> Any: if self._api_zone.get(): return await cb() task = asyncio.current_task(self._loop) parsed_st = _attach_api_call_information( getattr(task, "__pw_stack__", None) or _capture_stack_trace(), is_internal, title, ) self._api_zone.set(parsed_st) try: return await cb() except Exception as error: > raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None E playwright._impl._errors.TimeoutError: Locator.evaluate: Timeout 30000ms exceeded. E Call log: E - waiting for locator(".other-sign-in") ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_connection.py:636: TimeoutError ------------------------------ Captured log call ------------------------------- INFO tgwitness.journal:access.py:770 Main workspace · #1 process.started INFO mcp.server.streamable_http_manager:streamable_http_manager.py:162 StreamableHTTP session manager started INFO tgwitness.journal:access.py:770 Main workspace · #2 access.session_started INFO tgwitness.access:access.py:592 Session 946f767c4a97da7a started for Main administrator from 127.0.0.1 INFO mcp.server.streamable_http_manager:streamable_http_manager.py:166 StreamableHTTP session manager shutting down INFO tgwitness.journal:access.py:770 Main workspace · #3 process.stopped · reason graceful shutdown _____________________ test_independent_login_browser[True] _____________________ workspace = tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_independent_login_browser1') password = True @pytest.mark.parametrize("password", [False, True]) def test_independent_login_browser(workspace, tmp_path, password): from playwright.sync_api import expect, sync_playwright from tools.check_browser import RunningApp, unlock, navigate_panel from tools.browser_helpers import launch_browser, check_layout DonorClient.password = password raw = json.dumps({"session_str": DONOR, "api_id": 4, "api_hash": "a" * 32, "id": 44, **PROFILE, **({"twoFA": "wrong"} if password else {})}).encode() with RunningApp(tmp_path / "panel", client_factory=DonorClient) as server, sync_playwright() as p: browser = launch_browser(p) try: page = browser.new_page(viewport={"width": 1280, "height": 900}) errors = [] page.on("pageerror", lambda error: errors.append(str(error))) unlock(page, server) > navigate_panel(page, "connection") tools/test_session_import.py:339: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tools/check_browser.py:157: in navigate_panel page.locator('.other-sign-in').evaluate('el=>el.open=true') ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/sync_api/_generated.py:11491: in evaluate self._sync( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_locator.py:204: in evaluate return await self._with_element( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_locator.py:123: in _with_element handle = await self.element_handle(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_locator.py:334: in element_handle handle = await self._frame.wait_for_selector( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_frame.py:395: in wait_for_selector await self._channel.send( ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_connection.py:78: in send return await self._connection.wrap_api_call( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = cb = . at 0x70b6f9fe4220> is_internal = False, title = None async def wrap_api_call( self, cb: Callable[[], Any], is_internal: bool = False, title: str = None ) -> Any: if self._api_zone.get(): return await cb() task = asyncio.current_task(self._loop) parsed_st = _attach_api_call_information( getattr(task, "__pw_stack__", None) or _capture_stack_trace(), is_internal, title, ) self._api_zone.set(parsed_st) try: return await cb() except Exception as error: > raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None E playwright._impl._errors.TimeoutError: Locator.evaluate: Timeout 30000ms exceeded. E Call log: E - waiting for locator(".other-sign-in") ../../venv-dba742dbad616c88/lib/python3.12/site-packages/playwright/_impl/_connection.py:636: TimeoutError ------------------------------ Captured log call ------------------------------- INFO tgwitness.journal:access.py:770 Main workspace · #1 process.started INFO mcp.server.streamable_http_manager:streamable_http_manager.py:162 StreamableHTTP session manager started INFO tgwitness.journal:access.py:770 Main workspace · #2 access.session_started INFO tgwitness.access:access.py:592 Session 90ee0f84f71f2acd started for Main administrator from 127.0.0.1 INFO mcp.server.streamable_http_manager:streamable_http_manager.py:166 StreamableHTTP session manager shutting down INFO tgwitness.journal:access.py:770 Main workspace · #3 process.stopped · reason graceful shutdown ______ test_ots_structure_only_does_not_get_a_positive_indicator[unknown] ______ status = 'unknown' @pytest.mark.parametrize('status', ['pending', 'bitcoin-unverified', 'unknown']) def test_ots_structure_only_does_not_get_a_positive_indicator(status): console = verify.Console(io.StringIO(), 'en', False) checks = [] console.check = lambda state, label, detail: checks.append((state, label, detail)) console.timestamps([{'kind': 'opentimestamps', 'checkpoint': 'synthetic', 'service': 'test', 'status': status}]) assert checks[0][0] != 'ok' assert 'in Bitcoin:' not in checks[0][2] if status == 'unknown': > assert 'unknown' in checks[0][2] E AssertionError: assert 'unknown' in 'synthetic' tools/test_verifier_comparison.py:106: AssertionError _ test_direct_check_preserves_policy_and_archive_without_printing[tgwitness.verify] _ module = 'tgwitness.verify' capsys = <_pytest.capture.CaptureFixture object at 0x70b6bcc39010> @pytest.mark.parametrize('module', ['tgwitness.verify', 'tgwitness.verifier.cli']) def test_direct_check_preserves_policy_and_archive_without_printing(module, capsys): reader = importlib.import_module(module) expected = json.loads(EXPECTED.read_bytes()) policy = reader.verification_parser().parse_args(['--fingerprint', expected['result']['fingerprint']]) unchanged = copy.deepcopy(vars(policy)) archive_hash = hashlib.sha256(FIXTURE.read_bytes()).hexdigest() result = reader.verify_input(FIXTURE, policy) assert result.kind == 'evidence' and result.signer_pinned > assert same_decisions(result.details, expected['result']) E AssertionError: assert False E + where False = same_decisions({'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...}, {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...}) E + where {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...} = .details tools/test_verifier_dispatch.py:24: AssertionError _ test_direct_check_preserves_policy_and_archive_without_printing[tgwitness.verifier.cli] _ module = 'tgwitness.verifier.cli' capsys = <_pytest.capture.CaptureFixture object at 0x70b6ed16c560> @pytest.mark.parametrize('module', ['tgwitness.verify', 'tgwitness.verifier.cli']) def test_direct_check_preserves_policy_and_archive_without_printing(module, capsys): reader = importlib.import_module(module) expected = json.loads(EXPECTED.read_bytes()) policy = reader.verification_parser().parse_args(['--fingerprint', expected['result']['fingerprint']]) unchanged = copy.deepcopy(vars(policy)) archive_hash = hashlib.sha256(FIXTURE.read_bytes()).hexdigest() result = reader.verify_input(FIXTURE, policy) assert result.kind == 'evidence' and result.signer_pinned > assert same_decisions(result.details, expected['result']) E AssertionError: assert False E + where False = same_decisions({'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...}, {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...}) E + where {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...} = .details tools/test_verifier_dispatch.py:24: AssertionError _____________ test_review_modules_import_without_application_state _____________ def test_review_modules_import_without_application_state(): for part in PARTS: importlib.import_module('tgwitness.verifier.' + part) from tgwitness.verifier.bundles import verify_bundle expected = json.loads(EXPECTED.read_bytes()) > assert same_decisions(verify_bundle(FIXTURE, expected['result']['fingerprint']), expected['result']) E AssertionError: assert False E + where False = same_decisions({'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...}, {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...}) E + where {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...} = (PosixPath('/workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tools/fixtures/evidence-before-refactor.zip'), '56475aa75463474c0285df5dbf2bcab73da651358839e9b77481b2eab107708c') tools/test_verifier_sources.py:113: AssertionError _______________ test_frozen_verification_decisions_are_unchanged _______________ def test_frozen_verification_decisions_are_unchanged(): expected = EXPECTED.read_bytes() pin = json.loads(expected)['result']['fingerprint'] result = execute(OUTPUT, FIXTURE, '--json', '--fingerprint', pin) assert result.returncode == 0, result.stderr > assert same_decisions(result.stdout, expected) E assert False E + where False = same_decisions(b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc... },\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n', b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs... exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n') E + where b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc... },\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43...uested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n', stderr=b'').stdout tools/test_verifier_sources.py:121: AssertionError _________ test_old_and_new_decisions_and_statuses_match[pinned---json] _________ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_and_new_decisions_and0/previous.py') mode = '--json', policy = 'pinned' @pytest.mark.parametrize('mode', ['--json', '--plain', '--pretty']) @pytest.mark.parametrize('policy', ['pinned', 'unpinned', 'wrong_key', 'unmet_tier']) def test_old_and_new_decisions_and_statuses_match(previous, mode, policy): pin = json.loads(EXPECTED.read_bytes())['result']['fingerprint'] options = [mode, '--no-color', '--lang', 'en', '--strict'] if policy != 'unpinned': options += ['--fingerprint', '0' * 64 if policy == 'wrong_key' else pin] if policy == 'unmet_tier': options += ['--require-tier', 'witnessed'] before, after = (execute(script, FIXTURE, *options) for script in (previous, OUTPUT)) assert after.returncode == before.returncode assert after.stderr == before.stderr if mode == '--json': > assert same_decisions(after.stdout, before.stdout) E assert False E + where False = same_decisions(b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc... },\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n', b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs... exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n') E + where b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc... },\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43...uested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n', stderr=b'').stdout E + and b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs... exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...d; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n', stderr=b'').stdout tools/test_verifier_sources.py:137: AssertionError ________ test_old_and_new_decisions_and_statuses_match[pinned---plain] _________ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_and_new_decisions_and1/previous.py') mode = '--plain', policy = 'pinned' @pytest.mark.parametrize('mode', ['--json', '--plain', '--pretty']) @pytest.mark.parametrize('policy', ['pinned', 'unpinned', 'wrong_key', 'unmet_tier']) def test_old_and_new_decisions_and_statuses_match(previous, mode, policy): pin = json.loads(EXPECTED.read_bytes())['result']['fingerprint'] options = [mode, '--no-color', '--lang', 'en', '--strict'] if policy != 'unpinned': options += ['--fingerprint', '0' * 64 if policy == 'wrong_key' else pin] if policy == 'unmet_tier': options += ['--require-tier', 'witnessed'] before, after = (execute(script, FIXTURE, *options) for script in (previous, OUTPUT)) assert after.returncode == before.returncode assert after.stderr == before.stderr if mode == '--json': assert same_decisions(after.stdout, before.stdout) elif mode == '--plain' and plain_result(before.stdout) is not None: > assert same_decisions(plain_result(after.stdout), plain_result(before.stdout)) E assert False E + where False = same_decisions({'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...}, {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...}) E + where {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...} = plain_result(b'EVIDENCE: minimum not met \xc2\xb7 missing: external time from a third party covering the last record\n [met] Integ...d.\nIntegrity verified. Minimum not met \xc2\xb7 missing: external time from a third party covering the last record.\n') E + where b'EVIDENCE: minimum not met \xc2\xb7 missing: external time from a third party covering the last record\n [met] Integ...d.\nIntegrity verified. Minimum not met \xc2\xb7 missing: external time from a third party covering the last record.\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43... verified. Minimum not met \xc2\xb7 missing: external time from a third party covering the last record.\n', stderr=b'').stdout E + and {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...} = plain_result(b'ASSURANCE: self_attested \xe2\x80\x94 only the collector\'s own key vouches for this record\n{\n "events": 4,\n "h...point kept away from the collector.\nIntegrity verified. Source authenticity and clock accuracy are not established.\n') E + where b'ASSURANCE: self_attested \xe2\x80\x94 only the collector\'s own key vouches for this record\n{\n "events": 4,\n "h...point kept away from the collector.\nIntegrity verified. Source authenticity and clock accuracy are not established.\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...ay from the collector.\nIntegrity verified. Source authenticity and clock accuracy are not established.\n', stderr=b'').stdout tools/test_verifier_sources.py:139: AssertionError ________ test_old_and_new_decisions_and_statuses_match[unpinned---json] ________ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_and_new_decisions_and3/previous.py') mode = '--json', policy = 'unpinned' @pytest.mark.parametrize('mode', ['--json', '--plain', '--pretty']) @pytest.mark.parametrize('policy', ['pinned', 'unpinned', 'wrong_key', 'unmet_tier']) def test_old_and_new_decisions_and_statuses_match(previous, mode, policy): pin = json.loads(EXPECTED.read_bytes())['result']['fingerprint'] options = [mode, '--no-color', '--lang', 'en', '--strict'] if policy != 'unpinned': options += ['--fingerprint', '0' * 64 if policy == 'wrong_key' else pin] if policy == 'unmet_tier': options += ['--require-tier', 'witnessed'] before, after = (execute(script, FIXTURE, *options) for script in (previous, OUTPUT)) assert after.returncode == before.returncode assert after.stderr == before.stderr if mode == '--json': > assert same_decisions(after.stdout, before.stdout) E assert False E + where False = same_decisions(b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": false,\n "notes": [\n "Message records by sour...},\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": false\n}\n', b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": false,\n "notes": [\n "Chat -1001000000001: ID...exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": false\n}\n') E + where b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": false,\n "notes": [\n "Message records by sour...},\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": false\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43...ested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": false\n}\n', stderr=b'').stdout E + and b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": false,\n "notes": [\n "Chat -1001000000001: ID...exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": false\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": false\n}\n', stderr=b'').stdout tools/test_verifier_sources.py:137: AssertionError _______ test_old_and_new_decisions_and_statuses_match[unpinned---plain] ________ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_and_new_decisions_and4/previous.py') mode = '--plain', policy = 'unpinned' @pytest.mark.parametrize('mode', ['--json', '--plain', '--pretty']) @pytest.mark.parametrize('policy', ['pinned', 'unpinned', 'wrong_key', 'unmet_tier']) def test_old_and_new_decisions_and_statuses_match(previous, mode, policy): pin = json.loads(EXPECTED.read_bytes())['result']['fingerprint'] options = [mode, '--no-color', '--lang', 'en', '--strict'] if policy != 'unpinned': options += ['--fingerprint', '0' * 64 if policy == 'wrong_key' else pin] if policy == 'unmet_tier': options += ['--require-tier', 'witnessed'] before, after = (execute(script, FIXTURE, *options) for script in (previous, OUTPUT)) assert after.returncode == before.returncode assert after.stderr == before.stderr if mode == '--json': assert same_decisions(after.stdout, before.stdout) elif mode == '--plain' and plain_result(before.stdout) is not None: > assert same_decisions(plain_result(after.stdout), plain_result(before.stdout)) E assert False E + where False = same_decisions({'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...}, {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...}) E + where {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...} = plain_result(b'EVIDENCE: minimum not met \xc2\xb7 missing: confirmed signer key; external time from a third party covering the last.... Minimum not met \xc2\xb7 missing: confirmed signer key; external time from a third party covering the last record.\n') E + where b'EVIDENCE: minimum not met \xc2\xb7 missing: confirmed signer key; external time from a third party covering the last.... Minimum not met \xc2\xb7 missing: confirmed signer key; external time from a third party covering the last record.\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43... met \xc2\xb7 missing: confirmed signer key; external time from a third party covering the last record.\n', stderr=b'').stdout E + and {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...} = plain_result(b'ASSURANCE: self_attested \xe2\x80\x94 only the collector\'s own key vouches for this record\n{\n "events": 4,\n "h... clock accuracy are not established.\nSigner identity is unpinned: retain and compare the fingerprint independently.\n') E + where b'ASSURANCE: self_attested \xe2\x80\x94 only the collector\'s own key vouches for this record\n{\n "events": 4,\n "h... clock accuracy are not established.\nSigner identity is unpinned: retain and compare the fingerprint independently.\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...cy are not established.\nSigner identity is unpinned: retain and compare the fingerprint independently.\n', stderr=b'').stdout tools/test_verifier_sources.py:139: AssertionError _______ test_old_and_new_decisions_and_statuses_match[unmet_tier---json] _______ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_and_new_decisions_and9/previous.py') mode = '--json', policy = 'unmet_tier' @pytest.mark.parametrize('mode', ['--json', '--plain', '--pretty']) @pytest.mark.parametrize('policy', ['pinned', 'unpinned', 'wrong_key', 'unmet_tier']) def test_old_and_new_decisions_and_statuses_match(previous, mode, policy): pin = json.loads(EXPECTED.read_bytes())['result']['fingerprint'] options = [mode, '--no-color', '--lang', 'en', '--strict'] if policy != 'unpinned': options += ['--fingerprint', '0' * 64 if policy == 'wrong_key' else pin] if policy == 'unmet_tier': options += ['--require-tier', 'witnessed'] before, after = (execute(script, FIXTURE, *options) for script in (previous, OUTPUT)) assert after.returncode == before.returncode assert after.stderr == before.stderr if mode == '--json': > assert same_decisions(after.stdout, before.stdout) E assert False E + where False = same_decisions(b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc...},\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": false\n}\n', b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs...exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": false\n}\n') E + where b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc...},\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": false\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43...ested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": false\n}\n', stderr=b'').stdout E + and b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs...exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": false\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": false\n}\n', stderr=b'').stdout tools/test_verifier_sources.py:137: AssertionError ______ test_old_and_new_decisions_and_statuses_match[unmet_tier---plain] _______ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_and_new_decisions_and10/previous.py') mode = '--plain', policy = 'unmet_tier' @pytest.mark.parametrize('mode', ['--json', '--plain', '--pretty']) @pytest.mark.parametrize('policy', ['pinned', 'unpinned', 'wrong_key', 'unmet_tier']) def test_old_and_new_decisions_and_statuses_match(previous, mode, policy): pin = json.loads(EXPECTED.read_bytes())['result']['fingerprint'] options = [mode, '--no-color', '--lang', 'en', '--strict'] if policy != 'unpinned': options += ['--fingerprint', '0' * 64 if policy == 'wrong_key' else pin] if policy == 'unmet_tier': options += ['--require-tier', 'witnessed'] before, after = (execute(script, FIXTURE, *options) for script in (previous, OUTPUT)) assert after.returncode == before.returncode assert after.stderr == before.stderr if mode == '--json': assert same_decisions(after.stdout, before.stdout) elif mode == '--plain' and plain_result(before.stdout) is not None: > assert same_decisions(plain_result(after.stdout), plain_result(before.stdout)) E assert False E + where False = same_decisions({'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...}, {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...}) E + where {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...08c', 'coverage': {'continuous': [], 'gaps': [], 'session_windows': [], 'record_sources': {'unlabelled': 3}, ...}, ...} = plain_result(b'EVIDENCE: minimum not met \xc2\xb7 missing: external time from a third party covering the last record\n [met] Integ...ed\nIntegrity verified. Minimum not met \xc2\xb7 missing: external time from a third party covering the last record.\n') E + where b'EVIDENCE: minimum not met \xc2\xb7 missing: external time from a third party covering the last record\n [met] Integ...ed\nIntegrity verified. Minimum not met \xc2\xb7 missing: external time from a third party covering the last record.\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43... verified. Minimum not met \xc2\xb7 missing: external time from a third party covering the last record.\n', stderr=b'').stdout E + and {'events': 4, 'head_hash': 'b5b759fc6898cac1baa7f5da4567510a1318060e0b756a6357dbcff27bf5cdd2', 'fingerprint': '56475aa...3da651358839e9b77481b2eab107708c', 'coverage': {'continuous': [], 'gaps': [], 'notes': [], 'notes_total': 0, ...}, ...} = plain_result(b'ASSURANCE: self_attested \xe2\x80\x94 only the collector\'s own key vouches for this record\n{\n "events": 4,\n "h...nce tier was not reached: witnessed\nIntegrity verified. Source authenticity and clock accuracy are not established.\n') E + where b'ASSURANCE: self_attested \xe2\x80\x94 only the collector\'s own key vouches for this record\n{\n "events": 4,\n "h...nce tier was not reached: witnessed\nIntegrity verified. Source authenticity and clock accuracy are not established.\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...not reached: witnessed\nIntegrity verified. Source authenticity and clock accuracy are not established.\n', stderr=b'').stdout tools/test_verifier_sources.py:139: AssertionError ______ test_old_verifier_accepts_new_archive_with_identical_signed_events ______ previous = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_verifier_accepts_new_0/previous.py') tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_old_verifier_accepts_new_0') def test_old_verifier_accepts_new_archive_with_identical_signed_events(previous, tmp_path): root = tmp_path / 'journal' root.mkdir() signer, store = SyntheticSigner(), Store(root) try: store.bind(signer) with zipfile.ZipFile(FIXTURE) as archive: signed = archive.read('events.jsonl') for line in signed.splitlines(): event = json.loads(line) with patch('tgwitness.store.utcnow', return_value=event['observed_at']): store.append(**{key:event[key] for key in ('kind','payload','chat_id','message_id','actor_id','source_at')}) current = evidence.export_file(store, signer, 'zip', compact=True, include_board=False) with zipfile.ZipFile(current) as archive: assert archive.read('events.jsonl') == signed assert archive.read('verify.py') == OUTPUT.read_bytes() pin = verify.fingerprint(signer.public_key) old_result, new_result = (execute(script, current, '--json', '--fingerprint', pin) for script in (previous, OUTPUT)) assert old_result.returncode == new_result.returncode == 0 > assert same_decisions(new_result.stdout, old_result.stdout) E assert False E + where False = same_decisions(b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc... },\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n', b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs... exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n') E + where b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Message records by sourc... },\n "requested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/workspace/qa/src/c06a2ba5ae98a0ec2f43...uested_checks_unavailable": []\n },\n "evidence_profile": "integrity",\n "requirements_met": true\n}\n', stderr=b'').stdout E + and b'{\n "verified": true,\n "kind": "evidence",\n "signer_pinned": true,\n "notes": [\n "Chat -1001000000001: IDs... exported head; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n' = CompletedProcess(args=['/workspace/qa/venv-dba742dbad616c88/bin/python', '-I', '/tmp/pytest-of-root/pytest-8/test_old_...d; no claim of continuous or complete Telegram observation"\n }\n },\n "requirements_met": true\n}\n', stderr=b'').stdout tools/test_verifier_sources.py:190: AssertionError ________ test_terms_patterns_wallets_usernames_and_domains_are_matched _________ store = def test_terms_patterns_wallets_usernames_and_domains_are_matched(store): with_entries(store, {"kind": "term", "value": "release fee"}, {"kind": "regex", "value": r"\b0\.\d+\s?BTC\b"}, {"kind": "wallet", "value": WALLET}, {"kind": "username", "value": "@Fictional_Admin"}, {"kind": "domain", "value": "pay-fictional.invalid"}, {"kind": "phone", "value": "+1 555 000 1234"}) record(store, "Send the release fee of 0.01 BTC to " + WALLET) record(store, "Write @fictional_admin at https://login.pay-fictional.invalid/x", message_id=2) record(store, "Call +15550001234 now", message_id=3) record(store, "nothing interesting here", message_id=4) result = watchlists.scan(store) found = watchlists.hits(store) assert result["hits"] == 6 and len(found) == 6 assert {item["kind"] for item in found} == {"term", "regex", "wallet", "username", "domain", "phone"} > assert all("nothing about who wrote it" in (store.events(kind="watchlist.hit")[0]["payload"]["meaning"]) for _ in [0]) E assert False E + where False = all(. at 0x70b6ef5daf80>) tools/test_watchlists.py:61: AssertionError ____ test_cloudflare_queries_are_read_only_scoped_and_token_is_not_exported ____ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-8/test_cloudflare_queries_are_re0') def test_cloudflare_queries_are_read_only_scoped_and_token_is_not_exported(tmp_path): journal = Journal(tmp_path / "witness") token = "synthetic-provider-read-token" config = {"account": "a" * 32, "connector_id": "00000000-0000-0000-0000-000000000001", "gateway_location": "00000000-0000-0000-0000-000000000002", "source_ip": "192.0.2.5"} calls = [] def request(path, supplied, body=None): assert supplied == token calls.append((path, body)) if path == "/graphql": assert "mutation" not in body["query"] and body["variables"]["source"] == "192.0.2.5" return {"data": {"viewer": {"accounts": [{"cf1GatewayDnsRawGroups": []}]}}} return {"success": True, "result": {"status": "healthy"}} try: event = provider_observe(journal, config, token, request) assert len(calls) == 3 and all("/token" not in path for path, _ in calls) assert event["payload"]["outcomes"]["dns"]["complete"] is False > assert "no proof of continuous presence" in event["payload"]["scope"] E AssertionError: assert 'no proof of continuous presence' in 'provider state sampled at the polled time' addons/capture/tests/test_trusted_anchors.py:60: AssertionError =============================== warnings summary =============================== tools/test_client_checks.py::test_reported_connection_keeps_actual_language_pack /workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tools/test_client_checks.py:110: UserWarning: Telethon's schema layer differs from the reviewed profile baseline (230); review library compatibility. Imported profile values are retained. fake = collector.make_client(6, "a" * 32) tools/test_session_import.py: 20 warnings /workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tgwitness/session_import.py:113: UserWarning: Telethon's schema layer differs from the reviewed profile baseline (230); review library compatibility. Imported profile values are retained. donor = c.make_client(api_id, api_hash, session, supplied or None, languages) tools/test_session_import.py: 17 warnings /workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tgwitness/session_import.py:74: UserWarning: Telethon's schema layer differs from the reviewed profile baseline (230); review library compatibility. Imported profile values are retained. candidate = c.make_client(credentials["api_id"], credentials["api_hash"], tools/test_session_import.py::test_independent_authorization_and_provenance /workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tools/test_session_import.py:148: UserWarning: Telethon's schema layer differs from the reviewed profile baseline (230); review library compatibility. Imported profile values are retained. donor = c.make_client(4, "a" * 32, DONOR, PROFILE) tools/test_session_import.py: 11 warnings /workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tgwitness/session_import.py:113: UserWarning: The mobile system_lang_code has no region component. Imported profile values are retained. donor = c.make_client(api_id, api_hash, session, supplied or None, languages) tools/test_session_import.py::test_invalid_donor_does_not_expire_saved_authorization /workspace/qa/src/c06a2ba5ae98a0ec2f43b537c24e0a10fd32bd85/tgwitness/collector.py:1779: UserWarning: Telethon's schema layer differs from the reviewed profile baseline (230); review library compatibility. Imported profile values are retained. candidate = self.make_client(api_id, api_hash, session, client_profile, languages) -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html =========================== short test summary info ============================ FAILED tools/test_analyzer_graphs.py::test_edges_cite_journal_records_across_chosen_chats FAILED tools/test_analyzer_linking.py::test_rotated_account_ranks_first_with_explanation_and_citations FAILED tools/test_case_package.py::test_package_contains_readable_report_files_and_explanations[tsr] FAILED tools/test_case_package.py::test_package_contains_readable_report_files_and_explanations[ots] FAILED tools/test_chat_export_integrity.py::test_sparse_message_numbers_are_counted_without_expansion FAILED tools/test_cli.py::test_local_commands - AssertionError: assert [{'wor... FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[2-55123] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[2--1000000000555] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[9-55123] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[9--1000000000555] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[10-55123] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[10--1000000000555] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[15-55123] FAILED tools/test_deletion_semantics.py::test_deletion_of_subset_keeps_actor_and_whole_history_unknown[15--1000000000555] FAILED tools/test_device_attribution.py::test_the_dossier_counts_clients_sticker_sets_and_prepared_media FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-AT] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-BD] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-BE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-BG] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CA] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-HR] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CY] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CZ] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-DK] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-EE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-ET] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-FI] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-GE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-GR] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-HK] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-HU] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-IE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-IL] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-JP] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-LV] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-LT] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-LU] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-MT] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-MD] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-NL] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-NO] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-PL] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-RO] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-SK] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-SI] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-KR] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-SE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-CH] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-TW] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-TH] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[civil-UZ] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-AT] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-BD] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-BE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-BG] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-HR] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-CY] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-CZ] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-DK] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-EE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-ET] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-FI] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-GE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-GR] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-HK] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-HU] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-ID] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-IE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-IL] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-JP] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-LV] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-LT] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-LU] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-MT] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-MD] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-NL] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-NO] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-PL] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-RO] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-SK] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-SI] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-KR] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-SE] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-CH] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-TW] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-TH] FAILED tools/test_jurisdiction_guidance.py::test_catalog_routes_keep_sources_local_summary_and_research_status[criminal-UZ] FAILED tools/test_jurisdiction_guidance.py::test_choices_do_not_upgrade_facts_or_claim_safeguards_were_used FAILED tools/test_jurisdiction_guidance.py::test_api_permissions_workspace_isolation_and_export_inheritance FAILED tools/test_mtproto_forensics.py::test_outbox_read_watermark_does_not_identify_a_person FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[en] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[de] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[fr] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[es] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[it] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[pt] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[nl] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[pl] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[cs] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[sk] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[uk] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ro] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[bg] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[hu] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[el] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[sv] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[da] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[fi] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[no] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[lt] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[lv] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[et] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[tr] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ja] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[zh] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[zh-Hant] - KeyE... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ko] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ar] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[he] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[fa] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[hi] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ur] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[bn] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[id] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ms] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[vi] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[th] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[uz] - KeyError:... FAILED tools/test_narrative.py::test_pdf_html_and_svg_outputs[ru] - KeyError:... FAILED tools/test_narrative.py::test_jurisdictions_change_only_the_terms_section FAILED tools/test_narrative.py::test_catalog_policy_and_plural_edge_cases - F... FAILED tools/test_narrative.py::test_golden_model_is_language_independent - A... FAILED tools/test_narrative_config.py::test_optional_diagrams_cannot_hide_assurance_limits_or_references[selected0] FAILED tools/test_narrative_config.py::test_optional_diagrams_cannot_hide_assurance_limits_or_references[selected1] FAILED tools/test_narrative_config.py::test_optional_diagrams_cannot_hide_assurance_limits_or_references[selected2] FAILED tools/test_secret_chat_safety.py::test_allowed_close_is_sent_and_journaled FAILED tools/test_session_import.py::test_independent_login_browser[False] - ... FAILED tools/test_session_import.py::test_independent_login_browser[True] - p... FAILED tools/test_verifier_comparison.py::test_ots_structure_only_does_not_get_a_positive_indicator[unknown] FAILED tools/test_verifier_dispatch.py::test_direct_check_preserves_policy_and_archive_without_printing[tgwitness.verify] FAILED tools/test_verifier_dispatch.py::test_direct_check_preserves_policy_and_archive_without_printing[tgwitness.verifier.cli] FAILED tools/test_verifier_sources.py::test_review_modules_import_without_application_state FAILED tools/test_verifier_sources.py::test_frozen_verification_decisions_are_unchanged FAILED tools/test_verifier_sources.py::test_old_and_new_decisions_and_statuses_match[pinned---json] FAILED tools/test_verifier_sources.py::test_old_and_new_decisions_and_statuses_match[pinned---plain] FAILED tools/test_verifier_sources.py::test_old_and_new_decisions_and_statuses_match[unpinned---json] FAILED tools/test_verifier_sources.py::test_old_and_new_decisions_and_statuses_match[unpinned---plain] FAILED tools/test_verifier_sources.py::test_old_and_new_decisions_and_statuses_match[unmet_tier---json] FAILED tools/test_verifier_sources.py::test_old_and_new_decisions_and_statuses_match[unmet_tier---plain] FAILED tools/test_verifier_sources.py::test_old_verifier_accepts_new_archive_with_identical_signed_events FAILED tools/test_watchlists.py::test_terms_patterns_wallets_usernames_and_domains_are_matched FAILED addons/capture/tests/test_trusted_anchors.py::test_cloudflare_queries_are_read_only_scoped_and_token_is_not_exported 152 failed, 1909 passed, 35 skipped, 51 warnings, 18 subtests passed in 843.12s (0:14:03)