Implement bounded SBOM Nexus catch-up
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
This commit is contained in:
parent
192f74f678
commit
8e8c74bd4c
3 changed files with 221 additions and 20 deletions
|
|
@ -1,8 +1,4 @@
|
|||
"""sbom-nexus catch_up resolver (ACTIVITY-WP-0030-T01).
|
||||
|
||||
CUST-WP-0062-T03 has not landed, so the contract is exercised against a test
|
||||
double rather than a live nexus.
|
||||
"""
|
||||
"""sbom-nexus bounded catch-up resolver (ACTIVITY-WP-0030-T01/T02)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -40,6 +36,8 @@ class DummyClient:
|
|||
self._payload = payload
|
||||
self._status_code = status_code
|
||||
self.calls: list[tuple[str, dict[str, Any] | None]] = []
|
||||
self.posts: list[tuple[str, dict[str, Any] | None]] = []
|
||||
self.post_results: dict[str, list[DummyResponse]] = {}
|
||||
|
||||
def __enter__(self) -> "DummyClient":
|
||||
return self
|
||||
|
|
@ -51,6 +49,13 @@ class DummyClient:
|
|||
self.calls.append((url, params))
|
||||
return DummyResponse(self._payload, self._status_code)
|
||||
|
||||
def post(self, url: str, json: dict[str, Any] | None = None) -> DummyResponse:
|
||||
self.posts.append((url, json))
|
||||
for suffix, results in self.post_results.items():
|
||||
if url.endswith(suffix) and results:
|
||||
return results.pop(0)
|
||||
raise AssertionError(f"unexpected POST {url}")
|
||||
|
||||
|
||||
def _install(monkeypatch, payload: Any, status_code: int = 200) -> DummyClient:
|
||||
client = DummyClient(payload, status_code)
|
||||
|
|
@ -155,6 +160,127 @@ def test_catch_up_truncates_an_over_long_response(monkeypatch) -> None:
|
|||
assert result["selected_count"] == 2
|
||||
|
||||
|
||||
def test_apply_processes_at_most_limit_and_records_terminal_outcomes(monkeypatch) -> None:
|
||||
repos = [
|
||||
{
|
||||
"repo_slug": "no-checkout",
|
||||
"last_sbom_at": None,
|
||||
"has_sbom": False,
|
||||
"checkout_available": False,
|
||||
},
|
||||
{
|
||||
"repo_slug": "scan-me",
|
||||
"last_sbom_at": None,
|
||||
"has_sbom": False,
|
||||
"checkout_available": True,
|
||||
},
|
||||
{
|
||||
"repo_slug": "not-selected",
|
||||
"last_sbom_at": None,
|
||||
"has_sbom": False,
|
||||
"checkout_available": True,
|
||||
},
|
||||
]
|
||||
client = _install(monkeypatch, _payload(repos))
|
||||
client.post_results = {
|
||||
"/sbom/no-checkout/skip": [
|
||||
DummyResponse(
|
||||
{
|
||||
"repo_slug": "no-checkout",
|
||||
"status": "skipped",
|
||||
"reason": "no-checkout",
|
||||
"snapshot_id": "skip-1",
|
||||
}
|
||||
)
|
||||
],
|
||||
"/sbom/scan-me/ingest": [
|
||||
DummyResponse(
|
||||
{
|
||||
"repo_slug": "scan-me",
|
||||
"status": "ingested",
|
||||
"snapshot_id": "scan-1",
|
||||
"entry_count": 12,
|
||||
}
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
result = SbomNexusContextResolver().resolve(
|
||||
"catch_up", None, {"limit": 2, "apply": True}
|
||||
)
|
||||
|
||||
assert result["attempted_count"] == 2
|
||||
assert result["updated"] == [
|
||||
{
|
||||
"repo_slug": "scan-me",
|
||||
"status": "ingested",
|
||||
"snapshot_id": "scan-1",
|
||||
"entry_count": 12,
|
||||
}
|
||||
]
|
||||
assert result["skipped"] == [
|
||||
{
|
||||
"repo_slug": "no-checkout",
|
||||
"status": "skipped",
|
||||
"reason": "no-checkout",
|
||||
"snapshot_id": "skip-1",
|
||||
}
|
||||
]
|
||||
assert len(client.posts) == 2
|
||||
assert all("not-selected" not in url for url, _body in client.posts)
|
||||
|
||||
|
||||
def test_apply_records_ingest_error_when_ingest_fails(monkeypatch) -> None:
|
||||
client = _install(
|
||||
monkeypatch,
|
||||
_payload(
|
||||
[
|
||||
{
|
||||
"repo_slug": "broken",
|
||||
"last_sbom_at": None,
|
||||
"has_sbom": False,
|
||||
"checkout_available": True,
|
||||
}
|
||||
]
|
||||
),
|
||||
)
|
||||
client.post_results = {
|
||||
"/sbom/broken/ingest": [DummyResponse({}, status_code=503)],
|
||||
"/sbom/broken/skip": [
|
||||
DummyResponse(
|
||||
{
|
||||
"repo_slug": "broken",
|
||||
"status": "skipped",
|
||||
"reason": "ingest-error",
|
||||
"snapshot_id": "skip-error-1",
|
||||
}
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
result = SbomNexusContextResolver().resolve(
|
||||
"catch_up", None, {"limit": 3, "apply": True}
|
||||
)
|
||||
|
||||
assert result["attempted_count"] == 1
|
||||
assert result["updated"] == []
|
||||
assert result["skipped"][0]["reason"] == "ingest-error"
|
||||
assert client.posts[1][1] == {
|
||||
"reason": "ingest-error",
|
||||
"detail": "HTTPStatusError",
|
||||
}
|
||||
|
||||
|
||||
def test_read_only_default_never_posts(monkeypatch) -> None:
|
||||
client = _install(monkeypatch, _payload([{"repo_slug": "selected"}]))
|
||||
|
||||
result = SbomNexusContextResolver().resolve("catch_up", None, {"limit": 1})
|
||||
|
||||
assert result["selected_count"] == 1
|
||||
assert "attempted_count" not in result
|
||||
assert client.posts == []
|
||||
|
||||
|
||||
def test_catch_up_normalises_partial_entries(monkeypatch) -> None:
|
||||
_install(
|
||||
monkeypatch,
|
||||
|
|
@ -312,6 +438,7 @@ def test_daily_definition_is_bounded_and_disabled() -> None:
|
|||
assert source["type"] == "sbom-nexus"
|
||||
assert source["query"] == "catch_up"
|
||||
assert source["params"]["limit"] == 3
|
||||
assert source["params"]["apply"] is True
|
||||
assert source["bind_to"] == "context.catchup"
|
||||
instruction = definition.instructions[0]
|
||||
assert instruction["model"] == "deterministic"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue