File size: 5,368 Bytes
2276aab
 
 
3f28422
2276aab
 
 
 
 
 
3f28422
2276aab
3f28422
 
 
 
 
 
 
 
 
 
 
 
 
 
2276aab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f28422
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
"""
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