Commit Graph

2 Commits

Author SHA1 Message Date
Nora abaf23c122 fix(sp37-followup): SubmitOutcome.UNEXPECTED_ERROR for non-typed failures
Followup #3 from the SP37 final-state tracker. The router's
per-file try/except previously coerced every unexpected exception
to SubmitOutcome.SFTP_FAILED — misleading because a RuntimeError
from cycl_store.add has nothing to do with SFTP.

Changes:
  * submission/result.py — add UNEXPECTED_ERROR = 'unexpected_error'
    enum value. Docstring distinguishes it from the typed SFTP_FAILED.
  * api_routers/submission.py — exception handler now returns
    UNEXPECTED_ERROR (was SFTP_FAILED).
  * test_api_submit_batch.py::test_submit_batch_unexpected_exception_recorded_in_results
    — updated assertion (was sftp_failed, now unexpected_error).
  * test_unexpected_error_outcome.py (new, 4 tests):
    - enum value exists
    - distinct from typed failure values
    - API surfaces 'unexpected_error' for uncaught exceptions
    - typed SFTP_FAILED path still surfaces 'sftp_failed' (regression lock)

Tests: 40/40 pass in 1.41s (full SP37 chain + new file).
Live curl: POST /api/submit-batch in stub mode → HTTP 409 (unchanged
behavior, no regression on the config-level guards).
2026-07-07 12:19:42 -06:00
Cyclone SP37 f1dc06ec3b feat(sp37): POST /api/submit-batch endpoint
Thin HTTP wrapper around `cyclone.submission.submit_file` — the
HTTP twin of the `cyclone submit-batch` CLI (SP37 Task 5). Same
walker pattern, same `._*` AppleDouble skip, same `limit`
semantics, same per-file outcomes; both surfaces now agree on DB +
SFTP state byte-for-byte.

Body: `{ingest_dir, validate?, actor?, limit?}`. Response:
`{submitted, skipped, failed, results:[{file, outcome, batch_id,
error}]}`. Status codes:
  - 200 on completed runs (per-file failures live in the body)
  - 401 unauthenticated (matrix_gate)
  - 404 no clearhouse seeded
  - 409 clearhouse SFTP block in stub mode
  - 422 ingest_dir missing on disk or Pydantic body error

Does NOT inject `sftp_client_factory` — submit_file uses its
default paramiko factory so SKIPPED is reachable in production.
Tests monkey-patch submit_file itself.

Permissions entry: `POST /api/submit-batch` → WRITE_ROLES (admin +
user), matching `POST /api/resubmit` posture.

Tests (11): happy path, AppleDouble skip, limit, empty ingest_dir
422, mixed-outcome 200, unexpected-exception-as-failed,
Pydantic-missing-422, on-disk-missing-422, no-clearhouse-404,
stub-mode-409, auth-gate-401.
2026-07-07 11:36:47 -06:00