Skip to main content

write-tests

Write tests following project conventions. Use when adding new tests or modifying existing ones.

ソース情報

リポジトリ
getsentry/abacus
ソースの最終更新活動
2026年1月10日 20:51
検出された SKILL.md の言語
英語
スター
77
フォーク
5

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
write-tests
description
Write tests following project conventions. Use when adding new tests or modifying existing ones.
allowed-tools
Read, Grep, Glob, Edit, Write, Bash
# Write Tests Skill Write tests using Vitest. Tests are **colocated** next to source files. ## Structure ``` src/lib/utils.ts src/lib/utils.test.ts # colocated src/app/api/stats/route.ts src/app/api/stats/route.test.ts src/test-utils/ ├── setup.ts # global setup (PGlite, MSW, auth mock) ├── msw-handlers.ts # external API mocks └── auth.ts # auth test helpers ``` ## Auth Testing Auth is **globally mocked**. Use helpers to control auth state: ```typescript import { mockAuthenticated, mockUnauthenticated } from '@/test-utils/auth'; it('returns 401 when unauthenticated', async () => { await mockUnauthenticated(); const response = await GET(new Request('http://localhost/api/foo')); expect(response.status).toBe(401); }); it('returns data when authenticated', async () => { await mockAuthenticated(); // uses default test user // or with overrides: await mockAuthenticated({ email: 'custom@example.com' }); const response = await GET(new Request('http://localhost/api/foo')); expect(response.status).toBe(200); }); ``` Default test user: `test@example.com` / `Test User` ## Database Testing Uses **PGlite** (in-memory PostgreSQL). No Docker required. - Schema pushed automatically on boot - Each test runs in a transaction that rolls back - Use `insertUsageRecord` from `@/lib/queries` to seed data ```typescript import { insertUsageRecord, getOverallStats } from '@/lib/queries'; beforeEach(async () => { await insertUsageRecord({ date: '2025-01-01', email: 'user@example.com', tool: 'claude_code', model: 'sonnet-4', rawModel: 'claude-sonnet-4-20250514', inputTokens: 1000, outputTokens: 500, cacheWriteTokens: 0, cacheReadTokens: 0, cost: 0.01, }); }); ``` ## Running Tests ```bash pnpm test # run all pnpm test:watch # watch mode ``` ## Key Rules 1. Colocate tests next to source (`foo.ts` → `foo.test.ts`) 2. Use `mockAuthenticated()`/`mockUnauthenticated()` for auth 3. Use `insertUsageRecord` for seeding database 4. External APIs mocked via MSW in `src/test-utils/msw-handlers.ts` ## CRITICAL: Auth Tests Required **Every protected route MUST have an auth test.** This is non-negotiable. ### Session-Authenticated Routes Routes using `getSession()` must verify 401 on unauthenticated requests: ```typescript it('returns 401 for unauthenticated requests', async () => { await mockUnauthenticated(); const response = await GET(new Request('http://localhost/api/your-route')); expect(response.status).toBe(401); }); ``` ### Cron Routes Routes using `CRON_SECRET` must verify auth: ```typescript beforeEach(() => { vi.stubEnv('CRON_SECRET', 'test-secret'); }); it('returns 401 without authorization header', async () => { const response = await GET(new Request('http://localhost/api/cron/your-route')); expect(response.status).toBe(401); }); it('returns 401 with invalid authorization', async () => { const response = await GET( new Request('http://localhost/api/cron/your-route', { headers: { Authorization: 'Bearer wrong-secret' }, }) ); expect(response.status).toBe(401); }); ``` ### Webhook Routes Routes with signature verification must test invalid signatures: ```typescript it('returns 401 without signature', async () => { const response = await POST( new Request('http://localhost/api/webhooks/github', { method: 'POST', body: '{}', }) ); expect(response.status).toBe(401); }); it('returns 401 with invalid signature', async () => { const response = await POST( new Request('http://localhost/api/webhooks/github', { method: 'POST', body: '{}', headers: { 'x-hub-signature-256': 'sha256=invalid' }, }) ); expect(response.status).toBe(401); }); ``` **When adding a new route, always ask: "What auth does this route require?" and add the corresponding auth test.**
GitHubで見る