aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_report.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_report.py')
-rw-r--r--tests/test_report.py110
1 files changed, 110 insertions, 0 deletions
diff --git a/tests/test_report.py b/tests/test_report.py
index 6a340c9..b36252c 100644
--- a/tests/test_report.py
+++ b/tests/test_report.py
@@ -1,6 +1,7 @@
import copy
import email
import email.policy
+import hashlib
import json
import unittest
@@ -1596,5 +1597,114 @@ class DocumentIdentity(unittest.TestCase):
self.assertEqual(addresses[0].display_name, name)
+class Freeze(unittest.TestCase):
+ """A case that has reached a desk is an evidence record, not a draft."""
+
+ def _frozen(self, marker):
+ manifest = copy.deepcopy(MANIFEST)
+ manifest["frozen"] = marker
+ return manifest
+
+ def test_a_frozen_case_refuses(self):
+ manifest = self._frozen({"at": "2026-09-07T10:00:00Z",
+ "by": "abusedb"})
+ with self.assertRaises(report.Frozen) as caught:
+ report.check_regenerable(manifest, modified=[])
+ self.assertIn("abusedb", str(caught.exception))
+
+ def test_a_frozen_case_refuses_even_when_forced(self):
+ manifest = self._frozen({"at": "2026-09-07T10:00:00Z",
+ "by": "abusedb"})
+ with self.assertRaises(report.Frozen):
+ report.check_regenerable(manifest, modified=[], force=True)
+
+ def test_a_frozen_case_refuses_before_the_modified_check(self):
+ """Frozen wins over force for EVERY shape of the second argument.
+
+ The two rules are not independent: an edited body on a frozen case
+ must still refuse, and must refuse as Frozen rather than as
+ Modified, because Modified is the one --force can clear.
+ """
+ manifest = self._frozen({"at": "2026-09-07T10:00:00Z",
+ "by": "abusedb"})
+ with self.assertRaises(report.Frozen):
+ report.check_regenerable(manifest, modified=["email-1"])
+ with self.assertRaises(report.Frozen):
+ report.check_regenerable(manifest, modified=["email-1"],
+ force=True)
+
+ def test_a_present_but_empty_marker_still_freezes(self):
+ """Present means frozen. The marker is write-once and monotonic, so
+ a shape that is present and says nothing useful is a half-written or
+ corrupted record, and the safe direction is to refuse. Truthiness is
+ the wrong test: `{}` is present and falsy, and treating it as absent
+ silently unfreezes a case a desk may already hold.
+ """
+ for marker in ({}, "", 0, []):
+ with self.subTest(marker=marker):
+ with self.assertRaises(report.Frozen):
+ report.check_regenerable(self._frozen(marker),
+ modified=[], force=True)
+
+ def test_a_malformed_marker_refuses_rather_than_crashing(self):
+ """A marker that is not a mapping is still a refusal, not an
+ AttributeError. cli turns Frozen into a message and a traceback into
+ a bug report.
+ """
+ for marker in ("2026-09-07T10:00:00Z", 1, ["abusedb"]):
+ with self.subTest(marker=marker):
+ with self.assertRaises(report.Frozen):
+ report.check_regenerable(self._frozen(marker),
+ modified=[])
+
+ def test_an_explicit_null_marker_is_absent(self):
+ """JSON has no way to omit a key it wrote as null, and null is the
+ one present value that unambiguously carries no record.
+ """
+ manifest = self._frozen(None)
+ report.check_regenerable(manifest, modified=[])
+
+ def test_a_modified_body_refuses_without_force(self):
+ with self.assertRaises(report.Modified) as caught:
+ report.check_regenerable(MANIFEST, modified=["email-1"])
+ self.assertIn("email-1", str(caught.exception))
+
+ def test_a_modified_body_is_allowed_with_force(self):
+ report.check_regenerable(MANIFEST, modified=["email-1"], force=True)
+
+ def test_an_untouched_case_regenerates(self):
+ report.check_regenerable(MANIFEST, modified=[])
+
+ def test_it_does_not_mutate_the_manifest(self):
+ """It is a check, not a step. case.py is the only writer."""
+ manifest = copy.deepcopy(MANIFEST)
+ before = copy.deepcopy(manifest)
+ report.check_regenerable(manifest, modified=[])
+ self.assertEqual(manifest, before)
+
+
+class Hashes(unittest.TestCase):
+ def test_the_hash_detects_a_changed_body(self):
+ first = report.body_hash("one")
+ self.assertNotEqual(first, report.body_hash("two"))
+ self.assertEqual(first, report.body_hash("one"))
+
+ def test_it_is_sha256_of_the_utf8_bytes(self):
+ """The encoding is pinned, not incidental: the hash is compared
+ against a body read back off disk, and for a sent destination it is
+ the record of what was disclosed.
+ """
+ text = "a body with a non-ascii org name: Örg\n"
+ self.assertEqual(
+ report.body_hash(text),
+ hashlib.sha256(text.encode("utf-8")).hexdigest(),
+ )
+
+ def test_it_hashes_a_real_generated_body(self):
+ destination = report.email_destinations(MANIFEST["contacts"])[0]
+ text = report.build(MANIFEST, destination, IDENTITY)
+ self.assertEqual(len(report.body_hash(text)), 64)
+
+
if __name__ == "__main__":
unittest.main()