From 3bb8b93a4c21f113bf22ac206cef2ba23126722a Mon Sep 17 00:00:00 2001 From: Daniel Volz Date: Thu, 30 Apr 2026 12:37:45 +0200 Subject: [PATCH] chore: add .planning/codebase map (7 documents, gsd-map-codebase) --- .planning/codebase/ARCHITECTURE.md | 168 ++++++++++++++++++++++++ .planning/codebase/CONCERNS.md | 122 +++++++++++++++++ .planning/codebase/CONVENTIONS.md | 116 +++++++++++++++++ .planning/codebase/INTEGRATIONS.md | 111 ++++++++++++++++ .planning/codebase/STACK.md | 86 ++++++++++++ .planning/codebase/STRUCTURE.md | 138 ++++++++++++++++++++ .planning/codebase/TESTING.md | 203 +++++++++++++++++++++++++++++ 7 files changed, 944 insertions(+) create mode 100644 .planning/codebase/ARCHITECTURE.md create mode 100644 .planning/codebase/CONCERNS.md create mode 100644 .planning/codebase/CONVENTIONS.md create mode 100644 .planning/codebase/INTEGRATIONS.md create mode 100644 .planning/codebase/STACK.md create mode 100644 .planning/codebase/STRUCTURE.md create mode 100644 .planning/codebase/TESTING.md diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..f949144 --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,168 @@ + +# Architecture + +**Analysis Date:** 2026-04-30 + +## System Overview + +```text +┌─────────────────────────────────────────────────────────────┐ +│ Frontend SPA (React) │ +├──────────────────┬──────────────────┬───────────────────────┤ +│ App Shell/Routes │ Shared State │ Feature Pages │ +│ `frontend/src/ │ `frontend/src/ │ `frontend/src/pages/` │ +│ App.tsx` │ context/` │ │ +└────────┬─────────┴────────┬─────────┴──────────┬────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Backend API (Fastify) │ +│ `backend/src/index.ts` + `backend/src/routes/` │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ SQLite Persistence + Migration Layer │ +│ `backend/src/db/schema.ts` + `backend/src/db/client.ts` │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Component Responsibilities + +| Component | Responsibility | File | +|-----------|----------------|------| +| Frontend bootstrap | Mount providers/router and start app tree | `frontend/src/main.tsx` | +| App router/shell | Public share routes, authenticated shell routes, global modal composition | `frontend/src/App.tsx` | +| Frontend orchestration | Compose domain hooks and expose app-level state/actions | `frontend/src/context/AppContext.tsx` | +| API proxy boundary | Rewrite `/api/*` requests to backend root routes | `frontend/vite.config.ts` | +| Backend composition root | Register plugins/routes, await migrations, start schedulers | `backend/src/index.ts` | +| Route handlers | HTTP contracts, validation, auth hooks, response shaping | `backend/src/routes/*.ts` | +| Domain services | Shared domain logic and scheduler behavior | `backend/src/services/*.ts` | +| Persistence | Table definitions + compatibility migration/runtime initialization | `backend/src/db/schema.ts`, `backend/src/db/client.ts` | + +## Pattern Overview + +**Overall:** Layered modular monolith (single frontend SPA + single backend process) + +**Key Characteristics:** +- Frontend uses React Router + context/hook composition (`frontend/src/App.tsx`, `frontend/src/context/AppContext.tsx`). +- Backend uses route modules with shared service modules (`backend/src/routes/medications.ts`, `backend/src/services/medications-service.ts`). +- Data persistence is centralized in Drizzle schema + startup migrations (`backend/src/db/schema.ts`, `backend/src/db/client.ts`). + +## Layers + +**Frontend Presentation + Orchestration:** +- Purpose: Render UI, route navigation, manage client state, invoke API. +- Location: `frontend/src/main.tsx`, `frontend/src/App.tsx`, `frontend/src/pages/`, `frontend/src/context/`, `frontend/src/hooks/`. +- Contains: pages, modals, app shell, hook-based API callers. +- Depends on: backend `/api/*`, i18n, shared frontend utils/types. +- Used by: browser clients. + +**Backend HTTP/API Layer:** +- Purpose: Expose REST endpoints, authenticate/authorize requests, validate input, map to service/db logic. +- Location: `backend/src/index.ts`, `backend/src/routes/`, `backend/src/plugins/`. +- Contains: Fastify app setup, route registration, auth middleware. +- Depends on: services, db client/schema, env plugin. +- Used by: frontend SPA and API consumers. + +**Domain Services Layer:** +- Purpose: Reusable business logic for scheduling, notifications, stock math, parsing. +- Location: `backend/src/services/`, `backend/src/utils/`. +- Contains: reminder scheduler, notification builders/delivery, medication helpers. +- Depends on: db models and utilities. +- Used by: routes and startup process. + +**Persistence Layer:** +- Purpose: Define DB schema and keep existing SQLite instances compatible. +- Location: `backend/src/db/schema.ts`, `backend/src/db/client.ts`, `backend/drizzle/`. +- Contains: tables, migration execution, backward-compatible alter migrations. +- Depends on: Drizzle + libsql client. +- Used by: routes/services. + +## Data Flow + +### Primary Request Path + +1. Frontend page triggers API call via `/api/*` fetch (`frontend/src/pages/PlannerPage.tsx:307`). +2. Vite proxy rewrites `/api` prefix to backend route root (`frontend/vite.config.ts:23`, `frontend/vite.config.ts:26`). +3. Fastify route handles request under `/planner/send-email` with auth + validation (`backend/src/routes/planner.ts:141`, `backend/src/routes/planner.ts:158`). +4. Route loads user settings and dispatches channel delivery helpers (`backend/src/routes/planner.ts:221`, `backend/src/routes/planner.ts:432`, `backend/src/routes/planner.ts:829`). + +### Public Share Flow + +1. Frontend routes public token URL to shared schedule view (`frontend/src/App.tsx:35`). +2. Shared schedule component fetches token payload from `/api/share/:token` (`frontend/src/components/SharedSchedule.tsx:311`). +3. Backend public share route reads token/settings and returns filtered medication schedule (`backend/src/routes/share.ts:125`, `backend/src/routes/share.ts:156`). + +**State Management:** +- Frontend: context-centric state aggregation (`frontend/src/context/AppContext.tsx:248`, `frontend/src/context/AppContext.tsx:1020`). +- Backend: DB-backed state with runtime scheduler state persisted through notification state utilities (`backend/src/services/reminder-scheduler.ts:42`). + +## Key Abstractions + +**Auth Context + Guards:** +- Purpose: unify session/API-key auth across protected routes. +- Examples: `backend/src/plugins/auth.ts`, `backend/src/routes/settings.ts`. +- Pattern: route-level `preHandler` guard plus request decoration (`backend/src/routes/settings.ts:138`, `backend/src/plugins/auth.ts:236`). + +**Notification Delivery Contract:** +- Purpose: keep route-triggered and scheduler-triggered notifications consistent. +- Examples: `backend/src/routes/planner.ts`, `backend/src/services/reminder-scheduler.ts`, `backend/src/services/notifications/delivery.ts`. +- Pattern: shared builders/delivery/state helpers imported into both paths (`backend/src/routes/planner.ts:23`, `backend/src/services/reminder-scheduler.ts:39`). + +**Frontend App Context Aggregator:** +- Purpose: centralize shared medication/settings/dose/share/refill state for page/modal consumers. +- Examples: `frontend/src/context/AppContext.tsx`, `frontend/src/context/ShareContext.tsx`. +- Pattern: compose domain hooks, expose typed value via provider (`frontend/src/context/AppContext.tsx:248`, `frontend/src/context/AppContext.tsx:1020`). + +## Entry Points + +**Frontend bootstrap:** +- Location: `frontend/src/main.tsx` +- Triggers: browser loads `index.html`. +- Responsibilities: initialize theme/provider stack and router (`frontend/src/main.tsx:12`, `frontend/src/main.tsx:15`). + +**Backend process entry:** +- Location: `backend/src/index.ts` +- Triggers: `npm run dev`/`npm start` in backend package. +- Responsibilities: await migrations, register routes, start HTTP listener and schedulers (`backend/src/index.ts:231`, `backend/src/index.ts:305`, `backend/src/index.ts:309`, `backend/src/index.ts:334`). + +## Architectural Constraints + +- **Threading:** Single Node.js event loop process with in-process schedulers started at runtime (`backend/src/index.ts:309`, `backend/src/index.ts:323`). +- **Global state:** Module/global singletons exist in auth and context layers (`backend/src/plugins/auth.ts:15`, `frontend/src/context/AppContext.tsx:222`). +- **Circular imports:** Not detected from sampled route/service/db/frontend orchestration files. +- **API boundary:** Frontend network calls must use `/api/*` so proxy rewrite applies (`frontend/vite.config.ts:23`, `frontend/vite.config.ts:26`). + +## Anti-Patterns + +### Duplicated Backend App Wiring + +**What happens:** Route/plugin registration appears in both `createApp(...)` and top-level startup path. +**Why it's wrong:** Two bootstrap paths increase divergence risk when new routes/plugins are added in one path but not the other. +**Do this instead:** Keep a single shared app-construction function used by both test/runtime startup paths (`backend/src/index.ts:133`, `backend/src/index.ts:207`, `backend/src/index.ts:289`). + +### Oversized Frontend Orchestration Context + +**What happens:** `AppContext` aggregates many unrelated concerns (medications, settings, doses, sharing, import/export, modal history) in one large provider. +**Why it's wrong:** High coupling and broad rerender surface make safe changes harder and increase regression risk. +**Do this instead:** Preserve existing provider contract, but move new domain concerns into focused hooks/providers and re-export through composition only when needed (`frontend/src/context/AppContext.tsx`, file size ~1035 lines). + +## Error Handling + +**Strategy:** Fail fast at route boundary with explicit status codes and schema validation, then log context-rich errors. + +**Patterns:** +- Route validation + immediate 400 responses for invalid input (`backend/src/routes/medications.ts:76`, `backend/src/routes/medications.ts:584`). +- Planner routes return explicit channel/config errors (`backend/src/routes/planner.ts:204`, `backend/src/routes/planner.ts:509`). +- Frontend captures network errors and maps them to normalized error codes for UI handling (`frontend/src/hooks/useMedications.ts:80`). + +## Cross-Cutting Concerns + +**Logging:** Fastify logger options configured centrally with environment-aware formatting (`backend/src/index.ts:66`, `backend/src/index.ts:161`). +**Validation:** Zod validation for medication payloads and explicit OpenAPI schema contracts in routes (`backend/src/routes/medications.ts:76`, `backend/src/routes/planner.ts:157`). +**Authentication:** Route-level auth hooks and dual API-key/session handling (`backend/src/routes/planner.ts:141`, `backend/src/plugins/auth.ts:113`, `backend/src/plugins/auth.ts:236`). + +--- + +*Architecture analysis: 2026-04-30* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 0000000..a71171a --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -0,0 +1,122 @@ +# Codebase Concerns + +**Analysis Date:** 2026-04-30 + +## Tech Debt + +**Backend startup duplication and config drift:** +- Issue: `backend/src/index.ts` contains two parallel server setup paths (the exported `createApp(...)` flow and the top-level runtime bootstrap). Plugin/route registration and rate-limit defaults are duplicated in both branches. +- Files: `backend/src/index.ts` +- Impact: Configuration behavior can diverge between test/programmatic app construction and production startup (for example, `createApp` uses fixed `rateLimit` max `300`, while runtime startup uses `process.env.RATE_LIMIT_MAX` fallback `100`). +- Fix approach: Extract one canonical app-construction function and let both runtime startup and tests consume it; remove duplicated registration blocks. + +**Notification architecture leakage and duplicated composition logic:** +- Issue: Notification delivery service code imports a route-layer helper (`sendShoutrrrNotification`) from settings routes, and large HTML/text reminder composition blocks are duplicated across manual and automatic reminder paths. +- Files: `backend/src/services/notifications/delivery.ts`, `backend/src/routes/settings.ts`, `backend/src/routes/planner.ts`, `backend/src/services/reminder-scheduler.ts` +- Impact: Layer boundary violations increase coupling, and duplicated notification formatting logic makes behavior regressions likely when changing message content or channel behavior. +- Fix approach: Move `sendShoutrrrNotification` to a service-layer module, make routes call service APIs only, and centralize email/push payload builders for planner + scheduler flows. + +**Migration artifact ambiguity in drizzle numbering:** +- Issue: There are two migration files with `0008_` prefix, but the journal tracks only one `0008` tag and then jumps to `0009`. +- Files: `backend/drizzle/0008_add_obsolete_medications.sql`, `backend/drizzle/0008_add_prescription_tracking.sql`, `backend/drizzle/meta/_journal.json` +- Impact: Developer confusion and higher risk of migration-order mistakes during future schema changes. +- Fix approach: Align migration file names and journal tags so each migration number is unique and journal order is obvious. + +**Monolithic UI/editor and route modules with broad lint suppressions:** +- Issue: Core interaction files are very large and rely on file-level `biome-ignore-all` suppressions for multiple rule categories. +- Files: `frontend/src/pages/MedicationsPage.tsx`, `frontend/src/components/MobileEditModal.tsx`, `frontend/src/components/SharedSchedule.tsx`, `frontend/src/components/MedDetailModal.tsx`, `backend/src/routes/medications.ts` +- Impact: Refactors become high-risk; local regressions are harder to isolate; suppressed rule categories hide legitimate quality issues in future edits. +- Fix approach: Split by domain slices (state orchestration vs rendering vs helper transforms), then replace file-level suppressions with narrow, local exceptions only where justified. + +## Known Bugs + +**Environment-dependent behavior mismatch between test app factory and runtime app:** +- Symptoms: Programmatic app creation and runtime startup can apply different operational defaults (rate limiting and selected config pathways). +- Files: `backend/src/index.ts` +- Trigger: Using `createApp(...)` in tests/integration contexts while production startup uses the top-level runtime branch. +- Workaround: Explicitly pass runtime-equivalent options into `createApp(...)` in tests until startup construction is unified. + +## Security Considerations + +**Server-side outbound notification surface is broad and sensitive to parser correctness:** +- Risk: The app performs server-side HTTP requests to user-configurable notification URLs, including multiple protocol handlers (`pushover://`, `telegram://`, `gotify://`, generic webhook URLs). +- Files: `backend/src/routes/settings.ts` +- Current mitigation: URL sanitation/validation and hostname checks are present (`sanitizeNotificationUrl`, `validateNotificationHostname` usage in route logic). +- Recommendations: Add focused security regression tests for sanitizer bypasses and callback URL edge cases, and keep all outbound request execution in a dedicated service layer. + +**Auth-off bootstrap path creates implicit default user state:** +- Risk: In auth-disabled mode, startup creates/relies on a default user path automatically. +- Files: `backend/src/db/client.ts` +- Current mitigation: Controlled by `AUTH_ENABLED` environment setting. +- Recommendations: Add startup log warnings when running without auth outside development and enforce explicit environment confirmation in deployment templates. + +## Performance Bottlenecks + +**Reminder scheduling uses repeated full scans over users and medication/dose datasets:** +- Problem: Reminder checks iterate all user settings and compute stock/prescription reminders with repeated in-memory loops over medication and dose collections. +- Files: `backend/src/services/reminder-scheduler.ts`, `backend/src/utils/scheduler-utils.ts` +- Cause: Polling/check strategy prioritizes correctness and compatibility over incremental indexing. +- Improvement path: Introduce incremental candidate selection (changed-medication windows, per-user next-check indices) and reduce repeated whole-set scans. + +**Intake reminder scheduler polls every minute and may scale linearly with active schedules:** +- Problem: Intake reminder check loop runs continuously at 60s interval and processes all due reminders/users each tick. +- Files: `backend/src/services/intake-reminder-scheduler.ts` +- Cause: Fixed-interval scheduler (`CHECK_INTERVAL_MS = 60 * 1000`) with loop-driven due-item selection. +- Improvement path: Move toward next-due-time scheduling or bucketing strategy; keep minute polling as fallback only. + +## Fragile Areas + +**Reminder state persistence and lock handling mix sync file IO with best-effort catches:** +- Files: `backend/src/services/notifications/state.ts`, `backend/src/services/reminder-scheduler.ts` +- Why fragile: Reminder state writes are synchronous file writes and some read paths swallow errors (`catch {}`), while lock/state files are local filesystem coordination primitives. +- Safe modification: Keep file format backward-compatible, add explicit error telemetry, and add tests for concurrent/failed write scenarios before changing scheduler state logic. +- Test coverage: No direct tests detected for `notifications/delivery.ts` and only limited direct state-function assertions. + +**Desktop/mobile medication edit parity depends on two large independent UI paths:** +- Files: `frontend/src/pages/MedicationsPage.tsx`, `frontend/src/components/MobileEditModal.tsx`, `frontend/src/components/medications/MedicationEditCoordinator.tsx` +- Why fragile: The same editing domain is implemented in separate surfaces, each with dense UI logic and custom interaction handling. +- Safe modification: Apply shared form-section components first, then update desktop and mobile in the same change; validate both paths with targeted tests. +- Test coverage: Coverage exists (`MedicationEditCoordinator`, `MobileEditModal`, `MedicationDialogs` tests), but parity regressions remain a recurring risk due to file size/complexity. + +## Scaling Limits + +**Current reminder architecture is single-node/local-state oriented:** +- Current capacity: Scheduler state and lock coordination are local files under data directory (`reminder-state.json`, `scheduler-locks/*`). +- Limit: Horizontal multi-instance scaling can duplicate work or require externalized coordination. +- Scaling path: Move reminder state/locks to DB or distributed lock backend and make scheduler execution leader-aware. + +**SQLite file-backed persistence constrains concurrent write scaling:** +- Current capacity: Single SQLite file with local filesystem path resolution. +- Limit: Higher write concurrency and distributed deployments will hit filesystem/database locking and throughput limits. +- Scaling path: Keep SQLite for local/small deployments; define migration path to managed DB for larger multi-user workloads. + +## Dependencies at Risk + +**Route-to-service coupling in notification stack:** +- Risk: Service-layer delivery module depends on route-layer helper import. +- Impact: Refactors of route modules can break unrelated notification infrastructure and complicate testing boundaries. +- Migration plan: Move shared notification send helpers into `backend/src/services/notifications/*` and keep route modules thin. + +## Missing Critical Features + +**Risk-driven scheduler stress/integration test suite for state-lock edge cases:** +- Problem: Complex scheduler/state code paths rely on file coordination and mixed channel delivery outcomes, but dedicated stress/chaos-style verification is limited. +- Blocks: High-confidence scaling and reliability changes in reminder subsystems. + +## Test Coverage Gaps + +**Notification delivery abstraction lacks direct unit tests:** +- What's not tested: Direct behavior of SMTP transport creation/result validation and push delivery helpers in the dedicated delivery module. +- Files: `backend/src/services/notifications/delivery.ts` +- Risk: Regressions in recipient validation, SMTP response handling, or provider fallback can ship unnoticed. +- Priority: High + +**Reminder state persistence/locking has limited direct verification:** +- What's not tested: Corrupted file recovery, concurrent state writes, and lock stale-file behavior under failure modes. +- Files: `backend/src/services/notifications/state.ts`, `backend/src/services/reminder-scheduler.ts` +- Risk: Duplicate sends or missed sends after crashes/restarts are difficult to detect early. +- Priority: High + +--- + +*Concerns audit: 2026-04-30* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..604a990 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,116 @@ +# Coding Conventions + +**Analysis Date:** 2026-04-30 + +## Naming Patterns + +**Files:** +- Frontend React components and pages use PascalCase file names (for example `frontend/src/components/MobileEditModal.tsx`, `frontend/src/pages/MedicationsPage.tsx`). +- Hooks use `useX` camelCase naming in files and symbols (for example `frontend/src/hooks/useMedications.ts`, `frontend/src/hooks/useScheduleController.ts`). +- Backend routes/services use kebab-case file names with domain suffixes (for example `backend/src/routes/medications.ts`, `backend/src/services/medications-service.ts`). +- Test files use `*.test.ts` or `*.test.tsx` in dedicated test folders (for example `backend/src/test/planner.test.ts`, `frontend/src/test/components/MobileEditModal.test.tsx`). + +**Functions:** +- Use camelCase names for functions and methods (for example `parseIntakesWithUnits` in `backend/src/services/medications-service.ts`, `loadMeds` in `frontend/src/hooks/useMedications.ts`). +- Use verb-first names for side-effect operations (`loadMeds`, `deleteMed`, `uploadMedImage` in `frontend/src/hooks/useMedications.ts`). + +**Variables:** +- Use camelCase for local variables and state (`refillHistoryExpanded`, `scheduleDays`, `showFutureDays` in `frontend/src/context/AppContext.tsx`). +- Constant maps and singleton keys use UPPER_SNAKE_CASE (`LOG_LEVELS` in `backend/src/utils/logger.ts`, `APP_CONTEXT_SINGLETON_KEY` in `frontend/src/context/AppContext.tsx`). + +**Types:** +- Type aliases and interfaces use PascalCase (`AppContextValue` in `frontend/src/context/AppContext.tsx`, `TestContext` in `backend/src/test/setup.ts`). +- Return-shape interfaces use `UseXReturn` convention for hooks (`UseMedicationsReturn` in `frontend/src/hooks/useMedications.ts`). + +## Code Style + +**Formatting:** +- Tool used: Biome (`biome.json`, scripts in `frontend/package.json`, `backend/package.json`, `package.json`). +- Key settings from `biome.json`: + - `indentStyle: tab` + - `indentWidth: 2` + - `lineWidth: 120` + - JavaScript quote style is double quotes, semicolons enabled, trailing commas `es5`. + +**Linting:** +- Tool used: Biome linter (`biome.json`). +- Key rules enforced/relevant: + - `style.useConst: error` + - `style.noNestedTernary: warn` + - `correctness.noUnusedVariables: warn` + - `suspicious.noExplicitAny: warn` +- Project governance in `AGENTS.md` reinforces readable code, early returns, and no nested ternaries. + +## Import Organization + +**Order:** +1. Node built-ins first in backend modules (for example `node:path` in `backend/src/routes/medications.ts`, `node:crypto` in `backend/src/index.ts`). +2. External packages second (`fastify`, `zod`, `drizzle-orm` in backend; `react`, `@testing-library/*` in frontend). +3. Internal modules last with relative paths (`../db/client.js`, `../../types`). + +**Path Aliases:** +- Not detected in TypeScript configs (`frontend/tsconfig.json`, `backend/tsconfig.json` do not define `paths`). +- Relative imports are the standard. + +## Error Handling + +**Patterns:** +- Backend validates request data with Zod schemas and `.refine(...)` constraints before route logic (`backend/src/routes/medications.ts`). +- Backend route tests assert explicit status codes and body shape (`backend/src/test/routes-real.test.ts`, `backend/src/test/planner.test.ts`). +- Frontend hooks often normalize recoverable API errors into UI-safe states (`frontend/src/hooks/useMedications.ts` converts network failures into `NETWORK_ERROR`). +- Some frontend fetch flows still use tolerant fallbacks (`catch(() => setMeds([]))` in `frontend/src/hooks/useMedications.ts`), so future changes should prefer explicit user-facing error channels per `AGENTS.md` fail-clear guidance. + +## Logging + +**Framework:** +- Backend startup logger wrapper over console with level filtering in `backend/src/utils/logger.ts`. +- Runtime HTTP logging via Fastify logger options in `backend/src/index.ts` (`buildLoggerOptions`, request correlation IDs). +- Frontend logging utility mirrors backend level semantics (`frontend/src/utils/logger.ts`). + +**Patterns:** +- Central log-level maps (`LOG_LEVELS`) and `shouldLog` gating are standard in both frontend and backend logger modules. +- Correlation ID propagation is enforced at request boundaries (`backend/src/index.ts` onRequest hook setting `x-correlation-id`). + +## Comments + +**When to Comment:** +- Comments are used for rationale and test setup intent, not line-by-line narration. +- Typical examples: + - Migration/setup intent in `backend/src/test/setup.ts` + - E2E stability rationale in `frontend/e2e/fixtures/index.ts` + - Timeout/determinism notes in `frontend/vitest.config.ts` and `frontend/playwright.base.config.ts` + +**JSDoc/TSDoc:** +- Used selectively for exported utilities and test helpers (`backend/src/test/setup.ts`, `frontend/e2e/fixtures/index.ts`, `frontend/src/utils/logger.ts`). +- Not mandatory for every function; concise type annotations plus targeted comments are preferred. + +## Function Design + +**Size:** +- Small-to-medium focused functions are common in services/hooks (`parseRawIntakeUnits`, `normalizeDateTime` in `backend/src/services/medications-service.ts`). +- Larger orchestrator modules exist where domain aggregation is required (`frontend/src/context/AppContext.tsx`). + +**Parameters:** +- Object parameters are used for extensibility in test factories and route payload shapes (`CreateMedicationOptions` in `backend/src/test/setup.ts`). +- Explicit primitive parameters used for concise helpers (`clickEditMed(page, medName)` in `frontend/e2e/medication-edit.spec.ts`). + +**Return Values:** +- Explicit return types are common on exported functions (`Promise`, `UseMedicationsReturn`). +- Guard-clause returns are common for invalid input or unavailable state (`if (!intakesJson) return [];` in `backend/src/services/medications-service.ts`). + +## Module Design + +**Exports:** +- Named exports are preferred for utilities, hooks, and service functions (`backend/src/services/notifications/index.ts`, `frontend/src/hooks/index.ts`). +- Mixed export style is used where legacy/default exports remain practical (`default` exports in component barrel `frontend/src/components/index.ts`). + +**Barrel Files:** +- Barrel files are actively used for stable import surfaces: + - `frontend/src/components/index.ts` + - `frontend/src/hooks/index.ts` + - `backend/src/services/notifications/index.ts` +- Practical rule for new code: export domain-level public APIs through local barrels, keep deep internal helpers imported directly. + +--- + +*Convention analysis: 2026-04-30* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..d911db0 --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,111 @@ +# External Integrations + +**Analysis Date:** 2026-04-30 + +## APIs & External Services + +**Medication Data APIs:** +- European Medicines Agency (EMA) JSON catalog - medication lookup seed and periodic catalog refresh + - SDK/Client: native `fetch` in `backend/src/services/medication-enrichment.ts` (`EMA_MEDICINES_URL`) + - Auth: none detected in code +- RxNorm (NLM RxNav REST) - normalized name/search enrichment and strength/form hints + - SDK/Client: native `fetch` in `backend/src/services/medication-enrichment.ts` (`RXNORM_BASE_URL`) + - Auth: none detected in code +- openFDA NDC API - product/package metadata enrichment + - SDK/Client: native `fetch` in `backend/src/services/medication-enrichment.ts` (`OPENFDA_NDC_URL`) + - Auth: none detected in code + +**Authentication/Identity Provider Integration:** +- OIDC providers (Authelia, Authentik, Pocket ID, Keycloak documented) - SSO login/callback flow + - SDK/Client: `openid-client` used in `backend/src/routes/oidc.ts` + - Auth: `OIDC_ISSUER_URL`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, `OIDC_REDIRECT_URI` validated in `backend/src/plugins/env.ts` + +**Messaging/Notifications:** +- SMTP providers - transactional reminder/test emails + - SDK/Client: `nodemailer` in `backend/src/services/notifications/delivery.ts` + - Auth: `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS` or `SMTP_TOKEN`, `SMTP_FROM`, `SMTP_SECURE` +- Push endpoints via Shoutrrr-compatible URL parsing + - SDK/Client: native `fetch` in `backend/src/routes/settings.ts` (`sendShoutrrrNotification`) + - Auth: URL-embedded creds/token per provider and optional basic auth extracted/sanitized in code +- Explicit external push provider endpoints used directly: + - `https://api.pushover.net/1/messages.json` in `backend/src/routes/settings.ts` + - `https://api.telegram.org` in `backend/src/routes/settings.ts` + +## Data Storage + +**Databases:** +- SQLite (file-based, local persistent volume) + - Connection: `DATA_DIR` (path resolution), optional `DOTENV_PATH` for env source + - Client: `@libsql/client` + `drizzle-orm` in `backend/src/db/client.ts` +- Migration pipeline: + - SQL migration artifacts in `backend/drizzle/*.sql` + - Runtime migration/alter execution in `backend/src/db/client.ts` and `backend/src/db/migration-utils.ts` + +**File Storage:** +- Local filesystem only + - Backend data root resolved by `backend/src/db/path-utils.ts` + - Image/static user files served from `/images` in `backend/src/index.ts` + - Compose bind mount `./data:/app/data` in `docker-compose.yml` + +**Caching:** +- In-process memory cache only for selected integration data + - OIDC discovery config cache in `backend/src/routes/oidc.ts` (`oidcConfig`) + - EMA catalog snapshot + refresh promise in `backend/src/services/medication-enrichment.ts` +- No external cache service detected (no Redis/Memcached dependency in package manifests) + +## Authentication & Identity + +**Auth Provider:** +- Custom session/JWT auth with optional OIDC SSO extension + - Implementation: Fastify cookie + JWT plugin, refresh token table, API key hashing in `backend/src/plugins/auth.ts`, `backend/src/routes/auth.ts`, `backend/src/plugins/jwt.ts`, `backend/src/routes/oidc.ts` + +## Monitoring & Observability + +**Error Tracking:** +- None detected for third-party SaaS error tracking (no Sentry/Rollbar/etc. dependencies) + +**Logs:** +- Structured app logging via Fastify/Pino in `backend/src/index.ts` +- Pretty logging in dev through `pino-pretty` (`backend/package.json`, logger setup in `backend/src/index.ts`) +- Frontend/nginx log behavior controlled through env and `frontend/nginx-entrypoint.sh` (documented in `.env.example`) + +## CI/CD & Deployment + +**Hosting:** +- Container image publishing to GitHub Container Registry (`ghcr.io`) in `.github/workflows/docker-build.yml` +- Runtime deployment model is self-hosted Docker Compose stack (`docker-compose.yml`) + +**CI Pipeline:** +- GitHub Actions for lint/type/test (`.github/workflows/test.yml`) +- Playwright E2E job (`.github/workflows/e2e.yml`) +- Docker build/push and optional release automation (`.github/workflows/docker-build.yml`) + +## Environment Configuration + +**Required env vars:** +- Core runtime: `PORT`, `CORS_ORIGINS`, `LOG_LEVEL`, `TZ` (`backend/src/plugins/env.ts`, `.env.example`) +- Auth when enabled: `AUTH_ENABLED=true` with `JWT_SECRET`, `REFRESH_SECRET`, `COOKIE_SECRET` (`backend/src/plugins/env.ts`) +- OIDC when enabled: `OIDC_ENABLED=true` with issuer/client/redirect vars (`backend/src/plugins/env.ts`) +- Email notifications: `SMTP_HOST`, `SMTP_USER`, plus pass/token and sender config (`backend/src/services/notifications/delivery.ts`, `.env.example`) +- Data location: `DATA_DIR` used by DB path resolver (`backend/src/db/path-utils.ts`) + +**Secrets location:** +- Local runtime env file `.env` (present in repository root; values not inspected) +- CI secrets managed by GitHub Actions secret store (e.g., `${{ secrets.GITHUB_TOKEN }}` in `.github/workflows/docker-build.yml`) + +## Webhooks & Callbacks + +**Incoming:** +- OIDC callback endpoint: `/auth/oidc/callback` in `backend/src/routes/oidc.ts` +- No inbound third-party webhook receiver route detected in backend routes + +**Outgoing:** +- Outbound HTTP notifications to webhook-style targets from `sendShoutrrrNotification` in `backend/src/routes/settings.ts` +- Provider-specific outgoing callbacks/APIs: + - Pushover API endpoint + - Telegram Bot API endpoint +- Outbound SMTP delivery through configured mail host (`backend/src/services/notifications/delivery.ts`) + +--- + +*Integration audit: 2026-04-30* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..56ec064 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,86 @@ +# Technology Stack + +**Analysis Date:** 2026-04-30 + +## Languages + +**Primary:** +- TypeScript (ESM) - Backend and frontend application code in `backend/src/**/*.ts` and `frontend/src/**/*.{ts,tsx}` +- SQL (SQLite migrations) - Schema evolution files in `backend/drizzle/*.sql` + +**Secondary:** +- CSS - UI styling in `frontend/src/**/*.css` and CSS modules such as `frontend/src/features/schedule/TimelineSurface.module.css` +- YAML - CI/CD and compose configuration in `.github/workflows/*.yml`, `docker-compose.yml`, `docker-compose.dev.yml` +- Shell - Container/runtime entrypoints in `backend/docker-entrypoint.sh`, `frontend/nginx-entrypoint.sh` + +## Runtime + +**Environment:** +- Node.js 22 runtime baseline (`node:22-slim` in `backend/Dockerfile`, `frontend/Dockerfile`; `actions/setup-node@v6` with `node-version: '22'` in `.github/workflows/test.yml` and `.github/workflows/e2e.yml`) + +**Package Manager:** +- npm (scripts in root `package.json`, `backend/package.json`, `frontend/package.json`) +- Lockfile: present (`backend/package-lock.json`, `frontend/package-lock.json` referenced by workflow cache in `.github/workflows/test.yml`) + +## Frameworks + +**Core:** +- Fastify 5 (`fastify`, `@fastify/*` in `backend/package.json`; app bootstrap in `backend/src/index.ts`) +- React 19 (`react`, `react-dom` in `frontend/package.json`; app entry in `frontend/src/main.tsx`) +- Vite 8 (`vite` and `@vitejs/plugin-react` in `frontend/package.json`; config in `frontend/vite.config.ts`) +- Drizzle ORM + libSQL client (`drizzle-orm`, `@libsql/client` in `backend/package.json`; DB init in `backend/src/db/client.ts`) +- Mantine 8 UI system (`@mantine/*` in `frontend/package.json`; provider in `frontend/src/ui/providers/AppUiProvider.tsx`) + +**Testing:** +- Vitest 4 (`vitest`, `@vitest/coverage-v8` in backend/frontend package manifests; configs in `backend/vitest.config.ts`, `frontend/vitest.config.ts`) +- Playwright (`@playwright/test` in `frontend/package.json`; configs in `frontend/playwright*.config.ts`; CI run in `.github/workflows/e2e.yml`) +- Testing Library (`@testing-library/*` in `frontend/package.json`) + +**Build/Dev:** +- TypeScript compiler (`tsc` scripts in `backend/package.json` and frontend type-check via `frontend/package.json`) +- TSX watcher for backend dev (`tsx watch src/index.ts` in `backend/package.json`) +- Biome for lint/format (`biome.json`, lint/check scripts across package manifests) +- Drizzle Kit for DB migration generation (`drizzle-kit` in `backend/package.json`, config in `backend/drizzle.config.ts`) + +## Key Dependencies + +**Critical:** +- `fastify` and `@fastify/*` - HTTP API runtime, security middleware, docs middleware (`backend/src/index.ts`) +- `drizzle-orm` + `@libsql/client` - SQLite data access and migration execution (`backend/src/db/client.ts`) +- `openid-client` + `jose` - OIDC SSO and token operations (`backend/src/routes/oidc.ts`, `backend/package.json`) +- `nodemailer` - SMTP notification delivery (`backend/src/services/notifications/delivery.ts`) +- `react`, `react-router-dom`, `@mantine/*` - SPA UI shell, routing, and component system (`frontend/src/main.tsx`, `frontend/src/App.tsx`) +- `i18next` + `react-i18next` - Localization runtime (`frontend/src/i18n/index.ts`) + +**Infrastructure:** +- `dotenv` + `zod` - env loading/validation (`backend/src/plugins/env.ts`) +- `sharp` - image processing pipeline support (`backend/package.json`, image route usage in medication flows) +- `@fastify/swagger` + `@fastify/swagger-ui` - OpenAPI docs on `/docs` (`backend/src/index.ts`) + +## Configuration + +**Environment:** +- Runtime env schema and validation in `backend/src/plugins/env.ts` +- Example variable inventory in `.env.example` +- Frontend proxy target via `BACKEND_URL` in `frontend/vite.config.ts` and compose files + +**Build:** +- Backend TS build config: `backend/tsconfig.json` +- Frontend TS + Vite config: `frontend/tsconfig.json`, `frontend/tsconfig.node.json`, `frontend/vite.config.ts` +- DB migration tooling config: `backend/drizzle.config.ts` +- Quality tooling config: `biome.json` + +## Platform Requirements + +**Development:** +- Node.js 22 with npm for local runs (`backend/package.json`, `frontend/package.json` scripts) +- Optional Docker Compose local stack (`docker-compose.dev.yml`) +- Browser runtime for frontend and Playwright browser binaries for E2E (`frontend/package.json`, `.github/workflows/e2e.yml`) + +**Production:** +- Containerized deployment using prebuilt images from GHCR (`docker-compose.yml` references `ghcr.io/danielvolz/medassist-ng-backend:latest` and `ghcr.io/danielvolz/medassist-ng-frontend:latest`) +- Backend persistent filesystem for SQLite/data in mounted `./data` (`docker-compose.yml`, DB path resolver in `backend/src/db/path-utils.ts`) + +--- + +*Stack analysis: 2026-04-30* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..b569cc2 --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,138 @@ +# Codebase Structure + +**Analysis Date:** 2026-04-30 + +## Directory Layout + +``` +medassist/ +├── frontend/ # React + Vite SPA, UI, hooks, page routes, frontend tests +├── backend/ # Fastify API, domain services, DB schema/migrations, backend tests +├── backend/drizzle/ # SQL migration files + drizzle meta journal +├── docs/ # Product/ops docs and screenshots +├── doku/ # Local-only working notes and reports (ignored) +├── .github/ # CI workflows, agents, local skill/runtime metadata +├── .planning/codebase/ # Generated codebase mapping documents +├── data/ # Runtime/local SQLite backups and scheduler files +└── package.json # Root workspace scripts for lint orchestration +``` + +## Directory Purposes + +**frontend/src:** +- Purpose: Product UI and client-side app logic. +- Contains: `pages/`, `components/`, `context/`, `hooks/`, `ui/`, `utils/`, `i18n/`, `test/`. +- Key files: `frontend/src/main.tsx`, `frontend/src/App.tsx`, `frontend/src/context/AppContext.tsx`. + +**backend/src:** +- Purpose: HTTP API, auth, domain services, and persistence access. +- Contains: `routes/`, `services/`, `plugins/`, `db/`, `utils/`, `test/`. +- Key files: `backend/src/index.ts`, `backend/src/routes/medications.ts`, `backend/src/routes/planner.ts`, `backend/src/db/client.ts`. + +**backend/drizzle:** +- Purpose: SQL migration history for SQLite compatibility. +- Contains: numbered migration files and `meta/_journal.json`. +- Key files: `backend/drizzle/0000_init.sql`, `backend/drizzle/0014_add_user_settings_timezone.sql`. + +**frontend/e2e:** +- Purpose: Playwright end-to-end scenarios and fixtures. +- Contains: browser tests + auth fixtures. +- Key files: `frontend/e2e/fixtures/` and spec files under `frontend/e2e/`. + +**docs + doku:** +- Purpose: formal docs (`docs/`) and local-only work tracking (`doku/`). +- Contains: behavior/spec docs, screenshots, local report/memory logs. +- Key files: `docs/TECH_STACK.md`, `doku/memory_notes.md`, `doku/report.md`. + +## Key File Locations + +**Entry Points:** +- `frontend/src/main.tsx`: Browser bootstrap; mounts providers and router. +- `frontend/src/App.tsx`: Route graph and global modal/shell orchestration. +- `backend/src/index.ts`: Fastify app setup + startup runtime. + +**Configuration:** +- `frontend/vite.config.ts`: Dev server, `/api` proxy rewrite, build-time constants. +- `frontend/vitest.config.ts`: Frontend unit test config. +- `backend/vitest.config.ts`: Backend unit/integration test config. +- `backend/drizzle.config.ts`: Drizzle migration configuration. +- `.gitignore`: Local-only/generated path policy (including `.planning/`, `doku/`, `data/`, coverage/test artifacts). + +**Core Logic:** +- `backend/src/routes/`: API contracts and request handlers. +- `backend/src/services/`: Scheduler, notifications, medication helpers. +- `backend/src/db/schema.ts`: Source-of-truth table definitions. +- `frontend/src/context/`: Shared app orchestration state. +- `frontend/src/pages/`: Screen-level composition. + +**Testing:** +- `frontend/src/test/`: Frontend unit/component tests. +- `frontend/e2e/`: Playwright E2E tests. +- `backend/src/test/`: Backend route/service/db tests. + +## Naming Conventions + +**Files:** +- React components/pages use PascalCase: `frontend/src/pages/MedicationsPage.tsx`, `frontend/src/components/MedDetailModal.tsx`. +- Hooks use `use*` naming: `frontend/src/hooks/useMedications.ts`, `frontend/src/hooks/useSettings.ts`. +- Backend routes/services use kebab-case: `backend/src/routes/medication-enrichment.ts`, `backend/src/services/reminder-scheduler.ts`. +- Migrations use numbered descriptive names: `backend/drizzle/0012_add_api_keys_and_package_amount_columns.sql`. + +**Directories:** +- Feature/layer folders are lowercase: `frontend/src/context`, `backend/src/services`. +- Test directories stay colocated by runtime side (`frontend/src/test`, `backend/src/test`). + +## Where to Add New Code + +**New Feature:** +- Primary code: + - Frontend UI route/screen: `frontend/src/pages/` (compose from existing `components/`, `hooks/`, `ui/`). + - Backend endpoint: `backend/src/routes/` + matching domain logic in `backend/src/services/`. + - Persistence additions: `backend/src/db/schema.ts` plus migration updates in `backend/src/db/client.ts` and `backend/drizzle/`. +- Tests: + - Frontend unit/component: `frontend/src/test/`. + - Backend unit/integration: `backend/src/test/`. + - E2E flow: `frontend/e2e/`. + +**New Component/Module:** +- Implementation: + - Shared UI primitive/layout: `frontend/src/ui/`. + - Domain-specific UI component: `frontend/src/components/` (or nested feature folder). + - Backend reusable domain behavior: `backend/src/services/`. + +**Utilities:** +- Shared helpers: + - Frontend: `frontend/src/utils/`. + - Backend: `backend/src/utils/`. + - DB-specific helpers: `backend/src/db/` focused utility modules. + +## Special Directories + +**frontend/dist, backend/dist:** +- Purpose: build output artifacts. +- Generated: Yes. +- Committed: No (`dist/` ignored in `.gitignore`). + +**frontend/playwright-report, frontend/test-results, frontend/coverage, backend/coverage:** +- Purpose: test artifacts/reports. +- Generated: Yes. +- Committed: No (ignored in `.gitignore`). + +**data/:** +- Purpose: runtime/local DB, reminder state, scheduler locks. +- Generated: Yes. +- Committed: No (`data/` ignored in `.gitignore`). + +**doku/:** +- Purpose: local work memory/reporting and internal notes. +- Generated: Mixed (manual local notes + artifacts). +- Committed: No (`doku/` ignored in `.gitignore`). + +**.planning/codebase/:** +- Purpose: generated architecture/stack/convention/concern maps for GSD planning/execution. +- Generated: Yes. +- Committed: No (`.planning/` ignored by policy in this workspace). + +--- + +*Structure analysis: 2026-04-30* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..68b80ab --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,203 @@ +# Testing Patterns + +**Analysis Date:** 2026-04-30 + +## Test Framework + +**Runner:** +- Vitest 4.x for unit/integration tests in both packages: + - Frontend config: `frontend/vitest.config.ts` + - Backend config: `backend/vitest.config.ts` +- Config evidence: + - Frontend uses `environment: 'jsdom'` with React setup file `frontend/src/test/setup.ts`. + - Backend uses `environment: 'node'` with setup file `backend/src/test/setup.ts`. + +**Assertion Library:** +- Vitest `expect`. +- Frontend extends DOM assertions via `@testing-library/jest-dom` in `frontend/src/test/setup.ts`. + +**Run Commands:** +```bash +cd frontend && npm test # Watch/unit tests +cd frontend && npm run test:run # CI-style frontend run +cd frontend && npm run test:coverage # Frontend coverage +cd backend && npm test # Watch/unit tests +cd backend && npm run test:run # CI-style backend run +cd backend && npm run test:coverage # Backend coverage +cd frontend && npm run test:e2e # Stable Playwright suite +cd frontend && npm run test:e2e:all # Cross-browser Playwright suite +``` + +## Test File Organization + +**Location:** +- Backend unit/integration tests are in `backend/src/test/*.test.ts`. +- Frontend unit/component/hook/context tests are in `frontend/src/test/**`. +- Browser E2E tests are in `frontend/e2e/*.spec.ts`. + +**Naming:** +- Unit/integration: `*.test.ts` or `*.test.tsx` (for example `backend/src/test/routes-real.test.ts`, `frontend/src/test/components/MedicationDialogs.test.tsx`). +- E2E: `*.spec.ts` (for example `frontend/e2e/medication-edit.spec.ts`). + +**Structure:** +``` +backend/src/test/ + setup.ts + *.test.ts + +frontend/src/test/ + setup.ts + App.test.tsx + components/*.test.tsx + context/*.test.tsx + hooks/*.test.ts + pages/*.test.tsx + utils/*.test.ts + +frontend/e2e/ + auth.setup.ts + fixtures/index.ts + *.spec.ts +``` + +## Test Structure + +**Suite Organization:** +```typescript +describe("Feature Area", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("handles expected behavior", async () => { + // arrange + // act + // assert + expect(result).toEqual(expected); + }); +}); +``` +Pattern evidence: `frontend/src/test/components/MobileEditModal.test.tsx`, `backend/src/test/planner.test.ts`. + +**Patterns:** +- Setup pattern: + - Frontend centralizes browser mocks in `frontend/src/test/setup.ts` (fetch, localStorage, clipboard, history, i18n). + - Backend provides reusable app/database factories in `backend/src/test/setup.ts` (`buildTestApp`, `createTestUser`, `createTestMedication`). +- Teardown pattern: + - `afterAll` closes Fastify app and DB clients (`backend/src/test/planner.test.ts`, `backend/src/test/integration.test.ts`). +- Assertion pattern: + - Route tests assert both HTTP status and response body (`backend/src/test/routes-real.test.ts`). + - UI tests assert presence and behavior via Testing Library role/test-id queries (`frontend/src/test/components/MedicationDialogs.test.tsx`). + +## Mocking + +**Framework:** +- Vitest mocks (`vi.mock`, `vi.fn`, `vi.hoisted`, `vi.stubGlobal`). + +**Patterns:** +```typescript +const { testClient, testDb } = vi.hoisted(() => { + const client = createClient({ url: ":memory:" }); + const db = drizzle(client); + return { testClient: client, testDb: db }; +}); + +vi.mock("../db/client.js", () => ({ + db: testDb, + migrationsReady: Promise.resolve(), +})); +``` +Pattern evidence: `backend/src/test/integration.test.ts`, `backend/src/test/routes-real.test.ts`. + +```typescript +vi.mock("../../components/ConfirmModal", () => ({ + ConfirmModal: ({ onConfirm }) => , +})); +``` +Pattern evidence: `frontend/src/test/components/MedicationDialogs.test.tsx`. + +**What to Mock:** +- External side effects and infrastructure boundaries: SMTP/nodemailer, fetch network calls, auth/plugin env modules, browser APIs. +- Component dependencies in focused unit tests (replace heavy children with stubs). + +**What NOT to Mock:** +- Core business behavior under direct test (route handlers in route tests, hook logic in hook tests, E2E API + UI flow in Playwright). + +## Fixtures and Factories + +**Test Data:** +```typescript +const userId = await createTestUser(client, { username: "testuser" }); +const medId = await createTestMedication(client, { userId, name: "Test Medication" }); +``` +Pattern evidence: `backend/src/test/setup.ts`, used by `backend/src/test/medications.test.ts`. + +```typescript +export const test = base.extend({ + page: async ({ page }, use) => { + await applyVideoSafetyMode(page); + await setupAuthMeMock(page); + await use(page); + }, +}); +``` +Pattern evidence: `frontend/e2e/fixtures/index.ts`. + +**Location:** +- Backend factories/utilities: `backend/src/test/setup.ts`. +- Frontend E2E shared fixtures and API helpers: `frontend/e2e/fixtures/index.ts`. + +## Coverage + +**Requirements:** +- Frontend global thresholds in `frontend/vitest.config.ts`: lines/functions/branches/statements = 75. +- Backend global thresholds in `backend/vitest.config.ts`: lines 60, functions 65, branches 50, statements 60. + +**View Coverage:** +```bash +cd frontend && npm run test:coverage +cd backend && npm run test:coverage +``` + +## Test Types + +**Unit Tests:** +- Component/hook/utils tests in `frontend/src/test/**`. +- Utility/service route-unit style tests in `backend/src/test/*.test.ts`. + +**Integration Tests:** +- Backend route interaction and multi-route behavior tests in files like: + - `backend/src/test/integration.test.ts` + - `backend/src/test/routes-real.test.ts` + +**E2E Tests:** +- Playwright used with setup project and browser projects (`frontend/playwright.base.config.ts`). +- Auth/session and API seeding helpers in `frontend/e2e/fixtures/index.ts`. + +## Common Patterns + +**Async Testing:** +```typescript +await waitFor(() => { + expect(mockFn).toHaveBeenCalledTimes(1); +}); +``` +Pattern evidence: `frontend/src/test/context/AppContext.test.tsx`. + +```typescript +const response = await app.inject({ method: "GET", url: "/settings" }); +expect(response.statusCode).toBe(200); +``` +Pattern evidence: `backend/src/test/routes-real.test.ts`. + +**Error Testing:** +```typescript +const response = await app.inject({ method: "POST", url: "/planner/send-email", payload: { rows: [] } }); +expect(response.statusCode).toBe(400); +expect(response.json()).toEqual({ error: "Missing planner data" }); +``` +Pattern evidence: `backend/src/test/planner.test.ts`. + +--- + +*Testing analysis: 2026-04-30*