import io import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) import numpy as np import soundfile as sf from fastapi.testclient import TestClient from app import app from pipeline_runner import PipelineParams def wav_bytes() -> io.BytesIO: sr = 22050 t = np.arange(int(sr * 0.25)) / sr y = 0.15 * np.sin(2 * np.pi * 220 * t) * np.exp(-t * 10) buf = io.BytesIO() sf.write(buf, y, sr, format="WAV") buf.seek(0) return buf def test_mapping_coerces_browser_form_values() -> None: params = PipelineParams.from_mapping({ "stem": "all", "subdivision": "16", "demucs_shifts": "0", "demucs_overlap": "0.25", "target_min": "2", "target_max": "6", "synthesize": "false", "quantize_midi": "true", "use_disk_cache": "1", }) assert params.subdivision == 16 assert params.demucs_shifts == 0 assert params.demucs_overlap == 0.25 assert params.target_min == 2 assert params.target_max == 6 assert params.synthesize is False assert params.quantize_midi is True assert params.use_disk_cache is True def test_invalid_subdivision_is_actionable() -> None: try: PipelineParams.from_mapping({"subdivision": "12"}) except ValueError as exc: assert "subdivision must be one of 4, 8, 16, 32, 64" in str(exc) else: raise AssertionError("invalid subdivision was accepted") def test_jobs_endpoint_accepts_string_subdivision_from_browser_select() -> None: client = TestClient(app) params = { "stem": "all", "subdivision": "16", "demucs_shifts": "0", "synthesize": "false", "use_disk_cache": "false", } response = client.post( "/api/jobs", files={"file": ("tiny.wav", wav_bytes(), "audio/wav")}, data={"params": json.dumps(params)}, ) assert response.status_code == 202, response.text payload = response.json() assert payload["params"]["subdivision"] == 16 assert payload["params"]["demucs_shifts"] == 0 assert payload["params"]["synthesize"] is False assert payload["params"]["use_disk_cache"] is False def test_jobs_endpoint_reports_bad_parameter_as_visible_detail() -> None: client = TestClient(app) response = client.post( "/api/jobs", files={"file": ("tiny.wav", wav_bytes(), "audio/wav")}, data={"params": json.dumps({"subdivision": "12"})}, ) assert response.status_code == 400 payload = response.json() assert "Invalid extraction parameter" in payload["detail"] assert "subdivision must be one of 4, 8, 16, 32, 64" in payload["detail"] if __name__ == "__main__": test_mapping_coerces_browser_form_values() test_invalid_subdivision_is_actionable() test_jobs_endpoint_accepts_string_subdivision_from_browser_select() test_jobs_endpoint_reports_bad_parameter_as_visible_detail() print("parameter validation and API error visibility contract passed")