fix(evidence): close canonical contract gaps

This commit is contained in:
2026-07-12 03:15:17 +02:00
parent 4424fd3d90
commit 293d96e1a6
5 changed files with 176 additions and 15 deletions
+36 -1
View File
@@ -176,7 +176,7 @@ def test_datetimes_must_be_aware_and_are_normalized_to_utc():
def test_source_errors_are_typed_retryable_and_safe():
transient = EvidenceSourceError(
"remote source unavailable",
"password=hunter2 at https://user:secret@host",
category=EvidenceSourceErrorCategory.TRANSIENT,
details={"status": 503},
)
@@ -188,6 +188,16 @@ def test_source_errors_are_typed_retryable_and_safe():
assert transient.retryable is True
assert permanent.retryable is False
assert transient.details["status"] == 503
assert str(transient) == "evidence source operation failed"
assert transient.args == ("evidence source operation failed",)
assert "hunter2" not in repr(transient)
with pytest.raises(AttributeError):
transient.category = EvidenceSourceErrorCategory.PERMANENT
with pytest.raises(AttributeError):
transient.args = ("leak",)
with pytest.raises(AttributeError):
transient.details = {"unsafe": True}
assert "hunter2" not in repr(transient.__dict__)
with pytest.raises(TypeError):
transient.details["status"] = 200
with pytest.raises(ValueError, match="credential-like"):
@@ -202,3 +212,28 @@ def test_source_errors_are_typed_retryable_and_safe():
category=EvidenceSourceErrorCategory.PERMANENT,
details={"not_json": object()},
)
def test_source_error_preserves_original_only_through_exception_chaining():
cause = RuntimeError("transport diagnostic with password=hunter2")
error = EvidenceSourceError(
"ignored unsafe diagnostic",
category=EvidenceSourceErrorCategory.TRANSIENT,
)
try:
raise error from cause
except EvidenceSourceError as caught:
assert caught.__cause__ is cause
assert "hunter2" not in str(caught)
assert "hunter2" not in caught.args
def test_model_copy_revalidates_source_and_acquired_records():
source = SourceObject(source_id="source:a", uri="file:///a", fingerprint="sha256:a")
acquired = AcquiredDocument(source=source, content=b"a")
with pytest.raises(ValidationError, match="namespaced"):
source.model_copy(update={"source_id": "invalid"})
with pytest.raises(ValidationError, match="timezone-aware"):
acquired.model_copy(update={"acquired_at": datetime(2026, 7, 12)})