# tests/test_api_chains.py — integration tests for chain + upload endpoints from __future__ import annotations import io def test_create_and_list_chains(client): resp = client.post("/api/chains/", json={"name": "my chain"}) assert resp.status_code == 201 data = resp.json() assert data["name"] == "my chain" assert "id" in data resp = client.get("/api/chains/") assert resp.status_code == 200 assert len(resp.json()) == 1 def test_get_chain_not_found(client): resp = client.get("/api/chains/doesnotexist") assert resp.status_code == 404 def test_delete_chain(client): resp = client.post("/api/chains/", json={"name": "to delete"}) chain_id = resp.json()["id"] resp = client.delete(f"/api/chains/{chain_id}") assert resp.status_code == 204 resp = client.get(f"/api/chains/{chain_id}") assert resp.status_code == 404 def test_upload_root_node(client, tmp_path): resp = client.post("/api/chains/", json={"name": "upload test"}) chain_id = resp.json()["id"] # Minimal valid WAV header (44 bytes) so torchaudio doesn't crash # If torchaudio isn't installed, _probe_duration returns 0.0 gracefully wav_bytes = b"RIFF" + b"\x00" * 40 resp = client.post( f"/api/chains/{chain_id}/upload", files={"file": ("test.wav", io.BytesIO(wav_bytes), "audio/wav")}, ) assert resp.status_code == 201 node = resp.json() assert node["status"] == "ready" assert node["is_committed"] is True assert node["parent_id"] is None def test_upload_unsupported_format(client): resp = client.post("/api/chains/", json={"name": "format test"}) chain_id = resp.json()["id"] resp = client.post( f"/api/chains/{chain_id}/upload", files={"file": ("track.txt", io.BytesIO(b"not audio"), "text/plain")}, ) assert resp.status_code == 422 def test_upload_chain_not_found(client): resp = client.post( "/api/chains/nonexistent/upload", files={"file": ("test.wav", io.BytesIO(b""), "audio/wav")}, ) assert resp.status_code == 404