Add audit-emission test coverage for External Results write endpoints + spec doc update - #330
Merged
Merged
Conversation
… update spec doc Agent-Logs-Url: https://github.com/bg-playground/BGSTM/sessions/2f7fc268-3b43-4776-81a8-8972049d3c89 Co-authored-by: bg-playground <259109604+bg-playground@users.noreply.github.com>
…t test Agent-Logs-Url: https://github.com/bg-playground/BGSTM/sessions/2f7fc268-3b43-4776-81a8-8972049d3c89 Co-authored-by: bg-playground <259109604+bg-playground@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add audit emission test coverage for specified endpoints
Add audit-emission test coverage for External Results write endpoints + spec doc update
May 9, 2026
bg-playground
marked this pull request as ready for review
May 9, 2026 02:10
Owner
|
LGTM ✅ — all 11 checks green including Acceptance check:
Two pieces of careful engineering worth flagging:
Closes #302. Merging. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the remaining gap from #302: the audit emission code in
external_results.pyis already correct, but had no assertive test coverage for thecase.create,case.create.idempotent,case.update, andartifact.uploadpaths. The spec doc also omitted the idempotent action from the taxonomy table.New tests (
test_external_results_audit.py)Per-endpoint audit-emission tests
Four tests mirroring the existing
test_session_start_writes_auditshape — monkeypatchwrite_audit, drive the endpoint happy-path, assert the captured audit entry:test_case_create_writes_audit— verifiesaction,actor_kind,actor_id, and thatdetailscontainssession_id,outcome,external_id,auto_registeredtest_case_create_idempotent_writes_separate_audit— POSTs same payload twice; asserts exactly 1 ×case.create+ 1 ×case.create.idempotent, second response is HTTP 200test_case_update_writes_audit— assertscase.updatewithprevious_outcome/new_outcomein detailstest_artifact_upload_writes_audit— mocksget_storageto a no-opSimpleNamespace; asserts all five required detail fieldsParametrized enforcement test
test_state_changing_endpoint_emits_auditintrospects the live router at collection time:Produces 5 parametrized cases (one per write endpoint). Adding a new write endpoint without
write_auditautomatically fails this test._READ_ONLY_OPSis the explicit allowlist for intentionally non-audited endpoints.Setup prerequisite calls (create session/case) use the same mocked
write_audit;captured.clear()is called immediately before the endpoint under test to prevent setup noise from masking a missingwrite_audit.Spec doc (
external_results_v1.md)Added the missing row to the action taxonomy table in § h:
external_results.case.create.idempotentPOST /casereturning200(duplicateexternal_id)Original prompt
Goal
Close the remaining gap from #302: add the audit-emission test coverage for
external_results.case.create,external_results.case.create.idempotent,external_results.case.update, andexternal_results.artifact.upload, plus a regression-proof parametrized enforcement test that fails by default if a future endpoint is added without an audit call. Update the spec doc to list theidempotentaction.Closes: #302
Decisions already made — do not re-open
external_results.py. The router already callswrite_auditon every state-changing endpoint. This PR is test-and-doc only, with one tiny spec edit. If you find a real bug inwrite_auditcalls, file a separate issue.backend/tests/api/test_external_results_audit.py—monkeypatch.setattr("app.api.external_results.write_audit", fake_write_audit)+ capture list. Don't invent a new fixture pattern.external_results.router.routes, (b) calls each one happy-path, (c) asserts at least onewrite_auditcall captured per endpoint. If a future PR adds a 7th write endpoint and forgetswrite_audit, this test must fail.GETmethods.audit_logmodel. The CheckConstraint enforcingactor_kindinvariants is already correct.Scope
1. Add per-endpoint audit-emission tests (in
backend/tests/api/test_external_results_audit.py)Add four new tests, mirroring the existing
test_session_start_writes_auditshape:test_case_create_writes_audit— POST/external-results/case(happy path withexternal_id+requirement_external_idsto also exercise the diagnostic detail fields). Assertsaction == "external_results.case.create",actor_kind == "runner_token",actor_id == token_model.id, and thatdetailsincludes at leastsession_id,outcome,external_id,auto_registered.test_case_create_idempotent_writes_separate_audit— POST/external-results/casetwice with the sameexternal_id+ samesession_id. The second call returns 200, and aexternal_results.case.create.idempotentaudit must be captured. Both audits must appear (1.create+ 1.create.idempotent).test_case_update_writes_audit— POST a case, then PATCH it with a new outcome. Assertsexternal_results.case.updateis captured withprevious_outcomeandnew_outcomeindetails.test_artifact_upload_writes_audit— POST a case result, then POST/external-results/artifact(multipart) with a small in-memory PNG. Assertsexternal_results.artifact.uploadis captured withcase_result_id,kind,filename,content_type,size_bytesindetails.For the artifact test: use
io.BytesIO(b"\x89PNG\r\n\x1a\n" + b"\x00" * 64)or similar; declarekind=screenshot,filename=test.png,content_type=image/png. The smoke test in CI already exercises the full storage round-trip — this test only needs to confirm audit emission, so monkeypatchingwrite_auditandget_storage(to a no-opSimpleNamespacereturning a fakeStorageResult) is fine.2. Add the regression-proof enforcement test
Add
test_every_state_changing_endpoint_emits_audit: