..............................F...F..................................... [ 3%] ........................................................................ [ 6%] .........FF..............................................F.............. [ 9%] .................F...................................................... [ 12%] .........................................FFFFFFFF...............F....... [ 15%] ........................................................................ [ 19%] ........................................................................ [ 22%] ........................................................................ [ 25%] ........................................................................ [ 28%] ........................................................................ [ 31%] ........................................................................ [ 35%] ................................................................................................................................. [ 40%] ........................................................................ [ 44%] ........................................................................ [ 47%] ...............F........................................................ [ 50%] ........................................................................ [ 53%] ........................................................................ [ 56%] .................................................................FFFFFFF [ 60%] FFF.........sss..s...................................................... [ 63%] ........................................................................ [ 66%] ........................................................................ [ 69%] ........................................................................ [ 72%] .....................................FF.............................. [ 75%] ........................................................................ [ 79%] ..............................ss........................................ [ 82%] ......................FFF............................................... [ 85%] ..................................................ssssssssssssssssssssss [ 88%] sss.....................................................FFFF.FF....FF... [ 91%] F....F.................................................................. [ 94%] ................................................sss..................... [ 98%] ...................................s..F.. [100%] =================================== FAILURES =================================== _____________ test_edges_cite_journal_records_across_chosen_chats ______________ store = (, {'pay': {'seq': 1, 'observed_at': '2026-11-14T08:12:41.902+00:00', ...ward': {'seq': 4, 'observed_at': '2026-11-14T08:12:41.932+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 0x7101b5b0b6b0>) 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 0x7101b638d2f0>) tools/test_analyzer_linking.py:81: AssertionError ______ test_package_contains_readable_report_files_and_explanations[tsr] _______ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/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-9/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-9/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-9/test_local_commands0') capsys = <_pytest.capture.CaptureFixture object at 0x7101a04efa40> 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-9/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-9/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-9/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-9/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-9/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-9/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-9/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-9/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-9/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_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_unreadable_settings_never_restore_enabled_defaults[] ___________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never0') data = b'' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed __________ test_unreadable_settings_never_restore_enabled_defaults[{] __________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never1') data = b'{' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed _________ test_unreadable_settings_never_restore_enabled_defaults[[]] __________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never2') data = b'[]' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed ________ test_unreadable_settings_never_restore_enabled_defaults[null] _________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never3') data = b'null' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed _ test_unreadable_settings_never_restore_enabled_defaults[{"countries":"invalid"}] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never4') data = b'{"countries":"invalid"}' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed _ test_unreadable_settings_never_restore_enabled_defaults[{"github":{"enabled":[]}}] _ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never5') data = b'{"github":{"enabled":[]}}' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed __ test_unreadable_settings_never_restore_enabled_defaults[{"unknown":true}] ___ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never6') data = b'{"unknown":true}' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed ________ test_unreadable_settings_never_restore_enabled_defaults[\xff] _________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_unreadable_settings_never7') data = b'\xff' @pytest.mark.parametrize("data", [b"", b"{", b"[]", b"null", b'{"countries":"invalid"}', b'{"github":{"enabled":[]}}', b'{"unknown":true}', b"\xff"]) def test_unreadable_settings_never_restore_enabled_defaults(tmp_path, data): path = tmp_path / proxy_sources.SETTINGS path.write_bytes(data) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:37: Failed ____________ test_permission_error_never_restores_enabled_defaults _____________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_permission_error_never_re0') monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7101aa6cbef0> def test_permission_error_never_restores_enabled_defaults(tmp_path, monkeypatch): def denied(*args, **kwargs): raise PermissionError("private-path") monkeypatch.setattr(Path, "read_text", denied) > with pytest.raises(ValueError, match="Restore proxy-sources.json") as failure: E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:46: Failed _____________ test_invalid_settings_prevent_refresh_before_network _____________ tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/test_invalid_settings_prevent_0') monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7101aa6c98e0> def test_invalid_settings_prevent_refresh_before_network(tmp_path, monkeypatch): (tmp_path / proxy_sources.SETTINGS).write_text("{") download = AsyncMock(side_effect=AssertionError("network request")) monkeypatch.setattr("tgwitness.proxy_catalog.download", download) > with pytest.raises(ValueError, match="Restore proxy-sources.json"): E Failed: DID NOT RAISE ValueError tools/test_proxy_source_settings.py:55: Failed ____________________ test_independent_login_browser[False] _____________________ workspace = tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/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 0x71018af3db20> 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 c707e1a5cdb16f3f 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-9/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 0x71017087b240> 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 d2dbf4f51a988710 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 0x710170281ac0> @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 0x710170280740> @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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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/3a614fb780cd1d235fdf...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-9/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/3a614fb780cd1d235fdf...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-9/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-9/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/3a614fb780cd1d235fdf... 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-9/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-9/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/3a614fb780cd1d235fdf...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-9/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-9/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/3a614fb780cd1d235fdf... 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-9/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-9/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/3a614fb780cd1d235fdf...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-9/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-9/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/3a614fb780cd1d235fdf... 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-9/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-9/test_old_verifier_accepts_new_0/previous.py') tmp_path = PosixPath('/tmp/pytest-of-root/pytest-9/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/3a614fb780cd1d235fdf...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-9/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 0x7101b6be35a0>) 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-9/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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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/3a614fb780cd1d235fdf77e9b97d91828af1c219/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_mtproto_forensics.py::test_outbox_read_watermark_does_not_identify_a_person FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[{] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[[]] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[null] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[{"countries":"invalid"}] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[{"github":{"enabled":[]}}] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[{"unknown":true}] FAILED tools/test_proxy_source_settings.py::test_unreadable_settings_never_restore_enabled_defaults[\xff] FAILED tools/test_proxy_source_settings.py::test_permission_error_never_restores_enabled_defaults FAILED tools/test_proxy_source_settings.py::test_invalid_settings_prevent_refresh_before_network 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 42 failed, 2178 passed, 35 skipped, 51 warnings, 18 subtests passed in 1026.53s (0:17:06)