Compare commits
107 Commits
main
..
04be62e0da
| Author | SHA1 | Date | |
|---|---|---|---|
|
04be62e0da
|
|||
|
9931efdb05
|
|||
|
53095c7c5b
|
|||
|
1fff80888a
|
|||
|
ce4d8d0ca6
|
|||
|
79c1eff071
|
|||
|
60e5d400ac
|
|||
|
e93948db5f
|
|||
|
4c8f1d92c6
|
|||
|
987574f3cb
|
|||
|
32d0aa2b07
|
|||
|
9838519113
|
|||
|
79107582f6
|
|||
|
d55675216a
|
|||
|
7a5d079fc6
|
|||
|
dfe76ad1ee
|
|||
|
064e409680
|
|||
|
8aa935f6be
|
|||
|
c79f4bf29b
|
|||
|
102311d110
|
|||
|
fc866ac366
|
|||
|
650f87ec75
|
|||
|
8c408ffa9d
|
|||
|
7c2232e27c
|
|||
|
b255ccba32
|
|||
|
d23ac3534d
|
|||
|
9deca3b7ff
|
|||
|
a9bbbf4a91
|
|||
|
47082ddb12
|
|||
|
5f563be93c
|
|||
|
cafcf0158d
|
|||
|
db3f69e0b0
|
|||
|
2e4de5cc31
|
|||
|
1031af4e3b
|
|||
|
3b3479a7da
|
|||
|
2599f4f95a
|
|||
|
4a469abb96
|
|||
|
afbfabe0d4
|
|||
|
2b4ef8b7e7
|
|||
|
d48a211e3f
|
|||
|
d268ced579
|
|||
|
b577b4d62e
|
|||
|
de87136054
|
|||
|
d5d61ffc62
|
|||
|
3d7e46c9f7
|
|||
|
5b2b602d85
|
|||
|
4b492db033
|
|||
|
eefc2915a4
|
|||
|
2c0a051c10
|
|||
|
845b4dc3a0
|
|||
|
10959773d7
|
|||
|
885c7dde2a
|
|||
|
dfdae97d86
|
|||
|
f1c2c1f199
|
|||
|
bd85c951ec
|
|||
|
68565dc068
|
|||
|
300129bd62
|
|||
|
3b3a329e3d
|
|||
|
cb0b04c428
|
|||
|
fc9192937d
|
|||
|
92399264de
|
|||
|
1dd910bdde
|
|||
|
5826a52f7b
|
|||
|
d3e71e6f77
|
|||
|
ee914a4855
|
|||
|
2b69dc473c
|
|||
|
4cb4acbf53
|
|||
|
ffa5b97dad
|
|||
|
55c053ac4d
|
|||
|
78810a9ddb
|
|||
|
ddb20e24b9
|
|||
|
a5e325d76d
|
|||
|
1a6278211d
|
|||
|
82b993b33f
|
|||
|
1069a2ed41
|
|||
|
81ec45ff9b
|
|||
|
19849c938e
|
|||
|
b56e2259a2
|
|||
|
a8e2012878
|
|||
|
5e46d797c9
|
|||
|
dcc33990f5
|
|||
|
09a010415d
|
|||
|
e6b5044256
|
|||
|
1aba5719a8
|
|||
|
6a67326701
|
|||
|
699c937ccd
|
|||
|
2abcf8e4f6
|
|||
|
9a5ef95bab
|
|||
|
06a8e99755
|
|||
|
a186ca71e0
|
|||
|
0ec36469f9
|
|||
|
8077ec3d1c
|
|||
|
962959291c
|
|||
|
fc6a4e96f5
|
|||
|
beee7c6dd7
|
|||
|
c24a9d5116
|
|||
|
ba7aaedc10
|
|||
|
8d4b8784d4
|
|||
|
98d4ca5e0c
|
|||
|
dc382590a1
|
|||
|
e8f0e14b66
|
|||
|
822f120a46
|
|||
|
e85a7f1f4b
|
|||
|
4d725f88d1
|
|||
|
e1fc5cd650
|
|||
|
f12aea79c4
|
|||
|
06c47695f0
|
@@ -35,8 +35,6 @@ jobs:
|
||||
run: mypy src/
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
DATABASE_URL: "sqlite+aiosqlite:///:memory:"
|
||||
run: pytest --cov=src --cov-report=term-missing --cov-fail-under=80
|
||||
|
||||
frontend:
|
||||
@@ -63,7 +61,7 @@ jobs:
|
||||
run: npm run lint
|
||||
|
||||
- name: Type check
|
||||
run: npx tsc --noEmit
|
||||
run: tsc --noEmit
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
@@ -88,6 +86,8 @@ jobs:
|
||||
context: ./backend
|
||||
push: false
|
||||
tags: codetutor-backend:test
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Build frontend image
|
||||
uses: docker/build-push-action@v5
|
||||
@@ -95,3 +95,5 @@ jobs:
|
||||
context: ./frontend
|
||||
push: false
|
||||
tags: codetutor-frontend:test
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -177,92 +177,3 @@ async def test_get_stats(client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
assert data["by_difficulty"]["easy"] == 1
|
||||
assert len(data["by_category"]) == 1
|
||||
assert len(data["by_pattern"]) == 1
|
||||
|
||||
|
||||
async def test_get_pattern_tutorial(client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
await create_sample_data(db_session)
|
||||
response = await client.get("/api/patterns/two-pointers/tutorial")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["name"] == "Two Pointers"
|
||||
assert data["learning_progression"]["warmup"][0]["title"] == "Two Sum"
|
||||
assert len(data["learning_progression"]["core"]) == 0
|
||||
|
||||
|
||||
async def test_get_pattern_tutorial_not_found(client: AsyncClient) -> None:
|
||||
response = await client.get("/api/patterns/nonexistent/tutorial")
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
async def test_submit_passing(client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
data = await create_sample_data(db_session)
|
||||
q = data["question"]
|
||||
q.test_cases = {
|
||||
"visible": [{"input": {"nums": [2, 7], "target": 9}, "expected": [0, 1]}],
|
||||
"hidden": [{"input": {"nums": [3, 2, 4], "target": 6}, "expected": [1, 2]}],
|
||||
}
|
||||
await db_session.commit()
|
||||
|
||||
response = await client.post(
|
||||
"/api/questions/two-sum/submit",
|
||||
json={
|
||||
"code": "def solve(nums, t): pass",
|
||||
"hidden_outputs": [{"test_id": 1, "output": [1, 2]}],
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["passed"] is True
|
||||
assert result["total_passed"] == 1
|
||||
|
||||
|
||||
async def test_submit_wrong_output(client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
data = await create_sample_data(db_session)
|
||||
data["question"].test_cases = {
|
||||
"visible": [],
|
||||
"hidden": [{"input": {"nums": [1, 2], "target": 3}, "expected": [0, 1]}],
|
||||
}
|
||||
await db_session.commit()
|
||||
|
||||
response = await client.post(
|
||||
"/api/questions/two-sum/submit",
|
||||
json={
|
||||
"code": "def solve(nums, t): pass",
|
||||
"hidden_outputs": [{"test_id": 0, "output": [9, 9]}],
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["passed"] is False
|
||||
|
||||
|
||||
async def test_submit_missing_output(client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
data = await create_sample_data(db_session)
|
||||
data["question"].test_cases = {
|
||||
"visible": [],
|
||||
"hidden": [{"input": {"nums": [1, 2], "target": 3}, "expected": [0, 1]}],
|
||||
}
|
||||
await db_session.commit()
|
||||
|
||||
response = await client.post(
|
||||
"/api/questions/two-sum/submit",
|
||||
json={"code": "def solve(nums, t): pass", "hidden_outputs": []},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["hidden_results"][0]["error"] == "No output provided"
|
||||
|
||||
|
||||
async def test_submit_no_test_cases(client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
await create_sample_data(db_session)
|
||||
response = await client.post(
|
||||
"/api/questions/two-sum/submit",
|
||||
json={"code": "pass", "hidden_outputs": []},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
async def test_submit_not_found(client: AsyncClient) -> None:
|
||||
response = await client.post(
|
||||
"/api/questions/nonexistent/submit",
|
||||
json={"code": "pass", "hidden_outputs": []},
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
@@ -215,55 +215,6 @@ class TestPatternService:
|
||||
|
||||
assert result is None
|
||||
|
||||
async def test_get_tutorial(self, db_session: AsyncSession) -> None:
|
||||
related = Pattern(name="BFS", slug="bfs", description="Breadth-first search")
|
||||
pattern = Pattern(
|
||||
name="Two Pointers",
|
||||
slug="two-pointers",
|
||||
description="Two pointer technique",
|
||||
common_mistakes=[
|
||||
{"title": "Off by one", "description": "Wrong bounds", "fix": "Use <="}
|
||||
],
|
||||
variations=[{"name": "Fast/slow", "description": "Different speeds"}],
|
||||
related_patterns=["bfs"],
|
||||
visualization_examples=[{"id": "ex1", "title": "Basic", "code": "x=1", "steps": []}],
|
||||
)
|
||||
db_session.add_all([related, pattern])
|
||||
await db_session.flush()
|
||||
|
||||
q1 = Question(
|
||||
title="Easy Q", slug="easy-q", difficulty=Difficulty.EASY, description="Easy."
|
||||
)
|
||||
q2 = Question(title="Med Q", slug="med-q", difficulty=Difficulty.MEDIUM, description="Med.")
|
||||
q3 = Question(
|
||||
title="Hard Q", slug="hard-q", difficulty=Difficulty.HARD, description="Hard."
|
||||
)
|
||||
q1.patterns = [pattern]
|
||||
q2.patterns = [pattern]
|
||||
q3.patterns = [pattern]
|
||||
db_session.add_all([q1, q2, q3])
|
||||
await db_session.commit()
|
||||
|
||||
service = PatternService(db_session)
|
||||
result = await service.get_pattern_tutorial("two-pointers")
|
||||
|
||||
assert result is not None
|
||||
assert result.name == "Two Pointers"
|
||||
assert len(result.common_mistakes) == 1
|
||||
assert len(result.variations) == 1
|
||||
assert len(result.related_patterns) == 1
|
||||
assert result.related_patterns[0].slug == "bfs"
|
||||
assert len(result.learning_progression.warmup) == 1
|
||||
assert len(result.learning_progression.core) == 1
|
||||
assert len(result.learning_progression.challenge) == 1
|
||||
assert len(result.visualization_examples) == 1
|
||||
|
||||
async def test_get_tutorial_not_found(self, db_session: AsyncSession) -> None:
|
||||
service = PatternService(db_session)
|
||||
result = await service.get_pattern_tutorial("nonexistent")
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
class TestStatsService:
|
||||
async def test_get_stats(self, db_session: AsyncSession) -> None:
|
||||
|
||||
Reference in New Issue
Block a user