diff --git a/tests/test_resumes_api.py b/tests/test_resumes_api.py new file mode 100644 index 0000000..30ea93c --- /dev/null +++ b/tests/test_resumes_api.py @@ -0,0 +1,129 @@ +"""Tests for /api/resumes/* endpoints.""" +import json +import io +import sqlite3 +import tempfile +from pathlib import Path + +import pytest +from fastapi.testclient import TestClient + +from scripts.db_migrate import migrate_db + + +@pytest.fixture +def client(tmp_path, monkeypatch): + db_path = tmp_path / "test.db" + migrate_db(db_path) + monkeypatch.setenv("STAGING_DB", str(db_path)) + import dev_api + monkeypatch.setattr(dev_api, "_request_db", + type("CV", (), {"get": lambda self: str(db_path), "set": lambda *a: None})()) + return TestClient(dev_api.app), db_path + + +def test_create_and_list(client): + c, db = client + resp = c.post("/api/resumes", json={"name": "Base", "text": "Resume text here"}) + assert resp.status_code == 200 + r = resp.json() + assert r["name"] == "Base" + assert r["word_count"] == 3 + + resp2 = c.get("/api/resumes") + assert len(resp2.json()["resumes"]) == 1 + + +def test_get_single(client): + c, db = client + created = c.post("/api/resumes", json={"name": "Test", "text": "text"}).json() + fetched = c.get(f"/api/resumes/{created['id']}").json() + assert fetched["name"] == "Test" + + +def test_patch_resume(client): + c, db = client + created = c.post("/api/resumes", json={"name": "Old", "text": "old text"}).json() + updated = c.patch(f"/api/resumes/{created['id']}", json={"name": "New"}).json() + assert updated["name"] == "New" + assert updated["text"] == "old text" + + +def test_delete_resume(client): + c, db = client + a = c.post("/api/resumes", json={"name": "A", "text": "text a"}).json() + b = c.post("/api/resumes", json={"name": "B", "text": "text b"}).json() + resp = c.delete(f"/api/resumes/{a['id']}") + assert resp.status_code == 200 + assert len(c.get("/api/resumes").json()["resumes"]) == 1 + + +def test_delete_only_resume_rejected(client): + c, db = client + r = c.post("/api/resumes", json={"name": "Only", "text": "text"}).json() + resp = c.delete(f"/api/resumes/{r['id']}") + assert resp.status_code == 409 + + +def test_set_default(client): + c, db = client + a = c.post("/api/resumes", json={"name": "A", "text": "text a"}).json() + b = c.post("/api/resumes", json={"name": "B", "text": "text b"}).json() + c.post(f"/api/resumes/{a['id']}/set-default") + c.post(f"/api/resumes/{b['id']}/set-default") + resumes = {r["id"]: r for r in c.get("/api/resumes").json()["resumes"]} + assert resumes[a["id"]]["is_default"] == 0 + assert resumes[b["id"]]["is_default"] == 1 + + +def test_import_txt(client): + c, db = client + f = io.BytesIO(b"Software engineer with ten years experience building distributed systems.") + resp = c.post("/api/resumes/import", files={"file": ("resume.txt", f, "text/plain")}, + data={"name": "Imported"}) + assert resp.status_code == 200 + r = resp.json() + assert r["source"] == "import" + assert r["word_count"] > 0 + + +def test_import_yaml(client): + c, db = client + yaml_content = b""" +career_summary: Experienced engineer. +experience: + - title: Staff Engineer + company: Acme + start_date: 01/2020 + end_date: Present + bullets: + - Led platform redesign serving 2M users +skills: + - Python + - FastAPI +""" + f = io.BytesIO(yaml_content) + resp = c.post("/api/resumes/import", files={"file": ("resume.yaml", f, "application/x-yaml")}) + assert resp.status_code == 200 + r = resp.json() + assert r["source"] == "import" + assert r["struct_json"] is not None + + +def test_per_job_resume_endpoints(client): + c, db = client + conn = sqlite3.connect(db) + conn.execute("INSERT INTO jobs (id, title, company, source) VALUES (1, 'Eng', 'Co', 'test')") + conn.commit() + conn.close() + + r = c.post("/api/resumes", json={"name": "Default", "text": "default text"}).json() + c.post(f"/api/resumes/{r['id']}/set-default") + + result = c.get("/api/jobs/1/resume").json() + assert result["id"] == r["id"] + + specific = c.post("/api/resumes", json={"name": "Specific", "text": "specific text"}).json() + c.patch("/api/jobs/1/resume", json={"resume_id": specific["id"]}) + result2 = c.get("/api/jobs/1/resume").json() + assert result2["id"] == specific["id"]