원클릭으로
api-test
Generate an integration test for an API route with all required mocks and boilerplate. Use when creating tests for API endpoints.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Generate an integration test for an API route with all required mocks and boilerplate. Use when creating tests for API endpoints.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Launch, build, run, start, screenshot, or smoke-test the Librariarr Next.js webapp end-to-end. Use when asked to run librariarr, bring up the dev stack, take a screenshot of the dashboard or any UI page, verify a change in the running app, or check that the app boots cleanly.
Generate a new Next.js API route with project boilerplate (auth, validation, sanitize, Prisma). Use when creating new API endpoints.
Quick reference for all Librariarr project conventions and patterns. Consult when writing or reviewing code to verify correct patterns.
Create a Prisma migration file for schema changes. Required for production — db push only works in dev. Use after modifying schema.prisma.
Scaffold a complete new feature end-to-end (Prisma model, migration, schemas, API routes, tests). Use for new CRUD resources or features that span multiple layers.
Add a new Zod validation schema to src/lib/validation.ts. All schemas MUST live in this file using zod/v4. Use when creating validation for new API endpoints.
| 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> |
Generate an integration test for the API route at $ARGUMENTS.
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() },
}));
// 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 };
}),
}));
const { mockFn } = vi.hoisted(() => ({
mockFn: vi.fn(),
}));
vi.mock("@/lib/some-module", () => ({
someFunction: mockFn,
}));
import { GET, POST } from "@/app/api/path/to/route";
describe("API endpoint description", () => {
beforeEach(async () => {
await cleanDatabase();
clearMockSession();
vi.clearAllMocks();
});
afterAll(async () => {
await disconnectTestDb();
});
// tests go here
});
it("returns 401 when not authenticated", async () => {
const response = await callRoute(GET);
await expectJson(response, 401);
});
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);
});
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();
});
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);
});
callRoute(GET) or callRoute(POST, { body: { ... } })callRoute(GET, { url: "/api/path?param=value" })callRouteWithParams(PUT, { id: "abc" }, { body: { ... } })const body = await expectJson<{ key: Type }>(response, 200);tests/integration/<category>/<name>.test.tspnpm exec vitest run tests/integration/<category>/<name>.test.ts