Scrum4Me/docs/qa/api-test-plan.md

21 KiB
Raw 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