-
Notifications
You must be signed in to change notification settings - Fork 4
Open
Description
Locations
apps/backend/app/routers/reports.py:32,82(Input validation)package/src/pyaslreport/modalities/testdsc/processor.py:17-30(DSC Placeholder)
Description
Two sub-issues: (1) Passing an invalid modality string (e.g., "xyz") to either BIDS or DICOM endpoint causes ModalityTypeValues("xyz") to raise an uncaught ValueError, returning an unhelpful 500 error. (2) The DSCProcessor returns a hardcoded string "dsc data" instead of a dict, breaking the endpoint's response_model=dict contract.
Reproduction (on main branch)
Automated Results on main branch
| # | Test | Status | Detail |
|---|---|---|---|
| T1 | Valid modality 'ASL' accepted (no 500) | PASS | |
| T2 | Invalid modality 'xyz' on /bids → 422 | FAIL | Status=500 |
| T3 | Invalid modality 'INVALID' on /dicom → 422 | FAIL | Status=400 |
| T4 | No modality → defaults to ASL | PASS | |
| T5 | DSCProcessor returns dict | FAIL | Got string 'dsc data' |
Summary: PASSED=2 FAILED=3
API Responses on main
POST /api/report/process/bids modality=xyz → Status: 500
POST /api/report/process/dicom modality=INVALID → Status: 400 (file check ran first)
DSCProcessor.process() → type=str, value='dsc data'
Code to reproduce this -
"""Bug 4 reproduction: Modality Processing Flaws (Input Validation & DSC Placeholder)"""
import sys, os, json
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'apps', 'backend'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'package', 'src'))
from unittest.mock import MagicMock
sys.modules['weasyprint'] = MagicMock()
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app, raise_server_exceptions=False)
results = []
# ========== Sub-issue 1: Invalid modality input validation ==========
# T1: Valid modality "ASL" should work (returns 400 for no files, not 500 for bad modality)
r1 = client.post("/api/report/process/bids", data={"modality": "ASL"})
ok1 = r1.status_code in (200, 400)
results.append({"test": "T1: Valid modality 'ASL' accepted (no 500)",
"pass": ok1,
"detail": "" if ok1 else f"Status={r1.status_code}"})
# T2: Invalid modality "xyz" on BIDS endpoint should return 422 (not 500)
r2 = client.post("/api/report/process/bids", data={"modality": "xyz"})
ok2 = r2.status_code == 422
results.append({"test": "T2: Invalid modality 'xyz' on /bids returns 422 (not 500)",
"pass": ok2,
"detail": "" if ok2 else f"Status={r2.status_code}"})
# T3: Invalid modality on DICOM endpoint should also return 422 (not 400 for missing files first)
r3 = client.post("/api/report/process/dicom", data={"modality": "INVALID"})
ok3 = r3.status_code == 422
results.append({"test": "T3: Invalid modality 'INVALID' on /dicom returns 422 (not 400/500)",
"pass": ok3,
"detail": "" if ok3 else f"Status={r3.status_code}"})
# T4: Empty modality defaults to ASL (should not crash)
r4 = client.post("/api/report/process/bids")
ok4 = r4.status_code in (200, 400)
results.append({"test": "T4: No modality provided defaults to ASL (no crash)",
"pass": ok4,
"detail": "" if ok4 else f"Status={r4.status_code}"})
# ========== Sub-issue 2: DSC Processor placeholder ==========
# T5: DSC modality should return a proper dict, not a hardcoded string
from pyaslreport.modalities.testdsc.processor import DSCProcessor
proc = DSCProcessor({"modality": "DSC", "files": [], "nifti_file": None, "dcm_files": []})
result = proc.process()
ok5 = isinstance(result, dict)
results.append({"test": "T5: DSCProcessor.process() returns dict (not hardcoded string)",
"pass": ok5,
"detail": "" if ok5 else f"Got type={type(result).__name__}, value={repr(result)}"})
# ========== Print results ==========
passed = sum(1 for r in results if r["pass"])
failed = sum(1 for r in results if not r["pass"])
print(f"PASSED={passed} FAILED={failed}")
for r in results:
status = "PASS" if r["pass"] else "FAIL"
detail = f" -- {r['detail']}" if r["detail"] else ""
print(f" [{status}] {r['test']}{detail}")
with open(os.path.join(os.path.dirname(__file__), 'results.json'), 'w') as f:
json.dump({"passed": passed, "failed": failed, "results": results}, f, indent=2)Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels