/ packages / auths-python / tests / test_artifact_sign.py
test_artifact_sign.py
  1  """Tests for artifact attestation signing and publishing."""
  2  
  3  from unittest.mock import MagicMock
  4  
  5  import pytest
  6  
  7  from auths import ArtifactPublishResult, ArtifactSigningResult
  8  from auths.artifact import ArtifactPublishResult as ArtifactPublishFromModule
  9  from auths.artifact import ArtifactSigningResult as ArtifactFromModule
 10  
 11  
 12  class TestArtifactSigningResult:
 13  
 14      def test_fields(self):
 15          r = ArtifactSigningResult(
 16              attestation_json='{"rid":"sha256:abc"}',
 17              rid="sha256:abc123def456",
 18              digest="abc123def456",
 19              file_size=1024,
 20          )
 21          assert r.attestation_json == '{"rid":"sha256:abc"}'
 22          assert r.rid == "sha256:abc123def456"
 23          assert r.digest == "abc123def456"
 24          assert r.file_size == 1024
 25  
 26      def test_repr_shows_size(self):
 27          r = ArtifactSigningResult(
 28              attestation_json="{}",
 29              rid="sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
 30              digest="a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
 31              file_size=2_500_000,
 32          )
 33          s = repr(r)
 34          assert "ArtifactSigningResult" in s
 35          assert "MB" in s
 36  
 37      def test_repr_small_file(self):
 38          r = ArtifactSigningResult(
 39              attestation_json="{}",
 40              rid="sha256:short",
 41              digest="short",
 42              file_size=512,
 43          )
 44          s = repr(r)
 45          assert "512 B" in s
 46  
 47      def test_repr_kb(self):
 48          r = ArtifactSigningResult(
 49              attestation_json="{}",
 50              rid="sha256:x",
 51              digest="x",
 52              file_size=15_360,
 53          )
 54          s = repr(r)
 55          assert "KB" in s
 56  
 57  
 58  class TestImports:
 59  
 60      def test_importable_from_top_level(self):
 61          from auths import ArtifactSigningResult
 62          assert ArtifactSigningResult is not None
 63  
 64      def test_importable_from_module(self):
 65          from auths.artifact import ArtifactSigningResult
 66          assert ArtifactSigningResult is not None
 67  
 68      def test_ffi_functions_importable(self):
 69          from auths._native import sign_artifact, sign_artifact_bytes
 70          assert sign_artifact is not None
 71          assert sign_artifact_bytes is not None
 72  
 73      def test_sign_artifact_nonexistent_file(self):
 74          from auths._native import sign_artifact
 75          with pytest.raises(FileNotFoundError, match="not found"):
 76              sign_artifact("/nonexistent/path/file.bin", "main", "/tmp", None, None, None)
 77  
 78  
 79  class TestArtifactPublishResult:
 80  
 81      def test_fields(self):
 82          r = ArtifactPublishResult(
 83              attestation_rid="rid-abc",
 84              package_name="npm:react@18.3.0",
 85              signer_did="did:keri:abc",
 86          )
 87          assert r.attestation_rid == "rid-abc"
 88          assert r.package_name == "npm:react@18.3.0"
 89          assert r.signer_did == "did:keri:abc"
 90  
 91      def test_package_name_none(self):
 92          r = ArtifactPublishResult(attestation_rid="x", package_name=None, signer_did="y")
 93          assert r.package_name is None
 94  
 95      def test_repr_truncates_long_rid(self):
 96          long_rid = "a" * 60
 97          r = ArtifactPublishResult(attestation_rid=long_rid, package_name=None, signer_did="did:keri:z")
 98          assert len(repr(r)) < len(long_rid) + 40
 99          assert "..." in repr(r)
100  
101      def test_repr_with_package(self):
102          r = ArtifactPublishResult(attestation_rid="rid", package_name="npm:x", signer_did="did:keri:abc")
103          assert "npm:x" in repr(r)
104  
105      def test_repr_without_package(self):
106          r = ArtifactPublishResult(attestation_rid="rid", package_name=None, signer_did="did:keri:abc")
107          assert "pkg" not in repr(r)
108  
109      def test_top_level_export(self):
110          assert ArtifactPublishResult is ArtifactPublishFromModule
111  
112  
113  class TestPublishArtifactNative:
114  
115      def test_import(self):
116          from auths._native import publish_artifact  # noqa: F401
117  
118      def test_invalid_json_raises(self):
119          from auths._native import publish_artifact
120          with pytest.raises((ValueError, RuntimeError)):
121              publish_artifact("not-json", "http://localhost", None)
122  
123      def test_unreachable_host_raises(self):
124          from auths._native import publish_artifact
125          with pytest.raises((RuntimeError, OSError, ConnectionError)):
126              publish_artifact('{"attestation":"x"}', "http://127.0.0.1:1", None)
127  
128  
129  class TestPublishArtifactClient:
130  
131      def test_method_exists(self):
132          from auths import Auths
133          assert hasattr(Auths, "publish_artifact")
134  
135      def test_returns_result_type(self, monkeypatch):
136          import auths._native as native
137          mock_raw = MagicMock()
138          mock_raw.attestation_rid = "rid-1"
139          mock_raw.package_name = "npm:foo"
140          mock_raw.signer_did = "did:keri:abc"
141          monkeypatch.setattr(native, "publish_artifact", lambda *_: mock_raw)
142          from auths import Auths
143          result = Auths().publish_artifact('{"a":1}', registry_url="http://x")
144          assert isinstance(result, ArtifactPublishResult)
145          assert result.attestation_rid == "rid-1"
146  
147      def test_duplicate_raises_storage_error(self, monkeypatch):
148          import auths._native as native
149  
150          def _raise(*_):
151              raise RuntimeError("duplicate_attestation: artifact attestation already published (duplicate RID)")
152  
153          monkeypatch.setattr(native, "publish_artifact", _raise)
154          from auths import Auths
155          from auths._errors import StorageError
156          with pytest.raises(StorageError) as exc_info:
157              Auths().publish_artifact('{"a":1}', registry_url="http://x")
158          assert exc_info.value.code == "duplicate_attestation"
159  
160      def test_verification_failed_raises_verification_error(self, monkeypatch):
161          import auths._native as native
162  
163          def _raise(*_):
164              raise RuntimeError("verification_failed: signature rejected by registry")
165  
166          monkeypatch.setattr(native, "publish_artifact", _raise)
167          from auths import Auths
168          from auths._errors import VerificationError
169          with pytest.raises(VerificationError) as exc_info:
170              Auths().publish_artifact('{"a":1}', registry_url="http://x")
171          assert exc_info.value.code == "verification_failed"
172  
173      def test_network_error_raises_network_error(self, monkeypatch):
174          import auths._native as native
175  
176          def _raise(*_):
177              raise RuntimeError("registry unreachable: connection refused")
178  
179          monkeypatch.setattr(native, "publish_artifact", _raise)
180          from auths import Auths
181          from auths._errors import NetworkError
182          with pytest.raises(NetworkError):
183              Auths().publish_artifact('{"a":1}', registry_url="http://x")