Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
7899f11
docs: Create plan (#943)
KATO-Hiro Mar 1, 2026
e5e9ed7
docs: Add TDD to AGENTS.md (#943)
KATO-Hiro Mar 1, 2026
38e0006
docs: Update plan (#943)
KATO-Hiro Mar 1, 2026
5b5a4f8
refactor: Remove old mock (#943)
KATO-Hiro Mar 1, 2026
7879606
refactor: Extract calcWorkBookGradeModes to features/utils (#943)
KATO-Hiro Mar 1, 2026
57f6977
test: Add tests for calcWorkBookGradeModes (#943)
KATO-Hiro Mar 1, 2026
6521d6c
feat: Add WorkBookPlacement and SolutionCategory (#943)
KATO-Hiro Mar 1, 2026
35cbdd0
docs: Update plan (#943)
KATO-Hiro Mar 1, 2026
07d1d3c
docs: Add WorkBookPlacement (#943)
KATO-Hiro Mar 1, 2026
59712b6
feat: Add WorkBookPlacement and SolutionCategory (#943)
KATO-Hiro Mar 1, 2026
e6a84a7
docs: Add comment (#943)
KATO-Hiro Mar 1, 2026
e340b49
docs: Add dnd lib to CONTRIBUTING.md (#943)
KATO-Hiro Mar 1, 2026
1c0e1aa
build(deps): Use @dnd-kit for kanban board (#943)
KATO-Hiro Mar 1, 2026
456d86a
feat: Validate WorkBookPlacement using zod (#943)
KATO-Hiro Mar 1, 2026
3726fae
build: Update deps (#43)
KATO-Hiro Mar 2, 2026
a886711
docs: Add plan (#943)
KATO-Hiro Mar 2, 2026
fe59718
feat: Add DSL, GCL and NTL to AOJ courses (#943)
KATO-Hiro Mar 3, 2026
37c632c
feat: Add DSL, GCL and NTL to AOJ courses (#943)
KATO-Hiro Mar 3, 2026
90d1a92
feat: Add WorkBookPlacement ans related types (#943)
KATO-Hiro Mar 4, 2026
e9349a9
feat: [WIP] Enable sorting of workbook order in Kanban board (#943)
KATO-Hiro Mar 4, 2026
f6af23d
feat: Add seed data for Solution Categories (#943)
KATO-Hiro Mar 4, 2026
9cdf57d
docs: Add drafts for bugfix and refactor (#943)
KATO-Hiro Mar 4, 2026
3ce3942
docs: Create bug fix plan (#943)
KATO-Hiro Mar 4, 2026
6daf3a9
refactor: Replace form action with JSON API endpoint for placement up…
KATO-Hiro Mar 4, 2026
543efd8
docs: Add new bug and update plan (#943)
KATO-Hiro Mar 4, 2026
84c3b43
chore: Fix format (#943)
KATO-Hiro Mar 4, 2026
8d6c086
fix: Resolve cross-column drag assignment and improve E2E tests (#943)
KATO-Hiro Mar 4, 2026
7924f18
chore: Fix format (#943)
KATO-Hiro Mar 4, 2026
0fd3431
chore: Rename query params (#943)
KATO-Hiro Mar 4, 2026
36544d4
chore: Fix format (#943)
KATO-Hiro Mar 5, 2026
e19a0eb
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 8, 2026
5d78ce9
docs: Update plan (#943)
KATO-Hiro Mar 8, 2026
be06adb
docs: Update plan (#943)
KATO-Hiro Mar 8, 2026
6626fe9
docs: Revise refactor plan (#943)
KATO-Hiro Mar 8, 2026
73c2f1b
refactor: workbook order management with type-safe placements and Kan…
KATO-Hiro Mar 8, 2026
b61730e
chore: Fix format (#943)
KATO-Hiro Mar 8, 2026
374db64
refactor: simplify KanbanBoard with Record-based column state
KATO-Hiro Mar 8, 2026
f9cf7b7
chore: Fix format (#943)
KATO-Hiro Mar 8, 2026
c1a9783
refactor: extract validateAdminAccess and service helpers for placeme…
KATO-Hiro Mar 8, 2026
5a3136a
feat: improve Kanban UI — nav link, card link, and style tweaks
KATO-Hiro Mar 8, 2026
abb09b6
test: add unit tests for solutionCategory and fixture-based curriculu…
KATO-Hiro Mar 8, 2026
f13d97c
chore: Update plan (#943)
KATO-Hiro Mar 8, 2026
0e87e6d
docs: TODO list (#943)
KATO-Hiro Mar 8, 2026
332b1d9
chore: Fix typo (#943)
KATO-Hiro Mar 8, 2026
aef5236
chore: Add and update refactor plan v2 (#943)
KATO-Hiro Mar 8, 2026
e821536
refactor: Phase 7 — color tokens, type renames, BAD_REQUEST constant,…
KATO-Hiro Mar 9, 2026
1bb2960
refactor: Phase 8 — type extraction, WorkbookLink component, props sp…
KATO-Hiro Mar 9, 2026
04131c1
refactor: Phase 9 — layout width, snippet DRY, tab styling (#943)
KATO-Hiro Mar 9, 2026
def975d
refactor: Phase 10 — buildKanbanItems, calcPriorityUpdates, TabConfig…
KATO-Hiro Mar 9, 2026
e0d52a5
refactor: Phase 11 — service layer, KanbanTabBar extraction (#943)
KATO-Hiro Mar 9, 2026
01bd501
docs: update refactor.md to concise format (#943)
KATO-Hiro Mar 10, 2026
9e7ebef
chore: Add and update refactor plan (#943)
KATO-Hiro Mar 10, 2026
3058bc8
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 11, 2026
c967bc6
chore: Fix format (#943)
KATO-Hiro Mar 11, 2026
9f768ed
docs: Add todo v3 (#943)
KATO-Hiro Mar 11, 2026
25a8443
docs: Revise todo v3 (#943)
KATO-Hiro Mar 11, 2026
21647e5
docs: Revise todo v3 (#943)
KATO-Hiro Mar 11, 2026
0579b17
docs: Revise todo v3 (#943)
KATO-Hiro Mar 11, 2026
774d15d
refactor: Apply Phase 1-3 refactoring to workbook order feature
KATO-Hiro Mar 11, 2026
6f1b377
docs: Update project rules and architecture docs
KATO-Hiro Mar 11, 2026
ca4587a
refactor: Apply Phase 4-5 refactoring to workbook order feature
KATO-Hiro Mar 11, 2026
8dec79b
refactor: Apply Phase 6 service layer restructuring to workbook_place…
KATO-Hiro Mar 11, 2026
483cb83
refactor: Apply Phase 7 investigation and KanbanBoard cleanup
KATO-Hiro Mar 11, 2026
1841837
test: Apply Phase 8 test refactoring to workbook_placements
KATO-Hiro Mar 11, 2026
6550bf7
test: Expand kanban utils tests and extract fixtures
KATO-Hiro Mar 11, 2026
c08d42a
docs: Consolidate lessons learned into reusable rules and guides
KATO-Hiro Mar 11, 2026
1e1eeb1
docs: Rewrite refactor.md as reusable refactoring guide
KATO-Hiro Mar 11, 2026
33760fe
feat: Add /refactor-plan skill and consolidate Svelte component rules
KATO-Hiro Mar 11, 2026
332621b
docs: Add AI review (#943)
KATO-Hiro Mar 12, 2026
3d10cdb
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 13, 2026
d794c74
chore: Fix conflict (#943)
KATO-Hiro Mar 13, 2026
77e6d10
docs: Update AI review (#943)
KATO-Hiro Mar 13, 2026
f533f94
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 14, 2026
c0e7c5b
chore: Fix conflict (#943)
KATO-Hiro Mar 14, 2026
ff6d6ef
docs: Update review (#943)
KATO-Hiro Mar 14, 2026
16e05a8
chore: Fix format (#943)
KATO-Hiro Mar 14, 2026
b2864db
refactor: Apply fixes based on AI review feedback (#943)
KATO-Hiro Mar 14, 2026
6dc15e2
chore: Fix format (#943)
KATO-Hiro Mar 14, 2026
33344d2
docs: Consolidate dev-notes into plan.md and remove stale files (#943)
KATO-Hiro Mar 14, 2026
28cdf7c
chore: Fix format (#943)
KATO-Hiro Mar 14, 2026
3a8f320
docs: Resolve the TDD contradiction between workflow and conventions …
KATO-Hiro Mar 14, 2026
102f6d5
fix: Double-submit could throw an uncaught unique constraint violatio…
KATO-Hiro Mar 14, 2026
4b03a27
fix: typo (#943)
KATO-Hiro Mar 14, 2026
0565030
docs: Add rules from lessons (#943)
KATO-Hiro Mar 14, 2026
c5ecf36
docs: Update plan (#943)
KATO-Hiro Mar 14, 2026
bf4fca2
docs: Remove old tasks (#943)
KATO-Hiro Mar 14, 2026
fe12792
docs: Move important decisions to code (#943)
KATO-Hiro Mar 14, 2026
ab38501
chore: Update docs (#943)
KATO-Hiro Mar 14, 2026
b28badf
chore: Add and update rules (#943)
KATO-Hiro Mar 14, 2026
7916518
chore: Add and update rules (#943)
KATO-Hiro Mar 14, 2026
227dc35
docs: Add and update skills (#943)
KATO-Hiro Mar 14, 2026
20333d0
chore: Fix format (#943)
KATO-Hiro Mar 14, 2026
7009a63
chore: Rename (#943)
KATO-Hiro Mar 14, 2026
477dd33
chore: Add type (#943)
KATO-Hiro Mar 14, 2026
da0df19
chore: Use const and update descriptions (#943)
KATO-Hiro Mar 14, 2026
95d2010
chore: Use getter (#943)
KATO-Hiro Mar 14, 2026
60230e9
docs: Update plan (#943)
KATO-Hiro Mar 14, 2026
3db6e68
docs: Update plan (#943)
KATO-Hiro Mar 15, 2026
e0c65ce
chore: Fix type (#943)
KATO-Hiro Mar 15, 2026
e774963
chore: Use type (#943)
KATO-Hiro Mar 15, 2026
1b62f00
chore: Fix format (#943)
KATO-Hiro Mar 15, 2026
fb68371
refactor: Extract method (#943)
KATO-Hiro Mar 15, 2026
cd46c70
refactor (#943)
KATO-Hiro Mar 15, 2026
4fa7973
chore: Fix order (#943)
KATO-Hiro Mar 15, 2026
83cdf70
docs: Add note (#943)
KATO-Hiro Mar 15, 2026
aeecdcc
docs: Update rules and plan (#943)
KATO-Hiro Mar 15, 2026
f05eb21
refactor: Use consts instead of hardcoding (#943)
KATO-Hiro Mar 15, 2026
fcc0a53
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 16, 2026
9045d59
chore: Fix conflict (#943)
KATO-Hiro Mar 16, 2026
dc249d5
chore: Fix order (#943)
KATO-Hiro Mar 16, 2026
1aba9c9
refactor: To inline from helper (#943)
KATO-Hiro Mar 16, 2026
83e6e90
docs: Update rules for component boundaries, test integrity, and impl…
KATO-Hiro Mar 16, 2026
f54f3b1
docs: Update comment (#943)
KATO-Hiro Mar 16, 2026
de548c6
docs: Fix sample (#943)
KATO-Hiro Mar 16, 2026
81d24ab
docs: Add tasks after review (#943)
KATO-Hiro Mar 16, 2026
0ab9aa9
docs: Add dual-enforcement, empty-list fallback, and testing rules
KATO-Hiro Mar 16, 2026
a69d430
docs: Clarify redirect behavior in API routes and store migration req…
KATO-Hiro Mar 16, 2026
d230287
docs: Fix signnature (#943)
KATO-Hiro Mar 16, 2026
5241da6
docs: Simplify pure function example in svelte-components rule
KATO-Hiro Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .claude/rules/auth.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
description: Authentication rules
globs:
paths:
- 'src/lib/server/auth.ts'
- 'src/routes/(auth)/**'
- 'src/routes/(admin)/_utils/auth.ts'
- 'src/routes/(admin)/**'
- 'src/hooks.server.ts'
---

Expand Down Expand Up @@ -30,6 +32,9 @@ globs:

- `src/lib/server/auth.ts`: Lucia configuration
- `src/hooks.server.ts`: Global request handler
- `src/routes/(admin)/_utils/auth.ts`:
- `validateAdminAccess(locals)` — for page routes; redirects to `/login` for both unauthenticated and non-admin users (do not use in `+server.ts`)
- `validateAdminAccessForApi(locals)` — for API routes (`+server.ts`); throws `error(401)` if unauthenticated, `error(403)` if not admin
- `prisma/schema.prisma`: User, Session, Key models

## Security
Expand Down
48 changes: 48 additions & 0 deletions .claude/rules/coding-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Coding Style

## Naming

- **Abbreviations**: avoid non-standard abbreviations (`res` → `response`, `btn` → `button`). When in doubt, spell it out.
- **Lambda parameters**: no single-character names (e.g., use `placement`, `workbook`). Iterator index `i` is the only exception.
- **`upsert`**: only use when the implementation performs both insert and update. For insert-only, use `initialize`, `seed`, or another accurate verb.
- **`any`**: before using `any`, check the value's origin — adding a missing `@types/*` or `devDependency` often provides the correct type.
- **UI labels**: if a label does not match actual behavior, update it or add an inline comment explaining the intentional mismatch.

## Syntax

- **Braces**: always use braces for single-statement `if` blocks. Never `if () return;` — write `if () { return; }`.
- **Plural type aliases**: define `type Placements = Placement[]` instead of using `Placement[]` directly in signatures and variables.

## Markdown Code Blocks

Always specify a language identifier on every fenced code block. Never write bare ` ``` `.

Common identifiers: `typescript`, `svelte`, `sql`, `bash`, `mermaid`, `json`, `prisma`, `html`, `css`.

## SvelteKit: Routes vs API Endpoints

- Page routes (`+page.server.ts`): use `redirect()` to navigate
- API routes (`+server.ts`): use `error()` — throwing `redirect()` returns a 3xx response; `fetch` follows it by default and receives the HTML page at the redirect target instead of a JSON error

Comment thread
KATO-Hiro marked this conversation as resolved.
## Dual-Enforcement Constraints

When the same constraint is enforced in two layers (e.g. Zod validation + SQL `CHECK`), add an inline comment stating each layer's role and the obligation to keep them in sync:

```typescript
// XOR constraint: dual enforcement via Zod (early validation) and a CHECK in migration.sql (last line of defence).
// Prisma lacks @@check, so the SQL constraint is maintained manually. Keep both in sync.
.refine(...)
```

## Async Rollback: Capture State Before `await`

Capture `$state` values before the first `await` for safe rollback. A concurrent update can overwrite the variable while awaiting:

```typescript
const previous = items; // capture before await
try {
await saveToServer(items);
} catch {
items = previous;
}
```
65 changes: 47 additions & 18 deletions .claude/rules/prisma-db.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,69 @@
---
description: Prisma and database rules
globs:
paths:
- 'prisma/**'
- 'src/lib/server/**'
- 'src/lib/services/**'
- 'src/features/**/services/**'
---

# Prisma & Database

## Schema Changes

1. Edit `prisma/schema.prisma`
2. Run `pnpm exec prisma migrate dev --name <description>` to create migration
3. Run `pnpm exec prisma generate` to update client (auto-runs after migrate)
2. Run `pnpm exec prisma migrate dev --name <snake_case_description>`

## Naming

- Model names: `PascalCase` (e.g., `User`, `TaskAnswer`)
- Field names: `camelCase` (preferred) or `snake_case` (legacy)
- Relation fields: Descriptive names matching the relation

## Key Models

- `User`: User accounts with AtCoder validation status
- `Task`: Tasks with difficulty grades (Q11-D6)
- `TaskAnswer`: User submission status per task
- `WorkBook`: task collections
- `Tag` / `TaskTag`: task categorization
- Models: `PascalCase` | Fields: `camelCase` (preferred) or `snake_case` (legacy)

## Server-Only Code

- Import database client only in `src/lib/server/`
- Use `$lib/server/database` for Prisma client access
- Import DB client only in `src/lib/server/` via `$lib/server/database`
- Never import server code in client components

## Service Layer

- All CRUD through the service layer (`src/lib/services/` or `src/features/**/services/`)
- Route handlers call service methods — no direct Prisma in `+server.ts` / `+page.server.ts`
- Service functions return pure values (`{ error: string } | null`), never `Response` / `json()`

## Transactions

- Use `prisma.$transaction()` for multi-step operations
- Handle errors with try-catch and proper rollback
Use `prisma.$transaction()` for multi-step operations.

## N+1 Queries

Replace per-item DB calls in loops with a bulk fetch + `Map`:

```typescript
const records = await prisma.foo.findMany({ where: { id: { in: ids } } });
const map = new Map(records.map((r) => [r.id, r]));
```

## Enum Types

Prisma-generated enums and app-defined enums are distinct TypeScript types even with identical members. Keep explicit casts at the boundary — do not remove them as "redundant".

## Idempotent Writes

Prefer `createMany({ skipDuplicates: true })` over catching P2002 for expected unique violations (e.g., double-submit). Maps to `INSERT ... ON CONFLICT DO NOTHING`. Top-level only (not nested); PostgreSQL/CockroachDB/SQLite only.

## Zod Schema for Int Fields

`z.number().positive()` passes decimals. For Prisma `Int` fields use `z.number().int().positive()`.

## Validate Constraints

Prisma does not support `@@check`. To add one:

1. `pnpm exec prisma migrate dev --create-only --name <description>` — generate migration without applying
2. Edit the generated `migration.sql` to add the CHECK constraint manually
3. `pnpm exec prisma migrate dev` — apply

Document the constraint in `prisma/ERD.md` (the only place it's visible):

```mermaid
%% XOR constraint: workbookplacement_xor_grade_category — exactly one of taskGrade or solutionCategory must be non-null
```
95 changes: 77 additions & 18 deletions .claude/rules/svelte-components.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Svelte component development rules
globs:
paths:
- 'src/**/*.svelte'
- 'src/lib/components/**'
- 'src/lib/stores/**/*.svelte.ts'
Expand All @@ -10,37 +10,96 @@ globs:

## Runes Mode (Required)

- Use `$props()` for component props
- Use `$state()` for reactive state
- Use `$derived()` for computed values
- Use `$effect()` for side effects

## Props Pattern
Use `$props()`, `$state()`, `$derived()`, `$effect()` in all components. Props pattern:

```svelte
<script lang="ts">
interface Props {
title: string;
count?: number;
}

let { title, count = 0 }: Props = $props();
</script>
```

## Stores
## File Naming

- Place store files in `src/lib/stores/` with `.svelte.ts` extension
- Use class-based stores with `$state()` for internal state
- Export singleton instances
- Components: `PascalCase.svelte`
- Stores: `snake_case.svelte.ts` in `src/lib/stores/`, class-based with `$state()`, export singleton. Pre-Runes stores (using `writable()`, `.ts` extension) must be migrated to this pattern before adding features or extending them.

## Flowbite Svelte

- Import components from `flowbite-svelte`
- Use Tailwind CSS v4 utility classes
- Dark mode: Use `dark:` prefix for dark mode variants
Import from `flowbite-svelte`. Use Tailwind CSS v4 utility classes. Dark mode: `dark:` prefix.

## File Naming
## `$state()` Initialization with `$props()`

- Components: `PascalCase.svelte`
- Stores: `snake_case.svelte.ts`
Referencing `$props()` inside `$state()` initializer triggers "This reference only captures the initial value". Wrap with `untrack` if intentional:

```svelte
let count = $state(untrack(() => initialCount)); // intentional: prop is initial seed only
```

## `{#snippet}` Placement

Define snippets at the **top level**, outside component tags. Inside a tag = named slot = type error:

```svelte
<!-- Good -->
{#snippet footer()}...{/snippet}
<Dialog {footer} />

<!-- Bad: named slot, not a snippet prop -->
<Dialog>{#snippet footer()}...{/snippet}</Dialog>
```

## Snippet vs Component

Prefer `{#snippet}` when: (1) needs direct `$state` access, (2) pure display only, (3) same-file DRY.
Promote to component when: independent state/lifecycle needed, exceeds ~30 lines, or reused across files.

## Component Boundaries

- One component, one responsibility: don't mix display, state management, and data fetching
- Extract `$derived`/`$effect` logic exceeding ~5 lines to a custom store
- Extract repeated UI patterns (2+ uses) to a snippet or component (see Snippet vs Component)

## Keep Components Thin

Business logic and pure utilities belong outside `<script>` blocks — in the nearest `utils/` (or `_utils/` for routes), with adjacent unit tests. See `docs/guides/architecture.md` for layer-specific placement.

## Pure Functions and Side Effect Separation

Extract business logic as pure functions to `utils/` (or `_utils/` for routes); keep side effects in the caller:

```typescript
// Pure function in _utils/ — testable, no side effects
export function buildUpdatedUrl(url: URL, activeTab: ActiveTab): URL { ... }

// Side effect stays in the caller
replaceState(buildUpdatedUrl($page.url, activeTab), {});
```

## Empty-list Fallback in `{#each}`

Use `{:else}` to render a placeholder when the list is empty — no wrapper conditional needed:

```svelte
{#each items as item (item.id)}
<Card {item} />
{:else}
<p>No items yet.</p>
{/each}
```

## Eliminate Branching with Records

Replace `if`/ternary chains with `Record<EnumType, T>`:

```typescript
const TAB_CONFIGS: Record<ActiveTab, TabConfig> = {
curriculum: { label: 'Curriculum', ... },
solution: { label: 'Solution', ... },
};
```

Use the enum type as the key type, not `string`.
81 changes: 55 additions & 26 deletions .claude/rules/testing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Testing rules and patterns
globs:
paths:
- '**/*.test.ts'
- '**/*.spec.ts'
- 'tests/**'
Expand All @@ -9,46 +9,75 @@ globs:

# Testing

## Test Integrity

- Never delete, comment out, or weaken assertions (e.g. `toEqual` → `toBeDefined`) to make tests pass
- Fix the implementation, not the test; if the test itself is wrong, explain why in a comment or commit message

## Test Types

| Type | Tool | Location | Run Command |
| ----------- | ---------- | ----------------------- | ----------------------- |
| Unit | Vitest | `src/test/**/*.test.ts` | `pnpm test:unit` |
| Integration | Vitest | `src/test/` | `pnpm test:unit` |
| E2E | Playwright | `tests/*.test.ts` | `pnpm test:integration` |
| Type | Tool | Location | Run Command |
| ---- | ---------- | ----------------------------------------------------------------- | ----------------------- |
| Unit | Vitest | `src/test/` (mirrors `src/lib/`) or co-located in `src/features/` | `pnpm test:unit` |
| E2E | Playwright | `tests/` | `pnpm test:integration` |

## Assertions

- Use `toBe(true)` / `toBe(false)` over `toBeTruthy()` / `toBeFalsy()`
- For DB query tests, assert `orderBy`, `include`, and other significant parameters with `expect.objectContaining` — not just `where`
- Enum membership: `in` traverses the prototype chain; use `Object.hasOwn(Enum, value)` instead

## Unit Tests
## Cleanup in Tests

Wrap DB-mutating cleanup in `try/finally` — a failing assertion skips cleanup and contaminates later tests:

```typescript
try {
await doSomething();
expect(result).toBe(expected);
} finally {
await restoreState();
}
```

- Place tests in `src/test/` mirroring `src/lib/` structure
- Use `@quramy/prisma-fabbrica` for test data factories
- Mock external APIs with Nock
## Test Data

## E2E Tests
- Use realistic fixture values (real task IDs, grade names) instead of placeholders like `'t1'`
- Extract shared data into fixture files; inline is fine for single-use cases
- After `.filter()` on fixtures, verify actual contents — same ID may refer to a different entity after fixture updates

- Place in `tests/` directory
- Use Playwright test utilities
- Test user flows, not implementation details
## Mock Helpers

## Patterns
Extract repeated mock patterns into a helper in the test file:

```typescript
import { describe, test, expect, vi } from 'vitest';

describe('functionName', () => {
test('expects to do something', () => {
// Arrange
// Act
// Assert
});
});
function mockFindMany(value: WorkBookPlacements) {
vi.mocked(prisma.workBookPlacement.findMany).mockResolvedValue(
value as unknown as Awaited<ReturnType<typeof prisma.workBookPlacement.findMany>>,
);
}
```

## Testing Extracted Utilities

- Add tests at extraction time, not later
- For URL manipulation: assert the original URL is not mutated
- For multi-column operations (e.g., DnD): assert both source and destination columns

## Coverage

- Run `pnpm coverage` for coverage report
- Target: 80% lines, 70% branches

## Service Layer Split for Testability

When a service file mixes DB operations and pure functions, split it into two files:

- `crud.ts` — DB operations (`getXxx`, `updateXxx`, `createXxx`); tests need Prisma mocks
- `initializers.ts` — pure computation (grade grouping, priority assignment); tests need no mocks

Stop the split if internal helpers (e.g. `fetchUnplacedWorkbooks`) would be fragmented across files — cohesion matters more than the split itself.

## HTTP Mocking

- Use Nock for mocking external HTTP calls
- See `src/test/lib/clients/` for examples
Use Nock for external HTTP calls. See `src/test/lib/clients/` for examples.
Loading
Loading