Skip to main content

api-test

Generate an integration test for an API route with all required mocks and boilerplate. Use when creating tests for API endpoints.

跳到安装

来源信息

仓库
ahembree/librariarr
最近来源活动
2026年6月13日 20:16
检测到的 SKILL.md 语言
英语
星标
5
分支
1

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
api-test
description
Generate an integration test for an API route with all required mocks and boilerplate. Use when creating tests for API endpoints.
argument-hint
<route-import-path>
# Integration Test Generator Generate an integration test for the API route at `$ARGUMENTS`. ## Required Boilerplate (copy exactly) ### File header — imports and standard mocks: ```typescript import { describe, it, expect, beforeEach, afterAll, vi } from "vitest"; import { cleanDatabase, disconnectTestDb } from "../../setup/test-db"; import { setMockSession, clearMockSession } from "../../setup/mock-session"; import { callRoute, callRouteWithParams, expectJson, createTestUser, // Add other factories as needed: // createTestServer, createTestLibrary, createTestMediaItem, // createTestRuleSet, createTestSonarrInstance, createTestRadarrInstance, // createTestLidarrInstance, createTestSeerrInstance, createTestExternalId, // createTestLogEntry, createTestMediaStream } from "../../setup/test-helpers"; // Redirect prisma to test database — MUST come before route imports vi.mock("@/lib/db", async () => { const { getTestPrisma } = await import("../../setup/test-db"); return { prisma: getTestPrisma() }; }); // Suppress logger DB writes vi.mock("@/lib/logger", () => ({ logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }, apiLogger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }, dbLogger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }, })); ``` ### External service mocks (if needed): ```typescript // CRITICAL: Constructor mocks MUST use function() keyword, NOT arrow functions const mockTestConnection = vi.fn(); vi.mock("@/lib/arr/sonarr-client", () => ({ SonarrClient: vi.fn().mockImplementation(function () { return { testConnection: mockTestConnection }; }), })); ``` ### vi.hoisted() pattern — use when mock variables are referenced inside vi.mock() factories: ```typescript const { mockFn } = vi.hoisted(() => ({ mockFn: vi.fn(), })); vi.mock("@/lib/some-module", () => ({ someFunction: mockFn, })); ``` ### Route imports — MUST come AFTER all vi.mock() calls: ```typescript import { GET, POST } from "@/app/api/path/to/route"; ``` ### Lifecycle hooks: ```typescript describe("API endpoint description", () => { beforeEach(async () => { await cleanDatabase(); clearMockSession(); vi.clearAllMocks(); }); afterAll(async () => { await disconnectTestDb(); }); // tests go here }); ``` ## Standard Test Cases ### For every route — 401 unauthorized: ```typescript it("returns 401 when not authenticated", async () => { const response = await callRoute(GET); await expectJson(response, 401); }); ``` ### GET list endpoints: ```typescript it("returns empty array when user has no data", async () => { const user = await createTestUser(); setMockSession({ userId: user.id, plexToken: "tok", isLoggedIn: true }); const response = await callRoute(GET); const body = await expectJson<{ items: unknown[] }>(response, 200); expect(body.items).toEqual([]); }); it("returns data belonging to authenticated user", async () => { const user = await createTestUser(); setMockSession({ userId: user.id, plexToken: "tok", isLoggedIn: true }); // Create test data... const response = await callRoute(GET); const body = await expectJson<{ items: unknown[] }>(response, 200); expect(body.items).toHaveLength(1); }); it("does not return data belonging to another user", async () => { const user1 = await createTestUser(); const user2 = await createTestUser({ data: { plexId: "other", plexUsername: "other" } }); // Create data for user2... setMockSession({ userId: user1.id, plexToken: "tok", isLoggedIn: true }); const response = await callRoute(GET); const body = await expectJson<{ items: unknown[] }>(response, 200); expect(body.items).toHaveLength(0); }); ``` ### POST endpoints: ```typescript it("returns 400 with invalid body", async () => { const user = await createTestUser(); setMockSession({ userId: user.id, plexToken: "tok", isLoggedIn: true }); const response = await callRoute(POST, { body: {} }); await expectJson(response, 400); }); it("creates resource with valid data", async () => { const user = await createTestUser(); setMockSession({ userId: user.id, plexToken: "tok", isLoggedIn: true }); const response = await callRoute(POST, { body: { name: "Test", /* fields */ }, }); const body = await expectJson<{ item: { id: string } }>(response, 201); expect(body.item.id).toBeDefined(); }); ``` ### PUT/DELETE with [id] — ownership check: ```typescript it("returns 404 when resource belongs to another user", async () => { const user1 = await createTestUser(); const user2 = await createTestUser({ data: { plexId: "other", plexUsername: "other" } }); // Create resource owned by user2... setMockSession({ userId: user1.id, plexToken: "tok", isLoggedIn: true }); const response = await callRouteWithParams(DELETE, { id: resource.id }); await expectJson(response, 404); }); ``` ## Route Handler Invocation - Simple routes: `callRoute(GET)` or `callRoute(POST, { body: { ... } })` - With URL: `callRoute(GET, { url: "/api/path?param=value" })` - Dynamic params: `callRouteWithParams(PUT, { id: "abc" }, { body: { ... } })` - Assert response: `const body = await expectJson<{ key: Type }>(response, 200);` ## Steps 1. Read the route file to identify which HTTP methods it exports 2. Check imports for external services that need mocking (Plex, Sonarr, Radarr, etc.) 3. Identify which data factories are needed based on Prisma models used 4. Create test file at `tests/integration/<category>/<name>.test.ts` 5. Include all standard test cases plus route-specific edge cases 6. Run the test to verify: `pnpm exec vitest run tests/integration/<category>/<name>.test.ts`
在 GitHub 查看