From 6741ba0ed74be7d4f83ddcc601ef6f63a9167d72 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Sun, 6 Sep 2026 17:23:37 -0700 Subject: [PATCH 1/2] fix(tests): a failing signer must be a real key now, not a duck type agentrust-trace 0.10.0 requires the signing key to be an Ed25519PrivateKey rather than anything carrying a sign method. That is the right direction: a signing key should be a key. It also invalidates this test's stand-in. test_a_failure_inside_official_signing_is_reported_as_one built a hand-rolled FailsWhenUsed class to reach the signing call and raise from inside it. Under 0.10.0 that object is refused by the type check first, so the test saw "key must be an Ed25519PrivateKey, got FailsWhenUsed" instead of the error it was written to observe. Its own docstring warns against exactly this failure, passing on the wrong refusal, one shape earlier than it anticipated. A mock specced to Ed25519PrivateKey satisfies the isinstance check and still raises when used, which is the case the test is about: a real key whose hardware backing is unavailable at the moment of signing. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XbDBXDWWvMFa7c2jGgyq9t --- tests/test_trace_adapter_refusals.py | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/test_trace_adapter_refusals.py b/tests/test_trace_adapter_refusals.py index 956e6d0..41ea4ed 100644 --- a/tests/test_trace_adapter_refusals.py +++ b/tests/test_trace_adapter_refusals.py @@ -23,9 +23,12 @@ import json import sys import unittest +from unittest import mock from dataclasses import replace from pathlib import Path +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey + ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "src")) @@ -176,25 +179,27 @@ def refuse(name, *args, **kwargs): builtins.__import__ = original def test_a_failure_inside_official_signing_is_reported_as_one(self): - """A key that satisfies the shape check and then fails when used. - - `object()` would not reach here: it is refused earlier for having no - `sign`, so a case built that way passes on the wrong refusal. - """ - key = self.key + """A key that satisfies the type check and then fails when used. - class FailsWhenUsed: - def sign(self, *args, **kwargs): - raise RuntimeError("hardware signer unavailable") + `object()` would not reach here, and neither does a duck type: since + agentrust-trace 0.10.0 the signing path requires a real + Ed25519PrivateKey rather than anything carrying a `sign` method, so a + hand-rolled stand-in is refused for the wrong reason and this test + would pass on a refusal it is not about. - def public_key(self): - return key.public_key() + A mock specced to Ed25519PrivateKey satisfies that isinstance check and + still fails when actually used, which is the case this covers: a real + key whose hardware backing is unavailable at the moment of signing. + """ + FailsWhenUsed = mock.MagicMock(spec=Ed25519PrivateKey) + FailsWhenUsed.sign.side_effect = RuntimeError("hardware signer unavailable") + FailsWhenUsed.public_key.return_value = self.key.public_key() snapshot = self.snapshot( [fixture("policy-decision.json"), fixture("data-flow.json")] ) with self.assertRaises(TraceFinalizationError) as caught: - finalize_trace(snapshot, self.config, signing_key=FailsWhenUsed()) + finalize_trace(snapshot, self.config, signing_key=FailsWhenUsed) message = str(caught.exception) self.assertIn("official TRACE signing or validation failed", message) self.assertIn("hardware signer unavailable", message) From 361b354d7f6c3ba46b888d35bc6b95dabac1b384 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Sun, 6 Sep 2026 17:32:59 -0700 Subject: [PATCH 2/2] fix(tests): import cryptography inside the test, not at module scope This class is skipped below Python 3.11, where the trace extra and therefore cryptography are not installed. A module-level import breaks collection before setUpClass can apply that skip, so the 3.10 job failed on ModuleNotFoundError rather than skipping. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XbDBXDWWvMFa7c2jGgyq9t --- tests/test_trace_adapter_refusals.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_trace_adapter_refusals.py b/tests/test_trace_adapter_refusals.py index 41ea4ed..1578b79 100644 --- a/tests/test_trace_adapter_refusals.py +++ b/tests/test_trace_adapter_refusals.py @@ -27,8 +27,6 @@ from dataclasses import replace from pathlib import Path -from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey - ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "src")) @@ -191,6 +189,14 @@ def test_a_failure_inside_official_signing_is_reported_as_one(self): still fails when actually used, which is the case this covers: a real key whose hardware backing is unavailable at the moment of signing. """ + # Imported here rather than at module scope: this class is skipped + # below Python 3.11, where the trace extra and therefore cryptography + # are not installed at all, and a module-level import would break + # collection before the skip can apply. + from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + Ed25519PrivateKey, + ) + FailsWhenUsed = mock.MagicMock(spec=Ed25519PrivateKey) FailsWhenUsed.sign.side_effect = RuntimeError("hardware signer unavailable") FailsWhenUsed.public_key.return_value = self.key.public_key()