From da3788afbca6e6ae94dcc9d2aa726faaf0af1bd7 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Thu, 5 Mar 2026 02:00:38 -0500 Subject: [PATCH] test: add survey invite tracking tests Co-Authored-By: Claude Opus 4.6 --- backend/tests/test_survey.py | 115 +++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 backend/tests/test_survey.py diff --git a/backend/tests/test_survey.py b/backend/tests/test_survey.py new file mode 100644 index 00000000..ebde15c9 --- /dev/null +++ b/backend/tests/test_survey.py @@ -0,0 +1,115 @@ +"""Tests for the public survey submission endpoint.""" +import pytest + + +@pytest.mark.asyncio +async def test_submit_survey_success(client): + """POST valid survey data returns 200 with id.""" + payload = { + "respondent_name": "Test Engineer", + "responses": { + "prereqs": ["Who's affected", "What changed recently"], + "verify_fix": "Have the user confirm it's working", + "steps_at_a_time": "5 steps", + }, + } + response = await client.post("/api/v1/survey/submit", json=payload) + assert response.status_code == 200 + data = response.json() + assert "id" in data + assert data["message"] == "Thank you for your response!" + + +@pytest.mark.asyncio +async def test_submit_survey_anonymous(client): + """Survey works without respondent_name.""" + payload = { + "responses": { + "first_step": "Check if it's one user or many", + }, + } + response = await client.post("/api/v1/survey/submit", json=payload) + assert response.status_code == 200 + assert "id" in response.json() + + +@pytest.mark.asyncio +async def test_submit_survey_missing_responses(client): + """Missing responses field returns 422.""" + payload = {"respondent_name": "Test"} + response = await client.post("/api/v1/survey/submit", json=payload) + assert response.status_code == 422 + + +@pytest.mark.asyncio +async def test_submit_survey_empty_body(client): + """Empty body returns 422.""" + response = await client.post("/api/v1/survey/submit", json={}) + assert response.status_code == 422 + + +@pytest.mark.asyncio +async def test_check_invite_status_not_found(client): + """Invalid token returns 404.""" + response = await client.get("/api/v1/survey/invite/nonexistent") + assert response.status_code == 404 + + +@pytest.mark.asyncio +async def test_submit_with_completed_token_returns_409(client, admin_auth_headers): + """Submitting with an already-used token returns 409.""" + # Create invite + create_res = await client.post( + "/api/v1/admin/survey-invites", + json={"recipient_name": "Test User"}, + headers=admin_auth_headers, + ) + assert create_res.status_code == 200 + token = create_res.json()["token"] + + # First submit succeeds + submit_res = await client.post( + "/api/v1/survey/submit", + json={"responses": {"q1": "answer"}, "token": token}, + ) + assert submit_res.status_code == 200 + + # Second submit with same token returns 409 + submit_res2 = await client.post( + "/api/v1/survey/submit", + json={"responses": {"q1": "answer"}, "token": token}, + ) + assert submit_res2.status_code == 409 + + +@pytest.mark.asyncio +async def test_create_invite_requires_admin(client, auth_headers): + """Non-admin users cannot create invites.""" + response = await client.post( + "/api/v1/admin/survey-invites", + json={"recipient_name": "Test"}, + headers=auth_headers, + ) + assert response.status_code == 403 + + +@pytest.mark.asyncio +async def test_create_and_list_invites(client, admin_auth_headers): + """Admin can create and list invites.""" + # Create + create_res = await client.post( + "/api/v1/admin/survey-invites", + json={"recipient_name": "John Smith", "recipient_email": "john@msp.example.com"}, + headers=admin_auth_headers, + ) + assert create_res.status_code == 200 + data = create_res.json() + assert data["recipient_name"] == "John Smith" + assert data["status"] == "pending" + assert "survey_url" in data + + # List + list_res = await client.get("/api/v1/admin/survey-invites", headers=admin_auth_headers) + assert list_res.status_code == 200 + invites = list_res.json() + assert len(invites) >= 1