fix: 修正 combined_app 未執行啟動流程導致課程資料為空 - #257
Conversation
combined_app 只複製 fast_api_app 的路由,lifespan 卻直接指定為
mcp_app.lifespan,導致預抓資料與 service 初始化從未執行。其他 domain
在請求當下才取資料所以不受影響,但 courses_service 僅在啟動時載入,
使 /courses/、/courses/search、/courses/lists/{list_name} 恆回傳空陣列。
- 將 MCP 與應用程式兩個 lifespan 串接為 combined_lifespan
- 啟動報告改用 ASCII 標記,避免非 UTF-8 主控台的 UnicodeEncodeError
- 新增 tests/test_app_lifespan.py 涵蓋此回歸
此問題自 6dab59f (NTHU-SA#227) 引入。既有測試以 ASGITransport 呼叫 app,
預設不會執行 lifespan,且僅斷言狀態碼 200,因此空陣列無法被偵測。
There was a problem hiding this comment.
Pull request overview
This PR fixes the exported FastAPI app’s startup behavior by ensuring the combined app (MCP routes + API routes) actually executes the application startup lifespan that prefetches data and initializes stateful services (notably courses_service, which only loads at startup). It also adjusts startup status output to be ASCII-only to avoid console encoding errors.
Changes:
- Chain MCP lifespan and the existing application
lifespan()via a newcombined_lifespan, and attach it tocombined_app. - Replace Unicode status marks (
✓/✗) with ASCII markers ([OK]/[FAIL]) in startup reporting. - Add regression tests covering combined lifespan wiring and ASCII startup output.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/data_api/api/api.py |
Introduces combined_lifespan and attaches it to the served combined_app; updates startup status output to ASCII markers. |
tests/test_app_lifespan.py |
Adds tests to guard against lifespan wiring regressions and validate ASCII-only startup reporting. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
test_courses_endpoint_returns_data_after_startup 原本會執行真實的 prefetch 與 update_data,使測試同時依賴上游資料源;失敗時無法分辨是 lifespan 接線錯誤,還是 data.nthusa.tw 當下不可用。 改為 stub nthudata.get 與 MCP lifespan,讓真正的 update_data 對固定的 記憶體資料執行。app.router.lifespan_context 予以保留,因為那才是驗證 「實際被啟動的 app 是否掛上應用程式 lifespan」的部分,回歸偵測能力不變: 將 lifespan 改回 mcp_app.lifespan 後,測試仍以 assert [] 失敗。 一併解除 MCP session manager 每個 process 僅能啟動一次的限制, 後續在此檔案新增測試不需再繞開。執行時間 15.4s 降至 5.0s。
|
|
Hi @ChiuKuanHsun , |



combined_app只複製了fast_api_app的路由,lifespan卻直接指定為mcp_app.lifespan,導致create_app()內宣告的啟動流程從未執行。其他 domain 都在請求當下才取資料所以不受影響,但courses_service僅在啟動時載入,因此課程相關端點在線上恆回傳空陣列。Fixes
combined_app未執行應用程式 lifespan,導致/courses/、/courses/search、/courses/lists/{list_name}恆回傳空陣列(自 feat: add MCP interface, optimize OpenAPI spec and performance #227 引入)✓/✗改為 ASCII 標記,避免在非 UTF-8 主控台(如 Windows cp950)拋出UnicodeEncodeErrorNotes
GET https://api.nthusa.tw/courses/回傳[]。本機執行 app 的 lifespan 後courses_service.course_data長度為 0,且啟動時的Starting application.../ pre-fetch 訊息完全沒有輸出。ASGITransport呼叫 app,預設不會執行 lifespan;且課程相關測試僅斷言status_code == 200,而空陣列同樣是 200。lifespan改回mcp_app.lifespan後,新增的test_courses_endpoint_returns_data_after_startup會以assert []失敗;套用修正後通過。black與isort --profile black皆通過。