Skip to content

test(api): add route integration tests for low-coverage endpoints (#1032)#1040

Merged
otomatty merged 5 commits into
developfrom
cursor/route-integration-tests-1032-d7f1
Jun 11, 2026
Merged

test(api): add route integration tests for low-coverage endpoints (#1032)#1040
otomatty merged 5 commits into
developfrom
cursor/route-integration-tests-1032-d7f1

Conversation

@otomatty

@otomatty otomatty commented Jun 10, 2026

Copy link
Copy Markdown
Owner

概要

Issue #1032 の受け入れ条件に沿い、カバレッジが低かった API ルート層へ統合テストを追加・拡充した。既存の Hono + authRequired モック + createMockDb パターンを踏襲している。

変更点

  • ingest.test.ts: /plan/graph/run/graph/resume/apply の認証・バリデーション・成功系・エラー変換を追加
  • clip.test.ts: /youtube エンドポイントのテストを追加(認証・バリデーション・成功・502)
  • composeSessions.test.ts: GET projection・POST run (SSE)・PATCH resume 成功系・認可テストを追加
  • composeSessionProjection.test.ts: loadComposeSessionProjection と追加 interrupt 分岐のテスト
  • notes/tags.test.ts: GET /api/notes/:noteId/tags を新規追加(認可・ETag/304)
  • thumbnail/serve.test.ts: GET /api/thumbnail/serve/:id のストリーム配信テストを追加
  • createMockDb.ts: db.execute チェーン記録を追加(ingest / tags 向け)

変更の種類

  • 🧪 テスト (Tests)

テスト方法

cd server/api && bunx vitest run

対象ファイルのみ:

cd server/api && bunx vitest run \
  src/__tests__/routes/ingest.test.ts \
  src/__tests__/routes/clip.test.ts \
  src/__tests__/routes/composeSessions.test.ts \
  src/__tests__/routes/composeSessionProjection.test.ts \
  src/__tests__/routes/notes/tags.test.ts \
  src/__tests__/routes/thumbnail/serve.test.ts

チェックリスト

  • テストがすべてパスする(cd server/api && bunx vitest run — 1488 tests)
  • Lint エラーがない
  • 必要に応じてドキュメントを更新した(テストのみのため不要)
  • コミットメッセージが Conventional Commits に従っている

カバレッジ(対象ルートのラインカバレッジ)

ファイル 変更前(概算) 変更後
ingest.ts 13% 80%+
clip.ts 30% 93%
composeSessions.ts 50% 83%
composeSessionProjection.ts 56% 91%
notes/tags.ts 8% 100%
thumbnail/serve.ts 41% 100%

関連 Issue

Closes #1032

Open in Web Open in Cursor 

Summary by CodeRabbit

  • Tests
    • Expanded and hardened auth/authorization tests across many API endpoints; added comprehensive tags retrieval and thumbnail serving suites
    • Improved test infrastructure and mocks (hoisted/resettable mocks, richer DB/mock execution helpers, chain-aware DB mock behavior), and broadened coverage for ingest, compose-session, clip, and session flows
  • Bug Fixes
    • Clarified PDF reader cleanup behavior to avoid unmount/resource issues

)

Expand or add Hono route integration tests for ingest, clip, compose
sessions, compose session projection, notes/tags, and thumbnail serve.
Each endpoint covers auth, authorization, validation, success paths, and
error handling via mocked services and createMockDb.

Closes #1032
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 84f20fae-c671-4af1-8175-de2add9149d9

📥 Commits

Reviewing files that changed from the base of the PR and between f7222f2 and 8f21ee7.

📒 Files selected for processing (2)
  • server/api/src/__tests__/services/userAiCredentialService.test.ts
  • src/components/pdfReader/usePdfDocument.ts
✅ Files skipped from review due to trivial changes (2)
  • server/api/src/tests/services/userAiCredentialService.test.ts
  • src/components/pdfReader/usePdfDocument.ts

📝 Walkthrough

Walkthrough

Adds and restructures many server/api tests to a Hono test harness with hoisted Vitest mocks, extends the mock DB proxy to support execute-chaining, and updates a PDF hook cleanup call to use pdfDoc.loadingTask.destroy().

Changes

Route Integration Tests for Low-Coverage Endpoints

Layer / File(s) Summary
Mock DB chain execution support
server/api/src/__tests__/createMockDb.ts
Adds "execute" property handler to the mock DB proxy for chainable DB operation simulation.
Clip route tests with auth/config/usage verification
server/api/src/__tests__/routes/clip.test.ts
Refactors clip tests to a Hono app harness with authRequired/rateLimit mocks, hoisted spies for YouTube extraction and AI config, standardized authHeaders(), and updated fetch/youtube endpoint assertions and error mappings.
Compose session and projection tests
server/api/src/__tests__/routes/composeSessions.test.ts, server/api/src/__tests__/routes/composeSessionProjection.test.ts
Adds hoisted mocks for compose-session projection and GraphRunner, a sessionRow helper, and tests covering auth/authorization, projection loading/null conditions, SSE streaming/interrupts, and run/resume flows.
Ingest route tests with comprehensive harness refactor
server/api/src/__tests__/routes/ingest.test.ts
Rewrites ingest tests with hoisted mocks for usage/cost/extraction/planner/key lookup/activity/graph/checkpoint dependencies, createIngestApp, and extensive plan/graph/apply validation, error mapping, and success-path assertions.
Tags endpoint tests with auth and conditional requests
server/api/src/__tests__/routes/notes/tags.test.ts
New suite for GET /api/notes/:noteId/tags with mocked auth middleware (required/optional), owner aggregation assertions (ETag/Cache-Control), guest access, 404/403 negative cases, and 304 conditional-request handling.
Thumbnail serve tests with refactored app helper
server/api/src/__tests__/routes/thumbnail/serve.test.ts
Adds createServeApp(dbResults) helper returning { app, chains }, reusable THUMB_ROW fixture, and GET contract tests for auth, ownership-not-found, streaming with headers, and S3 error mappings.
PDF hook resource cleanup
src/components/pdfReader/usePdfDocument.ts
Updates inline docs to call pdfDoc.loadingTask.destroy() on unmount and documents pdf.js v6 change.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HonoApp
  participant GraphRunner
  participant Checkpointer
  Client->>HonoApp: GET /api/pages/:pageId/compose-sessions/:id
  HonoApp->>GraphRunner: streamEvents / resume
  GraphRunner->>Checkpointer: getState / request checkpoint
  Checkpointer-->>HonoApp: checkpoint state / projection
  HonoApp-->>Client: JSON response (projection / status)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • otomatty/zedi#643: Related changes around createMockDb chain handling used by tests that inspect captured execute() chains.
  • otomatty/zedi#602: Related clip endpoint tests exercising YouTube extraction and AI-summary usage recording.
  • otomatty/zedi#756: Prior work touching createMockDb-driven DB mock chaining used by route and service tests.

Possibly related issues

Poem

🐰 With whiskers twitching, tests hop in line,
Mock chains execute and spies align,
Hono routes guarded, headers snug and neat,
ETags, streams, and fixtures all greet,
A rabbit cheers for green CI feet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly summarizes the main change: adding route integration tests for low-coverage endpoints in the API layer.
Linked Issues check ✅ Passed PR implements all five required viewpoints (auth 401, authorization 403/404, validation 400, successful response shape with service call verification, error handler conversion) across all target files with ≥80% line coverage and passing test suite.
Out of Scope Changes check ✅ Passed Minor documentation update in usePdfDocument.ts and import statement cleanup in userAiCredentialService.test.ts are tangential to the core objective but do not conflict with it.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/route-integration-tests-1032-d7f1

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly expands test coverage across several API routes, including clip extraction, compose sessions, ingestion planning, note-wide tag aggregation, and thumbnail serving. The feedback highlights a potential logical issue in a compose session test case where a 403 Forbidden status is expected, even though the mock setup indicates the requesting user is the owner of the resource.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +322 to +333
it("returns 403 when caller cannot view the page", async () => {
const { app } = createComposeApp([
[{ id: PAGE_ID, ownerId: OTHER_USER_ID, noteId: NOTE_ID }],
[{ email: "other@example.com" }],
[mockNote({ ownerId: OTHER_USER_ID, visibility: "private" })],
[],
[],
]);
const res = await app.request(`/api/pages/${PAGE_ID}/compose-sessions/sess-2`, {
headers: authHeaders(OTHER_USER_ID),
});
expect(res.status).toBe(403);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

このテストケースの意図が少し不明確です。セットアップでは、ページとノートの両方が OTHER_USER_ID によって所有されており、リクエストも OTHER_USER_ID から送信されています。この場合、ユーザーは所有者であるため、assertPageViewAccess によるアクセスチェックは成功するはずです。したがって、期待されるステータスコードは 403 Forbidden ではなく、後続の処理でセッションが見つからないことによる 404 Not Found になるべきではないでしょうか。

もし 403 が意図した結果であるなら、アクセスが拒否される具体的な理由を明確にするためのコメントをテストに追加すると、可読性が向上すると思われます。

@otomatty otomatty marked this pull request as ready for review June 11, 2026 00:04
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Add missing GraphContext.feature field and ComposeContentLocale literal.
Fix compose-sessions 403 test to use a non-member caller on a private note
(per Gemini review).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/api/src/__tests__/routes/ingest.test.ts`:
- Around line 366-478: The tests for POST /api/ingest/graph/run and
/api/ingest/graph/resume are missing coverage for cross-user access denial on
stateful resources; add tests that simulate a thread/session owned by another
user and assert the API returns the expected 403/404. Specifically, in the "run"
and "resume" suites write cases using createIngestApp and authHeaders for a
different user context (or mock the ownership lookup) to call endpoints with a
threadId owned by TEST_USER_ID but authenticated as another user, and assert the
response is 403/404 and that mocks like mockGraphRunnerInvoke,
mockGraphRunnerResume, and mockResolveCheckpointerForRun are not invoked when
access is denied; reuse existing helpers (createIngestApp, authHeaders) and
existing mocks (mockGraphRunnerInvoke, mockGraphRunnerResume,
mockResolveCheckpointerForRun, mockAssertComposeBackendReady) to implement these
assertions.

In `@server/api/src/__tests__/routes/thumbnail/serve.test.ts`:
- Around line 166-173: Update the "returns 404 when thumbnail row is not found
for caller" test to instead seed an existing thumbnail row owned by TEST_USER_ID
and issue the request as ATTACKER_ID so we exercise the "row exists but belongs
to another user" authz path; use createServeApp with a dataset containing a
thumbnail object keyed by OBJECT_ID and ownerId set to TEST_USER_ID, call
app.request(`/api/thumbnail/serve/${OBJECT_ID}`) with header "x-test-user-id":
ATTACKER_ID, and assert res.status is 404 and mockS3Send was not called (keep
test name and assertions but change the seeded row to belong to TEST_USER_ID).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ebf83bd-3918-41f0-8133-77c48e4ae8f6

📥 Commits

Reviewing files that changed from the base of the PR and between 1478bcd and 9887e6d.

📒 Files selected for processing (7)
  • server/api/src/__tests__/createMockDb.ts
  • server/api/src/__tests__/routes/clip.test.ts
  • server/api/src/__tests__/routes/composeSessionProjection.test.ts
  • server/api/src/__tests__/routes/composeSessions.test.ts
  • server/api/src/__tests__/routes/ingest.test.ts
  • server/api/src/__tests__/routes/notes/tags.test.ts
  • server/api/src/__tests__/routes/thumbnail/serve.test.ts

Comment thread server/api/src/__tests__/routes/ingest.test.ts
Comment on lines +166 to +173
it("returns 404 when thumbnail row is not found for caller", async () => {
const { app } = createServeApp([[]]);
const res = await app.request(`/api/thumbnail/serve/${OBJECT_ID}`, {
headers: { "x-test-user-id": ATTACKER_ID },
});
expect(res.status).toBe(404);
expect(mockS3Send).not.toHaveBeenCalled();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Strengthen the authz case with an existing foreign-owned thumbnail row.

This case currently validates only “row missing,” not “row exists but belongs to another user.” It can still pass if ownership filtering regresses. Use an existing row owned by TEST_USER_ID and call with ATTACKER_ID, then assert 404 and no S3 call.

Suggested test adjustment
-  it("returns 404 when thumbnail row is not found for caller", async () => {
-    const { app } = createServeApp([[]]);
+  it("returns 404 when thumbnail exists but belongs to another user", async () => {
+    const { app } = createServeApp([[THUMB_ROW]]);
     const res = await app.request(`/api/thumbnail/serve/${OBJECT_ID}`, {
       headers: { "x-test-user-id": ATTACKER_ID },
     });
     expect(res.status).toBe(404);
     expect(mockS3Send).not.toHaveBeenCalled();
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("returns 404 when thumbnail row is not found for caller", async () => {
const { app } = createServeApp([[]]);
const res = await app.request(`/api/thumbnail/serve/${OBJECT_ID}`, {
headers: { "x-test-user-id": ATTACKER_ID },
});
expect(res.status).toBe(404);
expect(mockS3Send).not.toHaveBeenCalled();
});
it("returns 404 when thumbnail exists but belongs to another user", async () => {
const { app } = createServeApp([[THUMB_ROW]]);
const res = await app.request(`/api/thumbnail/serve/${OBJECT_ID}`, {
headers: { "x-test-user-id": ATTACKER_ID },
});
expect(res.status).toBe(404);
expect(mockS3Send).not.toHaveBeenCalled();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/api/src/__tests__/routes/thumbnail/serve.test.ts` around lines 166 -
173, Update the "returns 404 when thumbnail row is not found for caller" test to
instead seed an existing thumbnail row owned by TEST_USER_ID and issue the
request as ATTACKER_ID so we exercise the "row exists but belongs to another
user" authz path; use createServeApp with a dataset containing a thumbnail
object keyed by OBJECT_ID and ownerId set to TEST_USER_ID, call
app.request(`/api/thumbnail/serve/${OBJECT_ID}`) with header "x-test-user-id":
ATTACKER_ID, and assert res.status is 404 and mockS3Send was not called (keep
test name and assertions but change the seeded row to belong to TEST_USER_ID).

pdfjs-dist 6.0 removed PDFDocumentProxy.destroy(); migrate cleanup to
loadingTask.destroy() so Type Check passes after develop picked up #1004.
Add ingest graph run/resume 403 cases for foreign checkpoint threadIds.
Clarify thumbnail GET scopes lookup with where/limit chain assertions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/pdfReader/usePdfDocument.ts`:
- Line 12: The inline comment about the pdfjs-dist v6 API change is
English-only; update the comment near pdfDoc.loadingTask.destroy() (and the note
about PDFDocumentProxy.destroy) to include a concise Japanese translation as
well, keeping both languages side-by-side per coding guidelines (e.g., English
sentence followed by Japanese sentence explaining that pdfjs-dist v6 removed
PDFDocumentProxy.destroy and to call pdfDoc.loadingTask.destroy()). Make sure
the Japanese text is clear, succinct, and mirrors the existing English meaning
without changing code or behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4044da3f-40a7-4f16-bf72-2e937d406dfd

📥 Commits

Reviewing files that changed from the base of the PR and between 9887e6d and 90aca0a.

📒 Files selected for processing (1)
  • src/components/pdfReader/usePdfDocument.ts

Comment thread src/components/pdfReader/usePdfDocument.ts
@otomatty otomatty self-assigned this Jun 11, 2026
Combine bilingual TSDoc for pdfjs-dist v6 loadingTask.destroy() migration.
@otomatty otomatty merged commit c0ac3a7 into develop Jun 11, 2026
20 checks passed
@otomatty otomatty deleted the cursor/route-integration-tests-1032-d7f1 branch June 11, 2026 03:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(api): 低カバレッジのルート(ingest / clip / composeSessions / notes/tags / thumbnail)へ統合テストを追加する

2 participants