Skip to content

Commit 1245b28

Browse files
bnmnetpclaude
andcommitted
Phase 3: add route smoke tests for book and assignment servers
Book server (10 tests): - Verifies all major router prefixes are registered - Authenticated routes (bookevent, assessment, course) return 401/422 - Public routes (auth/login, books/crashtest) respond as expected Assignment server (17 tests): - Verifies student, instructor, and peer router prefixes are registered - All student routes return 401/422 unauthenticated - All instructor CRUD routes (list/get/create/update/delete assignments, gradebook, questions, roster) return 401/422 unauthenticated - Peer student/instructor routes return 401/422 unauthenticated No database required — these tests exercise route registration and auth enforcement only, using FastAPI's TestClient. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8ea0695 commit 1245b28

2 files changed

Lines changed: 216 additions & 6 deletions

File tree

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,124 @@
1-
from rsptx.assignment_server_api import core
1+
"""
2+
Smoke tests for the assignment server API.
23
4+
Verifies route registration and that unauthenticated requests return the
5+
expected auth error codes (401/422), not 500. No database is required.
6+
"""
37

4-
def test_sample():
5-
assert core is not None
8+
import pytest
9+
from fastapi.testclient import TestClient
10+
11+
from rsptx.assignment_server_api.core import app
12+
13+
14+
@pytest.fixture(scope="module")
15+
def client():
16+
with TestClient(app, raise_server_exceptions=False) as c:
17+
yield c
18+
19+
20+
# ---------------------------------------------------------------------------
21+
# App-level
22+
# ---------------------------------------------------------------------------
23+
24+
def test_app_exists():
25+
assert app is not None
26+
27+
28+
def test_routes_registered():
29+
paths = {r.path for r in app.routes}
30+
assert any("/student" in p for p in paths)
31+
assert any("/instructor" in p for p in paths)
32+
assert any("/peer" in p for p in paths)
33+
34+
35+
# ---------------------------------------------------------------------------
36+
# /student — all routes require auth
37+
# ---------------------------------------------------------------------------
38+
39+
def test_choose_assignment_unauthenticated(client):
40+
resp = client.get("/student/chooseAssignment")
41+
assert resp.status_code in (401, 422)
42+
43+
44+
def test_do_assignment_unauthenticated(client):
45+
resp = client.get("/student/doAssignment")
46+
assert resp.status_code in (401, 422)
47+
48+
49+
def test_update_submit_unauthenticated(client):
50+
resp = client.post("/student/update_submit", json={})
51+
assert resp.status_code in (401, 422)
52+
53+
54+
def test_studyclues_query_unauthenticated(client):
55+
resp = client.post("/student/studyclues_query", json={})
56+
assert resp.status_code in (401, 422)
57+
58+
59+
# ---------------------------------------------------------------------------
60+
# /instructor — all routes require auth + instructor role
61+
# ---------------------------------------------------------------------------
62+
63+
def test_assignments_list_unauthenticated(client):
64+
resp = client.get("/instructor/assignments")
65+
assert resp.status_code in (401, 422)
66+
67+
68+
def test_assignment_get_unauthenticated(client):
69+
resp = client.get("/instructor/assignments/1")
70+
assert resp.status_code in (401, 422)
71+
72+
73+
def test_assignment_create_unauthenticated(client):
74+
resp = client.post("/instructor/assignments", json={})
75+
assert resp.status_code in (401, 422)
76+
77+
78+
def test_assignment_update_unauthenticated(client):
79+
resp = client.put("/instructor/assignments/1", json={})
80+
assert resp.status_code in (401, 422)
81+
82+
83+
def test_assignment_delete_unauthenticated(client):
84+
resp = client.delete("/instructor/assignments/1")
85+
assert resp.status_code in (401, 422)
86+
87+
88+
def test_gradebook_unauthenticated(client):
89+
resp = client.get("/instructor/gradebook")
90+
assert resp.status_code in (401, 422)
91+
92+
93+
def test_new_question_unauthenticated(client):
94+
resp = client.post("/instructor/new_question", json={})
95+
assert resp.status_code in (401, 422)
96+
97+
98+
def test_new_assignment_q_unauthenticated(client):
99+
resp = client.post("/instructor/new_assignment_q", json={})
100+
assert resp.status_code in (401, 422)
101+
102+
103+
def test_search_questions_unauthenticated(client):
104+
resp = client.post("/instructor/search_questions", json={})
105+
assert resp.status_code in (401, 422)
106+
107+
108+
def test_course_roster_unauthenticated(client):
109+
resp = client.get("/instructor/course_roster")
110+
assert resp.status_code in (401, 422)
111+
112+
113+
# ---------------------------------------------------------------------------
114+
# /peer — all routes require auth
115+
# ---------------------------------------------------------------------------
116+
117+
def test_peer_student_unauthenticated(client):
118+
resp = client.get("/peer/student")
119+
assert resp.status_code in (401, 422)
120+
121+
122+
def test_peer_instructor_unauthenticated(client):
123+
resp = client.get("/peer/instructor")
124+
assert resp.status_code in (401, 422)
Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,96 @@
1-
from rsptx.book_server_api import core
1+
"""
2+
Smoke tests for the book server API.
23
4+
Verifies route registration and that unauthenticated requests return the
5+
expected auth error codes (401/422), not 500. No database is required.
6+
"""
37

4-
def test_sample():
5-
assert core is not None
8+
import pytest
9+
from fastapi.testclient import TestClient
10+
11+
from rsptx.book_server_api.main import app
12+
13+
14+
@pytest.fixture(scope="module")
15+
def client():
16+
with TestClient(app, raise_server_exceptions=False) as c:
17+
yield c
18+
19+
20+
# ---------------------------------------------------------------------------
21+
# App-level
22+
# ---------------------------------------------------------------------------
23+
24+
def test_app_exists():
25+
assert app is not None
26+
27+
28+
def test_routes_registered():
29+
paths = {r.path for r in app.routes}
30+
assert any("/logger" in p for p in paths)
31+
assert any("/assessment" in p for p in paths)
32+
assert any("/books" in p for p in paths)
33+
assert any("/course" in p for p in paths)
34+
assert any("/auth" in p for p in paths)
35+
36+
37+
# ---------------------------------------------------------------------------
38+
# /logger — authenticated routes return 401/422
39+
# ---------------------------------------------------------------------------
40+
41+
def test_bookevent_unauthenticated(client):
42+
resp = client.post("/logger/bookevent", json={})
43+
assert resp.status_code in (401, 422)
44+
45+
46+
# ---------------------------------------------------------------------------
47+
# /assessment — authenticated routes return 401/422
48+
# ---------------------------------------------------------------------------
49+
50+
def test_assessment_results_unauthenticated(client):
51+
resp = client.post("/assessment/results", json={})
52+
assert resp.status_code in (401, 422)
53+
54+
55+
def test_assessment_gethist_unauthenticated(client):
56+
resp = client.post("/assessment/gethist", json={})
57+
assert resp.status_code in (401, 422)
58+
59+
60+
def test_assessment_get_latest_code_unauthenticated(client):
61+
resp = client.post("/assessment/get_latest_code", json={})
62+
assert resp.status_code in (401, 422)
63+
64+
65+
# ---------------------------------------------------------------------------
66+
# /course — authenticated routes return 401/422
67+
# ---------------------------------------------------------------------------
68+
69+
def test_course_index_unauthenticated(client):
70+
resp = client.get("/course/index")
71+
assert resp.status_code in (401, 422)
72+
73+
74+
# ---------------------------------------------------------------------------
75+
# /auth — public routes are reachable (not 500)
76+
# ---------------------------------------------------------------------------
77+
78+
def test_auth_login_reachable(client):
79+
resp = client.get("/auth/login")
80+
assert resp.status_code != 500
81+
82+
83+
def test_auth_course_students_unauthenticated(client):
84+
resp = client.get("/auth/course_students")
85+
assert resp.status_code in (401, 422)
86+
87+
88+
# ---------------------------------------------------------------------------
89+
# /books — public routes are reachable (not 500)
90+
# ---------------------------------------------------------------------------
91+
92+
def test_books_crashtest_returns_500(client):
93+
# /books/crashtest intentionally raises ZeroDivisionError to exercise the
94+
# error handler; 500 is the correct response here.
95+
resp = client.get("/books/crashtest")
96+
assert resp.status_code == 500

0 commit comments

Comments
 (0)