Scrum4Me/docs/test-plan.md
Janpeter Visser 7e45bbdbc0
docs: AI-optimized docs restructure (Phases 1–8) (#61)
* docs(dialog-pattern): add generic entity-dialog spec

Introduceert docs/patterns/dialog.md als bron-of-truth voor elke
create/edit/detail-dialog in Scrum4Me, ongeacht het achterliggende
dataobject. Bevat 14 secties: uitgangspunten, stack, component-
architectuur, layout, validatie, drielaagse demo-policy, submission,
dialog-gedrag, theming, footer, triggers/URL-state, per-entiteit
profile-template, out-of-scope, en een verificatie-checklist.

Registreert het patroon in CLAUDE.md "Implementatiepatronen"-tabel
zodat Claude (en mensen) de spec verplicht raadplegen voor elke
nieuwe dialog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(dialog-pattern): convert task spec + add pbi/story entity-profiles

Reduceert docs/scrum4me-task-dialog.md van 507 naar ~140 regels: alle
gedeelde regels verhuisd naar docs/patterns/dialog.md, dit document
bevat nu alleen Task-specifieke velden, URL-pattern, status-veld,
server actions, triggers en bewuste out-of-scope-keuzes.

Voegt twee nieuwe entity-profielen toe voor bestaande dialogen:
- docs/scrum4me-pbi-dialog.md (PbiDialog: state-based, code+title-rij,
  PbiStatusSelect, geen delete in v1)
- docs/scrum4me-story-dialog.md (StoryDialog: state-based, header met
  status/priority badges, inline activity-log, demo-readonly-fallback,
  inline-delete-confirm i.p.v. AlertDialog)

Beide profielen documenteren expliciet de "Bekende gaps t.o.v.
generieke spec" zodat opvolgende PR's de afwijkingen kunnen
rechtzetten of bewust kunnen accorderen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Added pdevelopment docs

* docs(plans): add docs-restructure plan for AI-optimized lookup

Audit of existing 39 doc files (~10.700 lines) and a phased restructure
proposal aimed at minimising the tokens an AI agent has to read to find
the right reference. Captures resolved decisions on language (English),
ADR template (Nygard default with MADR escape-hatch), index generator
(node script), and folder taxonomy. Proposal status — fase 1 to follow.

* docs(adr): add ADR scaffolding (templates, README, meta-ADR)

Set up docs/adr/ as the canonical home for architecture decisions:

- templates/nygard.md — default four-section format (Status, Context,
  Decision, Consequences) for one-way-door decisions.
- templates/madr.md — MADR v4 with YAML front-matter and explicit
  Considered Options for decisions where rejected alternatives matter.
- README.md — naming convention (NNNN-kebab-case), template-selection
  guidance (Nygard default; MADR for auth, queue mechanics, agent
  integration), status lifecycle, and ADR roster.
- 0000-record-architecture-decisions.md — meta-ADR establishing the
  practice itself, in Nygard format.

Backfilling existing implicit decisions (base-ui-over-radix, float
sort_order, demo-user three-layer policy, etc.) is fase 6 of the
docs-restructure plan.

* feat(docs): add docs index generator + initial INDEX.md

scripts/generate-docs-index.mjs walks docs/**/*.md, parses YAML
front-matter (or first H1 fallback) and a Nygard-style ## Status
section, then writes docs/INDEX.md with grouped tables for ADRs,
Specs, Plans (with archive subsection), Patterns, and Other.

Pure Node 20 (no external deps); idempotent — running it twice
produces byte-identical output. Excludes adr/templates/, the ADR
README, INDEX.md itself, and any *_*.md sidecar file.

Wire-up:
- package.json: docs:index → node scripts/generate-docs-index.mjs

Initial run indexed 35 docs across the existing structure; the
generated INDEX.md is committed so the table is reviewable in the
PR before hooking generation into a pre-commit step.

* chore: ignore Obsidian vault and personal sidecar files

Add .obsidian/ (Obsidian vault config) and _*.md (personal sidecar
notes) to .gitignore so the docs/ tree can serve as canonical source
of truth while still being usable as an Obsidian vault for personal
authoring. The docs index generator already excludes the same _*.md
pattern from INDEX.md.

* docs(plans): add PBI bulk-create spec for docs-restructure

Machine-parseable spec for an executor that calls the scrum4me MCP
(create_pbi → create_story → create_task) to seed the docs-restructure
work into the DB.

- Section 1 (Context) is the PBI description; serves as task-context
  via mcp__scrum4me__get_claude_context.
- Section 2 lists the 6 resolved decisions (English, MD3+styling
  merged, solo-paneel merged, .Plans archived, Nygard ADR default,
  node index script).
- Section 3 records what already shipped on this branch so the
  executor doesn't duplicate the ADR scaffolding or index generator.
- Section 4 carries the structured YAML graph: 1 PBI, 8 stories
  (one per phase), 39 tasks. product_id is REPLACE_ME — fill before
  running.
- YAML validated with PyYAML; field schema sanity-checked.

* docs(junk-cleanup): remove stub patterns/test.md

* docs(junk-cleanup): archive .Plans/ to docs/plans/archive/

* docs(front-matter): add YAML front-matter to docs/ root

* docs(front-matter): add YAML front-matter to patterns/

* docs(front-matter): add YAML front-matter to plans + agent files

* docs(index): regenerate INDEX.md after front-matter pass

* docs(naming): drop scrum4me- prefix from doc filenames

* docs(naming): lowercase API.md and MD3 filenames

* docs(naming): rename plan file to kebab-case ASCII

* docs(naming): rename middleware.md to proxy.md (next 16)

* docs(naming): polish CLAUDE.md doc-index after renames

* docs(taxonomy): scaffold topical folders under docs/

* docs(taxonomy): move spec files into docs/specs/

* docs(taxonomy): move design/api/qa/backlog/assets into folders

* docs(taxonomy): move agent-instruction-audit into decisions/

* docs(split): break architecture.md into 6 topical files

* docs(split): merge solo-paneel-spec into specs/functional.md

* docs(split): merge md3-color-scheme into design/styling

* docs(trim): extract branch/commit rules into runbook

* docs(trim): extract MCP integration into runbook

* docs(adr): add 0001-base-ui-over-radix

* docs(adr): add 0002-float-sort-order

* docs(adr): add 0003-one-branch-per-milestone

* docs(adr): add 0004-status-enum-mapping

* docs(adr): add 0005-iron-session-over-nextauth

* docs(adr): add 0006-demo-user-three-layer-policy

* docs(adr): add 0007-claude-question-channel-design

* docs(adr): add 0008-agent-instructions-in-claude-md + update README index

* docs(index): regenerate after ADR 0001-0008

* docs(glossary): add docs/glossary.md

* chore(docs): regenerate INDEX.md in pre-commit hook

* docs(readme): link INDEX + glossary + agent instructions

* feat(docs): add doc-link checker script

* chore(docs): wire docs:check-links and docs npm scripts

* ci(docs): block merge on broken doc links

* docs(links): fix broken cross-references after restructure

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:21:59 +02:00

21 KiB
Raw Permalink Blame History

title status audience language last_updated
Scrum4Me — API Test Plan active
maintainer
contributor
nl 2026-05-03

Scrum4Me — API Test Plan

Versie: 1.0
Datum: 25 april 2026
Auteur: Jan Peter Visser
Status: Draft


1. Introduction

This document describes the test plan and test planning for the Scrum4Me API endpoints — the integration surface that Claude Code and external agents use to interact with the application. It covers strategy, scope, test cases, tooling, exit criteria, and a phased execution schedule.

The API consists of 7 endpoints that form the Definition of Done checkpoint: all 7 must pass curl-level verification before the project ships.


2. Objectives

# Objective
O-1 Verify that all 7 API endpoints return correct responses for valid input
O-2 Verify that unauthenticated requests are rejected with 401
O-3 Verify that demo users cannot perform write operations (403)
O-4 Verify that cross-user access is impossible at every endpoint
O-5 Verify that all Zod validation schemas reject malformed input with 400
O-6 Verify edge cases: resource-not-found (404), empty result sets, boundary values
O-7 Produce executable curl scripts that satisfy the DoD requirement

3. Scope

3.1 In scope

Endpoint Method Auth type Write Demo check
/api/products GET Bearer token No No
/api/products/:id/next-story GET Bearer token No No
/api/sprints/:id/tasks GET Bearer token No No
/api/stories/:id/tasks/reorder PATCH Bearer token Yes Yes
/api/stories/:id/log POST Bearer token Yes Yes
/api/tasks/:id PATCH Bearer token Yes Yes
/api/todos POST Bearer token Yes Yes

3.2 Out of scope

  • /api/profile/avatar (GET/POST) — session-cookie auth, separate concern
  • Server Actions (actions/*.ts) — UI-layer, covered by separate acceptance testing
  • Frontend components and pages
  • Database migrations and schema changes
  • Performance and load testing

4. Test Strategy

4.1 Layers

The strategy uses two complementary test layers. Together they satisfy both the automated regression requirement and the DoD curl requirement.

┌─────────────────────────────────────────────────────────────────────┐
│  Layer 1 — Vitest Unit Tests (automated, mocked)                    │
│  Fast, deterministic, no external DB required                       │
│  Covers: auth, demo block, cross-user isolation, input validation   │
├─────────────────────────────────────────────────────────────────────┤
│  Layer 2 — Curl Scripts (manual, real DB)                           │
│  Executable against localhost:3000 with seeded test data            │
│  Covers: happy paths, response shape, DoD compliance                │
└─────────────────────────────────────────────────────────────────────┘

No Playwright, no Jest — Vitest is already configured and the existing __tests__/api/security.test.ts establishes the mock pattern to follow.

4.2 Vitest approach

All unit tests mock two dependencies only:

vi.mock('@/lib/prisma', () => ({
  prisma: { /* model methods as vi.fn() */ }
}))

vi.mock('@/lib/api-auth', () => ({
  authenticateApiRequest: vi.fn()
}))

This approach:

  • Tests route handler logic in isolation
  • Keeps tests fast (no network/DB)
  • Follows the pattern already validated in security.test.ts
  • Does not fall into the mock/prod divergence trap because the mocked boundary (authenticateApiRequest + prisma) is stable and narrow

4.3 Curl approach

A single shell script scripts/test-api.sh with:

  • A TOKEN variable set at the top (obtained from a seeded user via the UI)
  • One function per endpoint, each printing pass/fail based on HTTP status
  • Run order that follows the Lars flow (read → write → verify)

4.4 Test data

The existing seed (prisma/seed.ts) creates:

  • lars — full-permission user, used as the primary test actor
  • demo — read-only user, used for demo-block tests

A second regular user (tester) must be created manually (or added to the seed) to test cross-user isolation scenarios.


5. Test Cases

The following tables list every test case by endpoint. Each case has an ID, description, input, expected HTTP status, and which layer covers it.

TC format

Field Meaning
ID Unique identifier, e.g. TC-P-01 (P = products)
Layer V = Vitest, C = Curl
Input What is sent
Expected HTTP status + key response fields

5.1 GET /api/products

ID Layer Scenario Input Expected
TC-P-01 V No token No Authorization header 401
TC-P-02 V Invalid token Bearer invalid123 401
TC-P-03 V Revoked token Valid hash but revoked_at set 401
TC-P-04 V Valid token, owns 2 products Valid token, 2 products in DB 200, array of 2
TC-P-05 V Valid token, is team member Valid token, member of product owned by other user 200, includes that product
TC-P-06 V Valid token, no products Valid token, no products in DB 200, empty array
TC-P-07 V Archived products excluded 1 active + 1 archived product 200, array of 1
TC-P-08 V Cross-user: other user's products not returned Token for user A, products owned by user B 200, empty array
TC-P-09 C Happy path (lars) Lars' token 200, ≥1 product

5.2 GET /api/products/:id/next-story

ID Layer Scenario Input Expected
TC-NS-01 V No token No Authorization header 401
TC-NS-02 V Invalid token Bearer invalid 401
TC-NS-03 V Product not found / not accessible Valid token, unknown product id 404
TC-NS-04 V No active sprint Valid token, product with no ACTIVE sprint 404
TC-NS-05 V Active sprint, no IN_SPRINT stories Valid token, sprint exists but 0 stories 404
TC-NS-06 V Returns highest-priority story Valid token, 3 IN_SPRINT stories with tasks 200, story with tasks array
TC-NS-07 V Cross-user: other user's product Token for user A, product owned by user B 404
TC-NS-08 C Happy path (lars, active sprint) Lars' token + DevPlanner product id 200, story object

5.3 GET /api/sprints/:id/tasks?limit=10

ID Layer Scenario Input Expected
TC-ST-01 V No token No Authorization header 401
TC-ST-02 V Invalid token Bearer invalid 401
TC-ST-03 V Sprint not found / not accessible Valid token, unknown sprint id 404
TC-ST-04 V Cross-user: other user's sprint Token for user A, sprint in user B's product 404
TC-ST-05 V Default limit applied No ?limit param 200, ≤10 tasks
TC-ST-06 V Custom limit respected ?limit=3, sprint has 5 tasks 200, exactly 3 tasks
TC-ST-07 V Limit boundary: limit=1 Sprint has multiple tasks 200, exactly 1 task
TC-ST-08 V Sprint with 0 tasks Valid sprint, no tasks 200, empty array
TC-ST-09 C Happy path (lars) Lars' token + active sprint id + ?limit=10 200, tasks array

5.4 PATCH /api/stories/:id/tasks/reorder

ID Layer Scenario Input Expected
TC-RO-01 V No token No Authorization header 401
TC-RO-02 V Invalid token Bearer invalid 401
TC-RO-03 V Demo user Valid token, isDemo: true 403
TC-RO-04 V Story not found / not accessible Valid token, unknown story id 404
TC-RO-05 V Cross-user: other user's story Token for user A, story in user B's product 404
TC-RO-06 V Empty task_ids array { "task_ids": [] } 400
TC-RO-07 V task_ids not array { "task_ids": "abc" } 400
TC-RO-08 V task_ids contains IDs from different story Valid token, mixed-story task IDs 400
TC-RO-09 V Happy path Valid token + valid task_ids in new order 200
TC-RO-10 C Happy path (lars) Lars' token + story id + ordered task ids 200

5.5 POST /api/stories/:id/log

ID Layer Scenario Input Expected
TC-L-01 V No token No Authorization header 401
TC-L-02 V Invalid token Bearer invalid 401
TC-L-03 V Demo user Valid token, isDemo: true 403
TC-L-04 V Story not found / not accessible Valid token, unknown story id 404
TC-L-05 V Cross-user: other user's story Token for user A, story in user B's product 404
TC-L-06 V Missing type field { "content": "..." } 400
TC-L-07 V Unknown type value { "type": "UNKNOWN", "content": "..." } 400
TC-L-08 V IMPLEMENTATION_PLAN — missing content { "type": "IMPLEMENTATION_PLAN" } 400
TC-L-09 V IMPLEMENTATION_PLAN — happy path { "type": "IMPLEMENTATION_PLAN", "content": "Approach: ..." } 201
TC-L-10 V TEST_RESULT — missing status { "type": "TEST_RESULT", "content": "..." } 400
TC-L-11 V TEST_RESULT — invalid status { "type": "TEST_RESULT", "content": "...", "status": "UNKNOWN" } 400
TC-L-12 V TEST_RESULT — happy path PASSED { "type": "TEST_RESULT", "content": "...", "status": "PASSED" } 201
TC-L-13 V TEST_RESULT — happy path FAILED { "type": "TEST_RESULT", "content": "...", "status": "FAILED" } 201
TC-L-14 V COMMIT — missing commit_hash { "type": "COMMIT", "content": "...", "commit_message": "..." } 400
TC-L-15 V COMMIT — missing commit_message { "type": "COMMIT", "content": "...", "commit_hash": "abc1234" } 400
TC-L-16 V COMMIT — happy path { "type": "COMMIT", "content": "...", "commit_hash": "abc1234", "commit_message": "feat: ..." } 201
TC-L-17 C IMPLEMENTATION_PLAN (lars) Lars' token + story id + IMPLEMENTATION_PLAN body 201
TC-L-18 C TEST_RESULT PASSED (lars) Lars' token + story id + TEST_RESULT body 201
TC-L-19 C COMMIT (lars) Lars' token + story id + COMMIT body 201

5.6 PATCH /api/tasks/:id

ID Layer Scenario Input Expected
TC-T-01 V No token No Authorization header 401
TC-T-02 V Invalid token Bearer invalid 401
TC-T-03 V Demo user Valid token, isDemo: true 403
TC-T-04 V Task not found Valid token, unknown task id 404
TC-T-05 V Cross-user: task in other user's product Token for user A, task in user B's product 404
TC-T-06 V Invalid status value { "status": "UNKNOWN" } 400
TC-T-07 V Empty body (no recognized fields) {} 400
TC-T-08 V Update status only { "status": "IN_PROGRESS" } 200
TC-T-09 V Update implementation_plan only { "implementation_plan": "Step 1: ..." } 200
TC-T-10 V Update both fields { "status": "DONE", "implementation_plan": "..." } 200
TC-T-11 V Team member can update task Token for team member (not owner), valid task 200
TC-T-12 C Update status to IN_PROGRESS (lars) Lars' token + task id + { "status": "IN_PROGRESS" } 200
TC-T-13 C Update status to DONE (lars) Lars' token + task id + { "status": "DONE" } 200

5.7 POST /api/todos

ID Layer Scenario Input Expected
TC-TD-01 V No token No Authorization header 401
TC-TD-02 V Invalid token Bearer invalid 401
TC-TD-03 V Demo user Valid token, isDemo: true 403
TC-TD-04 V Missing title { "product_id": "..." } 400
TC-TD-05 V Empty title { "title": "" } 400
TC-TD-06 V Without product_id (global todo) { "title": "My todo" } 201
TC-TD-07 V With valid product_id { "title": "My todo", "product_id": "..." } 201
TC-TD-08 V With product_id not accessible to user { "title": "...", "product_id": "<other user's product>" } 403 or 404
TC-TD-09 C Happy path without product (lars) Lars' token + { "title": "Test todo" } 201
TC-TD-10 C Happy path with product (lars) Lars' token + { "title": "...", "product_id": "..." } 201

6. Test Files

File Endpoints covered Layer
__tests__/api/security.test.ts products (GET), tasks/:id (PATCH) — already exists, extend V
__tests__/api/products.test.ts GET /api/products — happy paths + edge cases V
__tests__/api/next-story.test.ts GET /api/products/:id/next-story V
__tests__/api/sprint-tasks.test.ts GET /api/sprints/:id/tasks V
__tests__/api/story-log.test.ts POST /api/stories/:id/log V
__tests__/api/reorder.test.ts PATCH /api/stories/:id/tasks/reorder V
__tests__/api/tasks.test.ts PATCH /api/tasks/:id V
__tests__/api/todos.test.ts POST /api/todos V
scripts/test-api.sh All 7 endpoints C

7. Exit Criteria

The test phase is complete when all of the following are met:

Criterion Target
All Vitest tests pass npm test exits 0
No test is skipped or pending 0 skipped
All curl scripts return expected HTTP codes 100% pass rate on scripts/test-api.sh
Demo user blocked on all 4 write endpoints Verified via TC-RO-03, TC-L-03, TC-T-03, TC-TD-03
Cross-user access impossible Verified via TC-P-08, TC-NS-07, TC-ST-04, TC-RO-05, TC-L-05, TC-T-05, TC-TD-08
Security review passed No cross-user data leak found in any endpoint

8. Risks & Mitigations

Risk Likelihood Impact Mitigation
Mock divergence: mocked Prisma behavior differs from real DB Medium High Keep mocked boundary narrow (only prisma.* calls, not business logic); validate happy paths with curl against real DB
Seed data insufficient for cross-user tests Medium Medium Add a second test user tester to prisma/seed.ts
API token not available during curl tests Low High Document token creation step clearly in test-api.sh header
Zod schema changes break test expectations Low Low Vitest tests will catch this immediately on next npm test run
Reorder scope validation not fully covered Medium High TC-RO-08 explicitly tests mixed-story IDs; add to security review checklist

9. Dependencies

  • prisma/seed.ts must create (or document creation of) a second user for cross-user tests
  • scripts/ directory must exist before running the curl script
  • A valid API token must be obtained from a seeded lars user session before running curl tests
  • npm test must be runnable without a live database (all Vitest tests mock Prisma)


Test Planning

Overview

Phase What Duration Start End
P0 Setup & shared infrastructure 0.5 day 2026-04-28 2026-04-28
P1 Security & auth layer (Vitest) 1 day 2026-04-29 2026-04-29
P2 Per-endpoint unit tests (Vitest) 2 days 2026-04-30 2026-05-01
P3 Curl scripts 0.5 day 2026-05-02 2026-05-02
P4 Review, edge cases, DoD verification 0.5 day 2026-05-05 2026-05-05

Total: 4.5 days


Phase 0 — Setup & Infrastructure (2026-04-28)

Goal: Everything required to write and run tests is in place.

Tasks

ID Task Deliverable
P0-1 Add tester user to prisma/seed.ts with no shared products Updated seed file
P0-2 Create scripts/ directory with test-api.sh skeleton (TOKEN var, helper functions, empty cases) scripts/test-api.sh
P0-3 Verify npm test runs cleanly on security.test.ts Green CI
P0-4 Create __tests__/api/ test file skeletons (empty describe blocks) for each new file 7 new .test.ts files

Done when: npm test passes, all skeleton files exist, seed creates 3 users (demo, lars, tester).


Phase 1 — Security & Auth Layer (2026-04-29)

Goal: All auth (401), demo-block (403), and cross-user isolation test cases pass for all endpoints.

Tasks

ID Task Test cases File
P1-1 Extend security.test.ts — add missing endpoints to auth/demo/cross-user coverage TC-NS-0103,07 / TC-ST-0104 / TC-RO-0105 / TC-L-0105 / TC-TD-0103,08 security.test.ts
P1-2 Verify all 401 cases return { error: 'Unauthorized' } TC-P-0103 etc. security.test.ts
P1-3 Verify all 403 demo cases return { error: 'Niet beschikbaar in demo-modus' } TC-RO-03, TC-L-03, TC-T-03, TC-TD-03 security.test.ts
P1-4 Verify cross-user returns empty array or 404 (not 403) for read endpoints TC-P-08, TC-NS-07, TC-ST-04 security.test.ts

Done when: All P1 test cases green, 0 skipped. Security test file covers all 7 endpoints.


Phase 2 — Per-Endpoint Unit Tests (2026-04-30 2026-05-01)

Goal: Happy paths, input validation, and edge cases covered per endpoint.

Day 1 (2026-04-30) — Read endpoints

ID Task Test cases File
P2-1 products.test.ts — happy paths, empty result, archived filter TC-P-0407,09 products.test.ts
P2-2 next-story.test.ts — happy path, no sprint, no stories TC-NS-0406,08 next-story.test.ts
P2-3 sprint-tasks.test.ts — happy path, limit param, empty sprint TC-ST-0509 sprint-tasks.test.ts

Day 2 (2026-05-01) — Write endpoints

ID Task Test cases File
P2-4 story-log.test.ts — all 3 log types, each field validation TC-L-0616 story-log.test.ts
P2-5 reorder.test.ts — happy path, empty array, mixed-story IDs TC-RO-0609 reorder.test.ts
P2-6 tasks.test.ts — status update, plan update, both, invalid status, team member access TC-T-0611 tasks.test.ts
P2-7 todos.test.ts — with/without product_id, empty title TC-TD-0407 todos.test.ts

Done when: All Vitest files pass, npm test exits 0 with ≥60 test cases across all files.


Phase 3 — Curl Scripts (2026-05-02)

Goal: scripts/test-api.sh covers all 7 endpoints and all curl test cases pass against localhost.

Tasks

ID Task Test cases
P3-1 Implement curl script for GET endpoints (products, next-story, sprint-tasks) TC-P-09, TC-NS-08, TC-ST-09
P3-2 Implement curl script for write endpoints (reorder, log ×3, tasks ×2, todos ×2) TC-RO-10, TC-L-1719, TC-T-1213, TC-TD-0910
P3-3 Add negative cases to curl script: no token (401), demo token (403) TC-P-01, TC-TD-03
P3-4 Run full script against seeded local DB, fix any failures All C cases
P3-5 Document token acquisition steps in scripts/README.md or script header

Done when: bash scripts/test-api.sh prints all PASS, no failures.


Phase 4 — Review & DoD Verification (2026-05-05)

Goal: Test suite is complete, DoD exit criteria are all met.

Tasks

ID Task
P4-1 Run full npm test — verify 0 failures, 0 skipped
P4-2 Run scripts/test-api.sh against staging/Neon DB — verify all PASS
P4-3 Walk through security review checklist (cross-user access per endpoint)
P4-4 Verify demo user is blocked on all 4 write endpoints via curl
P4-5 Update __tests__/lars-flow-checklist.md to reference the new curl script
P4-6 Add test instructions to README (npm test, bash scripts/test-api.sh)
P4-7 Commit test files per commit strategy (separate commits per layer)

Done when: All exit criteria from section 7 are met. Test plan status → Approved.


Commit Plan

Following the strict commit strategy, test work is committed in these layers:

chore(tests): add tester user to prisma seed
test(security): extend security.test.ts to cover all 7 endpoints
test(products): add unit tests for GET /api/products
test(next-story): add unit tests for GET /api/products/:id/next-story
test(sprint-tasks): add unit tests for GET /api/sprints/:id/tasks
test(story-log): add unit tests for POST /api/stories/:id/log
test(reorder): add unit tests for PATCH /api/stories/:id/tasks/reorder
test(tasks): add unit tests for PATCH /api/tasks/:id
test(todos): add unit tests for POST /api/todos
chore(scripts): add test-api.sh curl test script
docs(tests): update README with test instructions

Summary Timeline

Week of 2026-04-28
  Mon 28 Apr  │ P0 — Setup & infrastructure
  Tue 29 Apr  │ P1 — Security & auth layer (Vitest)
  Wed 30 Apr  │ P2 Day 1 — Read endpoint unit tests
  Thu 01 May  │ P2 Day 2 — Write endpoint unit tests
  Fri 02 May  │ P3 — Curl scripts

Week of 2026-05-05
  Mon 05 May  │ P4 — Review, DoD verification, commit