Skip to content

Commit 6e3b1ad

Browse files
committed
test: add new asynchronous tests for course, meme, notification, and statistics functionalities, including error handling for not found scenarios and direct CRUD operations
1 parent 7038511 commit 6e3b1ad

4 files changed

Lines changed: 405 additions & 10 deletions

File tree

backend/tests/api/test_courses.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,36 @@ def fake_presigned(object_name: str, *, expires):
678678
await session.commit()
679679

680680

681+
@pytest.mark.asyncio
682+
async def test_get_archive_preview_url_not_found(
683+
client: AsyncClient,
684+
make_user,
685+
):
686+
user = await make_user()
687+
688+
app.dependency_overrides[get_current_user] = _override_user(user)
689+
try:
690+
response = await client.get("/courses/999/archives/1/preview")
691+
assert response.status_code == 404
692+
finally:
693+
app.dependency_overrides.pop(get_current_user, None)
694+
695+
696+
@pytest.mark.asyncio
697+
async def test_get_archive_download_url_not_found(
698+
client: AsyncClient,
699+
make_user,
700+
):
701+
user = await make_user()
702+
703+
app.dependency_overrides[get_current_user] = _override_user(user)
704+
try:
705+
response = await client.get("/courses/123/archives/456/download")
706+
assert response.status_code == 404
707+
finally:
708+
app.dependency_overrides.pop(get_current_user, None)
709+
710+
681711
@pytest.mark.asyncio
682712
async def test_update_archive_direct_sets_fields(
683713
session_maker,
@@ -990,6 +1020,46 @@ async def test_delete_course_not_found_direct(session_maker, make_user):
9901020
assert exc.value.status_code == 404
9911021

9921022

1023+
@pytest.mark.asyncio
1024+
async def test_admin_delete_course_soft_deletes_archives(
1025+
client: AsyncClient,
1026+
session_maker,
1027+
make_user,
1028+
):
1029+
admin = await make_user(is_admin=True)
1030+
course = await _create_course(session_maker)
1031+
archive = await _create_archive(
1032+
session_maker,
1033+
course_id=course.id,
1034+
uploader_id=admin.id,
1035+
)
1036+
1037+
app.dependency_overrides[get_current_user] = _override_user(admin)
1038+
try:
1039+
response = await client.delete(
1040+
f"/courses/admin/courses/{course.id}"
1041+
)
1042+
assert response.status_code == 200
1043+
body = response.json()
1044+
assert "1 associated archives" in body["message"]
1045+
1046+
async with session_maker() as session:
1047+
refreshed_course = await session.get(Course, course.id)
1048+
refreshed_archive = await session.get(Archive, archive.id)
1049+
assert refreshed_course.deleted_at is not None
1050+
assert refreshed_archive.deleted_at is not None
1051+
finally:
1052+
app.dependency_overrides.pop(get_current_user, None)
1053+
async with session_maker() as session:
1054+
await session.execute(
1055+
delete(Archive).where(Archive.id == archive.id)
1056+
)
1057+
await session.execute(
1058+
delete(Course).where(Course.id == course.id)
1059+
)
1060+
await session.commit()
1061+
1062+
9931063
@pytest.mark.asyncio
9941064
async def test_list_all_courses_direct_returns_courses(
9951065
session_maker,
@@ -1009,3 +1079,129 @@ async def test_list_all_courses_direct_returns_courses(
10091079
async with session_maker() as session:
10101080
await session.execute(delete(Course).where(Course.id == course.id))
10111081
await session.commit()
1082+
1083+
1084+
@pytest.mark.asyncio
1085+
async def test_get_course_archives_not_found_direct(session_maker, make_user):
1086+
user = await make_user()
1087+
async with session_maker() as session:
1088+
with pytest.raises(HTTPException) as exc:
1089+
await get_course_archives(
1090+
course_id=999999,
1091+
current_user=UserRoles(user_id=user.id, is_admin=False),
1092+
db=session,
1093+
)
1094+
assert exc.value.status_code == 404
1095+
1096+
1097+
@pytest.mark.asyncio
1098+
async def test_get_archive_preview_url_not_found_direct(session_maker, make_user):
1099+
user = await make_user()
1100+
async with session_maker() as session:
1101+
with pytest.raises(HTTPException) as exc:
1102+
await get_archive_preview_url(
1103+
course_id=1,
1104+
archive_id=2,
1105+
current_user=UserRoles(user_id=user.id, is_admin=False),
1106+
db=session,
1107+
)
1108+
assert exc.value.status_code == 404
1109+
1110+
1111+
@pytest.mark.asyncio
1112+
async def test_get_archive_download_url_not_found_direct(session_maker, make_user):
1113+
user = await make_user()
1114+
async with session_maker() as session:
1115+
with pytest.raises(HTTPException) as exc:
1116+
await get_archive_download_url(
1117+
course_id=1,
1118+
archive_id=2,
1119+
current_user=UserRoles(user_id=user.id, is_admin=False),
1120+
db=session,
1121+
)
1122+
assert exc.value.status_code == 404
1123+
1124+
1125+
@pytest.mark.asyncio
1126+
async def test_update_archive_course_creates_new_course_direct(session_maker, make_user):
1127+
admin = await make_user(is_admin=True)
1128+
course = await _create_course(session_maker, name="Direct Course")
1129+
archive = await _create_archive(
1130+
session_maker,
1131+
course_id=course.id,
1132+
uploader_id=admin.id,
1133+
)
1134+
1135+
async with session_maker() as session:
1136+
response = await update_archive_course(
1137+
course_id=course.id,
1138+
archive_id=archive.id,
1139+
course_update=ArchiveUpdateCourse(
1140+
course_name="Brand New Course",
1141+
course_category=CourseCategory.GRADUATE,
1142+
),
1143+
current_user=UserRoles(user_id=admin.id, is_admin=True),
1144+
db=session,
1145+
)
1146+
new_course_id = response["new_course_id"]
1147+
new_course = await session.get(Course, new_course_id)
1148+
assert new_course.name == "Brand New Course"
1149+
1150+
await session.execute(delete(Archive).where(Archive.id == archive.id))
1151+
await session.execute(delete(Course).where(Course.id == course.id))
1152+
await session.execute(delete(Course).where(Course.id == new_course_id))
1153+
await session.commit()
1154+
1155+
1156+
@pytest.mark.asyncio
1157+
async def test_create_course_duplicate_direct(session_maker, make_user):
1158+
admin = await make_user(is_admin=True)
1159+
course = await _create_course(
1160+
session_maker,
1161+
name="Duplicate Direct",
1162+
category=CourseCategory.GENERAL,
1163+
)
1164+
1165+
async with session_maker() as session:
1166+
with pytest.raises(HTTPException) as exc:
1167+
await create_course(
1168+
course_data=CourseCreate(
1169+
name="Duplicate Direct",
1170+
category=CourseCategory.GENERAL,
1171+
),
1172+
current_user=UserRoles(user_id=admin.id, is_admin=True),
1173+
db=session,
1174+
)
1175+
assert exc.value.status_code == 400
1176+
1177+
async with session_maker() as session:
1178+
await session.execute(delete(Course).where(Course.id == course.id))
1179+
await session.commit()
1180+
1181+
1182+
@pytest.mark.asyncio
1183+
async def test_delete_course_soft_deletes_direct(session_maker, make_user):
1184+
admin = await make_user(is_admin=True)
1185+
course = await _create_course(session_maker, name="Delete Direct")
1186+
archive = await _create_archive(
1187+
session_maker,
1188+
course_id=course.id,
1189+
uploader_id=admin.id,
1190+
)
1191+
1192+
async with session_maker() as session:
1193+
result = await delete_course(
1194+
course_id=course.id,
1195+
current_user=UserRoles(user_id=admin.id, is_admin=True),
1196+
db=session,
1197+
)
1198+
assert "associated archives" in result["message"]
1199+
1200+
refreshed_course = await session.get(Course, course.id)
1201+
refreshed_archive = await session.get(Archive, archive.id)
1202+
assert refreshed_course.deleted_at is not None
1203+
assert refreshed_archive.deleted_at is not None
1204+
1205+
await session.execute(delete(Archive).where(Archive.id == archive.id))
1206+
await session.execute(delete(Course).where(Course.id == course.id))
1207+
await session.commit()

backend/tests/api/test_meme.py

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
11
import pytest
2+
from fastapi import HTTPException
23
from sqlalchemy import delete, select
34

5+
from app.api.services.meme import get_random_meme
6+
47
from app.models.models import Meme
58

69

710
@pytest.mark.asyncio
8-
async def test_get_random_meme_returns_meme(client):
9-
response = await client.get("/meme")
10-
assert response.status_code == 200
11+
async def test_get_random_meme_returns_meme(client, session_maker):
12+
created_id = None
13+
async with session_maker() as session:
14+
existing = await session.execute(select(Meme))
15+
meme = existing.scalars().first()
16+
if meme is None:
17+
meme = Meme(content="Seed", language="en")
18+
session.add(meme)
19+
await session.commit()
20+
await session.refresh(meme)
21+
created_id = meme.id
1122

12-
payload = response.json()
13-
assert payload["content"]
14-
assert payload["language"]
23+
try:
24+
response = await client.get("/meme")
25+
assert response.status_code == 200
26+
27+
payload = response.json()
28+
assert payload["content"]
29+
assert payload["language"]
30+
finally:
31+
if created_id is not None:
32+
async with session_maker() as session:
33+
await session.execute(delete(Meme).where(Meme.id == created_id))
34+
await session.commit()
1535

1636

1737
@pytest.mark.asyncio
@@ -38,3 +58,31 @@ async def test_get_random_meme_returns_404_when_empty(client, session_maker):
3858
for item in snapshot
3959
)
4060
await session.commit()
61+
62+
63+
@pytest.mark.asyncio
64+
async def test_get_random_meme_direct_returns_entry(session_maker):
65+
async with session_maker() as session:
66+
await session.execute(delete(Meme))
67+
await session.commit()
68+
69+
meme = Meme(content="Hi", language="en")
70+
session.add(meme)
71+
await session.commit()
72+
await session.refresh(meme)
73+
74+
result = await get_random_meme(db=session)
75+
assert result.id == meme.id
76+
77+
await session.execute(delete(Meme).where(Meme.id == meme.id))
78+
await session.commit()
79+
80+
81+
@pytest.mark.asyncio
82+
async def test_get_random_meme_direct_raises_when_empty(session_maker):
83+
async with session_maker() as session:
84+
await session.execute(delete(Meme))
85+
await session.commit()
86+
87+
with pytest.raises(HTTPException):
88+
await get_random_meme(db=session)

0 commit comments

Comments
 (0)