Summary
The integration test files for the two largest CRUD/operational routers (pipeline.py, 18 endpoints; operations.py, 16 endpoints) only assert that list endpoints return a 200/array and that a couple of Pydantic validators reject empty input. None of the actual mutating business logic — create/update/delete, RSS collection runs, STIX/MISP/Atlas import, observable enrichment, or detection generation — is exercised at the route level.
Evidence
- backend/tests/integration/test_pipeline_routes.py is 19 lines total: one test loops over
GET /api/pipeline/{sources,runs,observables,detections/versions,audit} asserting 200 + list type, plus one test hitting POST /api/pipeline/detections/validate with empty content. Nothing exercises POST/PUT /sources, POST /sources/{id}/run, POST /observables, POST /observables/{id}/enrich, POST /detections/generate, or any of import/stix, import/misp, import/atlas — 13 of the router's 18 routes (backend/app/api/routes/pipeline.py) have no route-level test.
- backend/tests/integration/test_operations_routes.py is 28 lines total: list-array checks for the four list endpoints, two 422-validation checks, and one invalid-UUID check on delete. None of the actual create/update/delete success paths (
POST/PUT/DELETE on investigations, intake, detections, tracked-actors — 12 of the router's 16 routes) are exercised.
- By contrast,
extract_observables, stix_reports, misp_reports, generate_detection, and validate_detection (the underlying service functions these routes call) do have direct unit tests in backend/tests/unit/test_pipeline.py — but a unit test of the pure function doesn't verify the route wiring, request validation, database persistence, or audit-logging behavior around it.
- Nothing in either file confirms
AuditEvent rows are actually written by the routes that call audit(...) (e.g. source.create, observable.enrich, detection.generate in pipeline.py), despite an audit trail being a stated part of the team-auth model in backend/app/services/auth.py.
Why this matters
This is exactly the surface area most likely to regress silently: it's where most of the application's actual state-changing behavior lives (collection sources, observables, detections, investigations, intake, audit trail). A future refactor of add_observable, ingest_reports, or the audit-call sites could break persistence or silently stop writing audit events, and the existing test suite would still pass at the documented 47% coverage gate.
Attack or failure scenario
Not attacker-facing — this is a quality-assurance gap. The realistic failure mode is a regression shipping unnoticed: e.g. a refactor that breaks ingest_reports's dedupe-by-(url, title) check, or one that breaks flag_modified-style persistence of JSONB fields, would not be caught by any existing test before merge.
Root cause
Tests were added for the pure/stateless helper functions and for basic request validation, but route-level integration coverage was never extended to the mutating endpoints as the routers grew — consistent with the project's own documented "Coverage gate: Partial — enforced at 47%; target is 60%+" status in docs/production-readiness.md.
Recommended fix
Add integration tests covering, at minimum: successful create/update/delete for each entity in both routers; the RSS collection run path (POST /sources/{id}/run) with a mocked fetch_rss; the three import endpoints (import/stix, import/misp, import/atlas) with representative payloads; observables/{id}/enrich with a mocked enrich_observable; detections/generate end to end; and an assertion that AuditEvent rows are created for each audited action.
Acceptance criteria
- Each mutating route in
pipeline.py and operations.py has at least one integration test covering its success path.
- At least one test asserts an
AuditEvent row is written for an audited action.
- Backend coverage gate (
--cov-fail-under) is raised in step with the added tests, per the project's own stated target of 60%+.
Suggested labels
enhancement, testing
Priority
P2
Severity
Medium — no direct security impact, but a significant, well-documented (by the project itself) test-coverage gap on the application's primary mutating logic.
Confidence
Confirmed — verified directly by reading both test files in full and cross-referencing every route defined in pipeline.py and operations.py.
Summary
The integration test files for the two largest CRUD/operational routers (
pipeline.py, 18 endpoints;operations.py, 16 endpoints) only assert that list endpoints return a 200/array and that a couple of Pydantic validators reject empty input. None of the actual mutating business logic — create/update/delete, RSS collection runs, STIX/MISP/Atlas import, observable enrichment, or detection generation — is exercised at the route level.Evidence
GET /api/pipeline/{sources,runs,observables,detections/versions,audit}asserting200+ list type, plus one test hittingPOST /api/pipeline/detections/validatewith empty content. Nothing exercisesPOST/PUT /sources,POST /sources/{id}/run,POST /observables,POST /observables/{id}/enrich,POST /detections/generate, or any ofimport/stix,import/misp,import/atlas— 13 of the router's 18 routes (backend/app/api/routes/pipeline.py) have no route-level test.POST/PUT/DELETEoninvestigations,intake,detections,tracked-actors— 12 of the router's 16 routes) are exercised.extract_observables,stix_reports,misp_reports,generate_detection, andvalidate_detection(the underlying service functions these routes call) do have direct unit tests in backend/tests/unit/test_pipeline.py — but a unit test of the pure function doesn't verify the route wiring, request validation, database persistence, or audit-logging behavior around it.AuditEventrows are actually written by the routes that callaudit(...)(e.g.source.create,observable.enrich,detection.generateinpipeline.py), despite an audit trail being a stated part of the team-auth model inbackend/app/services/auth.py.Why this matters
This is exactly the surface area most likely to regress silently: it's where most of the application's actual state-changing behavior lives (collection sources, observables, detections, investigations, intake, audit trail). A future refactor of
add_observable,ingest_reports, or the audit-call sites could break persistence or silently stop writing audit events, and the existing test suite would still pass at the documented 47% coverage gate.Attack or failure scenario
Not attacker-facing — this is a quality-assurance gap. The realistic failure mode is a regression shipping unnoticed: e.g. a refactor that breaks
ingest_reports's dedupe-by-(url, title)check, or one that breaksflag_modified-style persistence of JSONB fields, would not be caught by any existing test before merge.Root cause
Tests were added for the pure/stateless helper functions and for basic request validation, but route-level integration coverage was never extended to the mutating endpoints as the routers grew — consistent with the project's own documented "Coverage gate: Partial — enforced at 47%; target is 60%+" status in
docs/production-readiness.md.Recommended fix
Add integration tests covering, at minimum: successful create/update/delete for each entity in both routers; the RSS collection run path (
POST /sources/{id}/run) with a mockedfetch_rss; the three import endpoints (import/stix,import/misp,import/atlas) with representative payloads;observables/{id}/enrichwith a mockedenrich_observable;detections/generateend to end; and an assertion thatAuditEventrows are created for each audited action.Acceptance criteria
pipeline.pyandoperations.pyhas at least one integration test covering its success path.AuditEventrow is written for an audited action.--cov-fail-under) is raised in step with the added tests, per the project's own stated target of 60%+.Suggested labels
enhancement, testing
Priority
P2
Severity
Medium — no direct security impact, but a significant, well-documented (by the project itself) test-coverage gap on the application's primary mutating logic.
Confidence
Confirmed — verified directly by reading both test files in full and cross-referencing every route defined in
pipeline.pyandoperations.py.