pseudogen-backend / tests /test_app.py
Zeetay
Add login/register: JWT auth, SQLite users, protected generate, Login/Register pages and AuthContext
3f28422
Raw
History Blame Contribute Delete
5.37 kB
"""
Unit tests for Pseudogen FastAPI app.
Tests GenerateRequest validation, style/detail handling, and generate endpoint with mocked LLM.
Auth is overridden so generate endpoints receive a fake user.
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch
from app import app, GenerateRequest
from auth import get_current_user
# Override auth so generate endpoints see a fake user (plan from user, not header)
FAKE_USER_FREE = {"id": 1, "email": "test@test.com", "plan": "free"}
FAKE_USER_PREMIUM = {"id": 2, "email": "premium@test.com", "plan": "premium"}
async def _fake_user_free():
return FAKE_USER_FREE
async def _fake_user_premium():
return FAKE_USER_PREMIUM
app.dependency_overrides[get_current_user] = _fake_user_free
client = TestClient(app)
def test_root_returns_service_info():
"""GET / returns service info and docs link."""
r = client.get("/")
assert r.status_code == 200
data = r.json()
assert data.get("service") == "Pseudogen API"
assert data.get("docs") == "/docs"
assert "generate-pseudocode" in data.get("generate", "")
def test_generate_request_validates_problem_description_min_length():
"""GenerateRequest rejects empty problem_description."""
with patch("app.call_llm") as mock_llm:
mock_llm.return_value = "BEGIN\nEND"
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "",
"style": "Academic",
"detail": "Concise",
},
)
assert r.status_code == 422
def test_generate_request_validates_style_regex():
"""GenerateRequest rejects invalid style."""
with patch("app.call_llm"):
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "Sort a list",
"style": "InvalidStyle",
"detail": "Concise",
},
)
assert r.status_code == 422
def test_generate_request_validates_detail_regex():
"""GenerateRequest rejects invalid detail."""
with patch("app.call_llm"):
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "Sort a list",
"style": "Academic",
"detail": "Medium",
},
)
assert r.status_code == 422
@pytest.mark.parametrize("style", ["Academic", "Developer-Friendly", "English-Like", "Step-by-Step"])
@pytest.mark.parametrize("detail", ["Concise", "Detailed"])
def test_generate_returns_markdown_for_valid_input(style, detail):
"""Valid request returns 200 and markdown from mocked call_llm."""
with patch("app.call_llm") as mock_llm:
mock_llm.return_value = "BEGIN\n SORT list\nEND"
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "Sort a list of numbers",
"style": style,
"detail": detail,
},
)
assert r.status_code == 200
data = r.json()
assert "markdown" in data
assert data["markdown"] == "BEGIN\n SORT list\nEND"
mock_llm.assert_called_once()
def test_generate_returns_502_when_llm_fails():
"""When call_llm raises, endpoint returns 502."""
with patch("app.call_llm") as mock_llm:
mock_llm.side_effect = RuntimeError("API key invalid")
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "Sort a list",
"style": "Academic",
"detail": "Concise",
},
)
assert r.status_code == 502
assert "API key invalid" in r.json().get("detail", "")
def test_generate_request_max_length_12000():
"""Problem description over 12000 chars is rejected by schema."""
with patch("app.call_llm"):
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "x" * 12001,
"style": "Academic",
"detail": "Concise",
},
)
assert r.status_code == 422
def test_free_plan_rejects_input_over_4000_chars():
"""Without X-Plan: premium, input over 4000 chars returns 400."""
with patch("app.call_llm"):
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "x" * 4001,
"style": "Academic",
"detail": "Concise",
},
)
assert r.status_code == 400
assert "4000" in r.json().get("detail", "")
def test_premium_plan_accepts_input_up_to_12000_chars():
"""With user plan premium, input up to 12000 chars is accepted."""
app.dependency_overrides[get_current_user] = _fake_user_premium
try:
with patch("app.call_llm") as mock_llm:
mock_llm.return_value = "BEGIN\nEND"
r = client.post(
"/generate-pseudocode",
json={
"problem_description": "y" * 10000,
"style": "Academic",
"detail": "Concise",
},
)
assert r.status_code == 200
assert r.json().get("markdown") == "BEGIN\nEND"
finally:
app.dependency_overrides[get_current_user] = _fake_user_free