Coverage for tests/conftest.py: 98%
55 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
1"""Offline upstream fixtures for API smoke tests."""
3from datetime import datetime, timezone
4from unittest.mock import AsyncMock
6import httpx
7import pytest
9from data_api.data.manager import nthudata
10from data_api.data.nthudata import Snapshot
11from data_api.models.courses import CourseData
14def course_payload(**overrides):
15 data = {
16 "id": "11510TEST100000",
17 "chinese_title": "Test course",
18 "english_title": "Test Course",
19 "note": "",
20 "limit_note": "",
21 "object": "",
22 "ge_type": "",
23 "ge_category": "",
24 "no_extra_selection": "",
25 "semester_code": "11510",
26 "department_code": "TEST",
27 "course_number": "100000",
28 "department_name": None,
29 "college_name": None,
30 "course_level_code": None,
31 "course_level": None,
32 "remote_teaching": None,
33 "credits": 3,
34 "capacity": None,
35 "capacity_unlimited": True,
36 "enrolled_count": None,
37 "enrollment_after_selection": None,
38 "enrollment_after_withdrawal": None,
39 "reserved_for_freshmen": 0,
40 "is_cancelled": False,
41 "language_code": "zh",
42 "lecturers": [],
43 "meetings": [],
44 "expertises": [],
45 "programs": [],
46 "curriculum_requirements": [],
47 "prerequisite_text": "",
48 "parsing_issues": [],
49 }
50 return data | overrides
53def make_course(data=None, **overrides):
54 return CourseData.model_validate(course_payload(**((data or {}) | overrides)))
57def set_courses(monkeypatch, courses, version="course-test-version"):
58 from data_api.domain.courses.services import courses_service
60 snapshot = Snapshot([], courses, version, datetime.now(timezone.utc))
61 monkeypatch.setattr(courses_service, "get_snapshot", AsyncMock(return_value=snapshot))
62 return snapshot
65NEWSLETTER_NAMES = ["NTHU-Newsletter", "教務處綜合教務組電子報"]
68@pytest.fixture(autouse=True)
69def no_external_network(monkeypatch):
70 async def unavailable(self, request):
71 return httpx.Response(503, request=request)
73 monkeypatch.setattr(httpx.AsyncHTTPTransport, "handle_async_request", unavailable)
76@pytest.fixture
77def mock_upstream(monkeypatch, no_external_network):
78 def install(handler):
79 transport = httpx.MockTransport(handler)
81 async def handle_request(self, request):
82 return await transport.handle_async_request(request)
84 monkeypatch.setattr(httpx.AsyncHTTPTransport, "handle_async_request", handle_request)
86 return install
89@pytest.fixture
90def library_space_payload():
91 return {
92 "resmsg": "成功",
93 "rows": [
94 {
95 "spacetype": "1",
96 "spacetypename": "Study room",
97 "zoneid": "main",
98 "zonename": "Main library",
99 "count": "12",
100 }
101 ],
102 }
105@pytest.fixture
106def lost_items_html():
107 def make(count=12):
108 header = "<tr><td>序號</td><td>拾獲時間</td><td>拾獲地點</td><td>描述</td></tr>"
109 rows = "".join(
110 f"<tr><td>{index}</td><td>2026-09-30</td><td>Main library</td>"
111 "<td> Book\n title </td></tr>"
112 for index in range(count)
113 )
114 return f"<table>{header}{rows}</table>"
116 return make
119@pytest.fixture
120def lost_items_empty_page():
121 return '<div id="content"><h1>失物招領系統 Lost and Found System</h1>' "<br>目前無資料 !!</div>"
124@pytest.fixture
125async def dataset_runtime():
126 data = {
127 "/courses/latest.json": [course_payload()],
128 "/courses/sources.json": {"semesters": ["11510"]},
129 "/buses.json": {},
130 "/calendars.json": [],
131 "/libraries/calendars.json": [],
132 "/announcements.json": [],
133 "/announcements_list.json": [],
134 "/dining.json": [],
135 "/directory.json": [],
136 "/newsletters.json": [
137 {"name": name, "link": "https://example.com", "details": {}, "articles": []}
138 for name in NEWSLETTER_NAMES
139 ],
140 "/maps.json": {
141 "main": {
142 name: {"latitude": "24.79", "longitude": "120.99"}
143 for name in ["校門", "綜合", "台積", "台達"]
144 }
145 },
146 }
148 def handler(request):
149 if request.url.path == "/file_details.json":
150 return httpx.Response(
151 200,
152 json={
153 "file_details": {
154 "/": [{"name": path.lstrip("/"), "version": "fixture"} for path in data]
155 }
156 },
157 )
158 if request.url.path in data:
159 return httpx.Response(200, json=data[request.url.path])
160 return httpx.Response(404)
162 async with nthudata.lifespan(httpx.AsyncClient(transport=httpx.MockTransport(handler))):
163 yield nthudata