Repository navigation
fix: reject CLEAN submissions with no sequences - #162
matthewberry wants to merge 2 commits into
Conversation
The legacy route skips schema validation and the CLEAN command builder only checked
the upper bound, so `{"input_fasta": {}}` ran CLEAN on an empty FASTA.
matthewberry
left a comment
There was a problem hiding this comment.
The fix is correct. Nothing blocking.
Checked
- Nothing is written for a rejected submission: on the legacy route,
prepare_jobruns before the DB row is written (app/routers/job.py~L107 vs ~L131), so a 400 from the builder leaves no row and creates no Job. - The v1 claim holds, by reading:
_validate_against_schemaruns unconditionally atapp/routers/v1/jobs.py:243, beforeprepare_jobat :300. The only early return between them is an idempotent replay of an existing job.clean.input.jsonmakesinput_fastaan array withminItems: 1, so{}and[]both get a 422 there and never reach the builder. - The tests catch the bug: I ran them in the
Dockerfile.testimage (3 passed). With the== 0guard disabled, both empty cases fail and the one-sequence case still passes. - No conflict with #161: this branch and #161's merge cleanly with each other.
Notes, not for this PR
- The v1 path is covered by the schema, but no test pins it. One
POST /v1/tools/clean/jobswithinput_fasta: []expecting 422 would. - Other malformed input still returns a 500, as before this PR: a non-empty dict (
{"input_fasta": {"a": 1}}) iterates its keys and fails onrecord["sequence"]with aTypeError, and a record withoutDNA_sequenceraisesKeyError. Only the legacy route is affected; v1's schema rejects both.
|
Thanks for the review. I took both notes. v1 test: added in 1e00d2c. 500s on malformed input: I reproduced both on this branch:
v1 returns 422 for both. I agree they're out of scope here. Both come from the legacy route skipping schema validation, which also lets empty input through for 13 other job types, so I opened #170 to cover all of it. |
| # v1 validates against clean.input.json (an array with minItems 1) before the builder runs | ||
| resp = client.post('/v1/tools/clean/jobs', json={'input_fasta': input_fasta}) | ||
|
|
||
| assert resp.status_code == 422 |
There was a problem hiding this comment.
Nit, optional: FastAPI also answers 422 for its own request-validation errors, so a status check alone could keep passing if this request later failed for a different reason. I ran these two payloads against v1 and the 422 is the schema's: title is "Input failed schema validation", with one error at pointer /input_fasta. One valid sequence gets a 201. Pinning that would keep the test honest:
assert resp.status_code == 422
assert resp.json()["title"] == "Input failed schema validation"
assert [e["pointer"] for e in resp.json()["errors"]] == ["/input_fasta"]
Problem
POST /clean/jobsaccepts a CLEAN job with no sequences and runs it.app/routers/job.py). The v1 schema'sminItems: 1(clean.input.json) never applies to it.CleanService.build_clean_job_commandrejects more than 20 sequences, but has no lower bound.{"input_fasta": {}}or[], the builder writes an empty FASTA and the Job starts. CLEAN then crashes, because an empty FASTA leavesget_ec_id_dictwith no rows andtorch.catgets an empty list.Job
8e8bca70…(2026-07-07) was this case. Itsjob_infois{"input_fasta":{}}, its command writes an empty FASTA, and it has no_maxsep.csv. It was marked completed only because of thepipefailbug that #161 fixes. With #161 alone, this case would end aserror; this change rejects it before a Job is created.Change
build_clean_job_commandraises a 400 when there are no sequences, next to the existing over-20 check and in the same style. Both routes call it throughjob_builder.prepare_job. On the legacy route,prepare_jobruns before the DB row is written, so a rejected submission leaves no row and creates no Job. On v1, the schema already rejects it first.Testing
tests/test_clean_empty_input.pyposts to/clean/jobs:main, both empty cases get a 201 and create a Job.Dockerfile.testimage: 626 passed.