You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ai_synth/docs/superpowers/plans/2026-03-22-test-coverage-do...

18 KiB

Test Coverage & Documentation Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Close the quality gaps identified in the tech lead assessment: add frontend page tests, JSDoc documentation, backend auth middleware tests, and E2E tests.

Architecture: Frontend tests use the existing Vitest + @solidjs/testing-library + manual fetch mocking pattern (no MSW — the existing vi.fn() pattern works well). Backend tests use inline #[cfg(test)] modules. E2E tests use Playwright against a Docker stack.

Tech Stack: Vitest, @solidjs/testing-library, Playwright, Rust #[cfg(test)], Docker

Spec: docs/superpowers/specs/2026-03-22-test-coverage-documentation-design.md

Note: The spec called for schema.rs (6 tests) and token.rs (4 tests), but exploration revealed these already have 7 and 8 tests respectively. Only middleware/auth.rs (0 tests) remains as a backend gap.


File Map

New Files

  • frontend/src/__tests__/test-utils.tsx — shared renderWithProviders() helper
  • frontend/src/__tests__/pages/settings.test.tsx — Settings page tests
  • frontend/src/__tests__/pages/home.test.tsx — Home page tests
  • frontend/src/__tests__/pages/sources.test.tsx — Sources page tests
  • frontend/src/__tests__/pages/login.test.tsx — Login page tests
  • frontend/src/__tests__/pages/register.test.tsx — Register page tests
  • frontend/src/__tests__/pages/generate.test.tsx — GenerateSynthesis page tests
  • e2e/playwright.config.ts — Playwright config
  • e2e/package.json — E2E dependencies
  • e2e/docker-compose.test.yml — test Docker stack
  • e2e/seed.ts — DB seed script
  • e2e/helpers/auth.ts — auth helpers
  • e2e/tests/*.spec.ts — 5 E2E test files

Modified Files (documentation only — no logic changes)

  • frontend/src/api/client.ts — JSDoc
  • frontend/src/api/auth.ts — JSDoc
  • frontend/src/api/settings.ts — JSDoc
  • frontend/src/api/sources.ts — JSDoc
  • frontend/src/api/syntheses.ts — JSDoc
  • frontend/src/api/admin.ts — JSDoc
  • frontend/src/api/config.ts — JSDoc
  • frontend/src/api/apiKeys.ts — JSDoc
  • frontend/src/pages/*.tsx (all 11 pages) — JSDoc
  • frontend/src/components/*.tsx (all 10 components) — JSDoc
  • frontend/src/utils/*.ts (all 3 utils) — JSDoc
  • frontend/src/contexts/AuthContext.tsx — JSDoc
  • backend/src/middleware/auth.rs — unit tests added

Task 1: Backend Auth Middleware Unit Tests

Files:

  • Modify: backend/src/middleware/auth.rs

  • Step 1: Read the current cookie extraction logic

Read /Users/oabrivard/Projects/rust/ai_synth/backend/src/middleware/auth.rs lines 34-81 to understand the AuthUser::from_request_parts() implementation. The cookie extraction is at lines 41-51.

  • Step 2: Write unit tests

Add a #[cfg(test)] mod tests block at the bottom of auth.rs. Since the full from_request_parts requires a DB connection, test the cookie extraction logic as a standalone helper function. Extract the cookie parsing into a testable function if it isn't already:

/// Extract session token from Cookie header value.
/// Returns None if the session cookie is not found.
fn extract_session_token(cookie_header: &str, cookie_name: &str) -> Option<String> {
    cookie_header
        .split(';')
        .map(|s| s.trim())
        .find(|s| s.starts_with(&format!("{}=", cookie_name)))
        .and_then(|s| s.strip_prefix(&format!("{}=", cookie_name)))
        .map(|s| s.to_string())
}

#[cfg(test)]
mod tests {
    use super::*;

    const COOKIE_NAME: &str = "session";

    #[test]
    fn test_valid_session_cookie_extracted() {
        let header = "session=abc123def";
        let result = extract_session_token(header, COOKIE_NAME);
        assert_eq!(result, Some("abc123def".into()));
    }

    #[test]
    fn test_missing_session_cookie_returns_none() {
        let header = "other=value; another=thing";
        let result = extract_session_token(header, COOKIE_NAME);
        assert_eq!(result, None);
    }

    #[test]
    fn test_multiple_cookies_correct_one_picked() {
        let header = "theme=dark; session=mytoken123; lang=fr";
        let result = extract_session_token(header, COOKIE_NAME);
        assert_eq!(result, Some("mytoken123".into()));
    }

    #[test]
    fn test_cookie_with_whitespace_trimmed() {
        let header = "  session=abc123  ; other=val  ";
        let result = extract_session_token(header, COOKIE_NAME);
        assert_eq!(result, Some("abc123".into()));
    }

    #[test]
    fn test_empty_cookie_header_returns_none() {
        let header = "";
        let result = extract_session_token(header, COOKIE_NAME);
        assert_eq!(result, None);
    }
}

If the current code already has a separate extraction function, add the tests to it. If the extraction is inline in from_request_parts, extract it first as shown above, then use it in from_request_parts.

  • Step 3: Run tests

Run: cd backend && cargo test --lib middleware Expected: 5 new tests PASS

  • Step 4: Commit
cd /Users/oabrivard/Projects/rust/ai_synth
git add backend/src/middleware/auth.rs
git commit -m "test: add unit tests for auth middleware cookie extraction"

Task 2: Frontend Test Utilities

Files:

  • Create: frontend/src/__tests__/test-utils.tsx

  • Step 1: Create shared test utilities

import { render } from '@solidjs/testing-library';
import { Router } from '@solidjs/router';
import type { ParentComponent } from 'solid-js';
import { I18nProvider } from '~/i18n';
import { ToastProvider } from '~/components/ui/Toast';

/**
 * Wraps a component in all required providers for testing.
 * Includes: Router, I18nProvider, ToastProvider.
 * AuthContext is NOT included — mock it per-test as needed.
 */
export function renderWithProviders(ui: () => any) {
  return render(() => (
    <Router>
      <I18nProvider locale="fr">
        <ToastProvider>
          {ui()}
        </ToastProvider>
      </I18nProvider>
    </Router>
  ));
}

/**
 * Creates a mock fetch that returns the given response for any call.
 * Resets between tests via afterEach.
 */
export function mockFetch(response: any, status = 200) {
  const fn = vi.fn().mockResolvedValue({
    ok: status >= 200 && status < 300,
    status,
    json: () => Promise.resolve(response),
    text: () => Promise.resolve(JSON.stringify(response)),
    headers: new Headers({ 'content-type': 'application/json' }),
  });
  globalThis.fetch = fn;
  return fn;
}

/**
 * Creates a mock fetch that returns different responses per URL pattern.
 */
export function mockFetchRoutes(routes: Record<string, { body: any; status?: number }>) {
  const fn = vi.fn().mockImplementation((url: string) => {
    const match = Object.entries(routes).find(([pattern]) => url.includes(pattern));
    const { body, status } = match?.[1] ?? { body: {}, status: 404 };
    return Promise.resolve({
      ok: (status ?? 200) >= 200 && (status ?? 200) < 300,
      status: status ?? 200,
      json: () => Promise.resolve(body),
      text: () => Promise.resolve(JSON.stringify(body)),
      headers: new Headers({ 'content-type': 'application/json' }),
    });
  });
  globalThis.fetch = fn;
  return fn;
}
  • Step 2: Verify it compiles

Run: cd frontend && npx tsc --noEmit Expected: no errors

  • Step 3: Commit
git add frontend/src/__tests__/test-utils.tsx
git commit -m "test: add shared test utilities (renderWithProviders, mockFetch)"

Task 3: Frontend Page Tests — Home

Files:

  • Create: frontend/src/__tests__/pages/home.test.tsx

  • Step 1: Read Home.tsx

Read /Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/Home.tsx to understand the component structure, signals, and API calls.

  • Step 2: Write tests

Write ~7 tests covering: list rendering, empty state, new synthesis link, delete double-click flow, auto-cancel, in-progress banner. Mock ~/api/syntheses module. Use renderWithProviders from test-utils. Use vi.mock() for the API module and waitFor for async assertions.

  • Step 3: Run tests

Run: cd frontend && npx vitest run src/__tests__/pages/home.test.tsx Expected: all PASS

  • Step 4: Commit
git add frontend/src/__tests__/pages/home.test.tsx
git commit -m "test: add Home page interaction tests"

Task 4: Frontend Page Tests — Settings

Files:

  • Create: frontend/src/__tests__/pages/settings.test.tsx

  • Step 1: Read Settings.tsx

Read /Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/Settings.tsx — understand provider/model selection, rate limits, export/import, save flow.

  • Step 2: Write tests

Write ~10 tests covering: form rendering with API data, save calls PUT, provider dropdown, dual model dropdowns, rate limit inputs, rate limit reset, export triggers download, import populates form. Mock ~/api/settings, ~/api/config, ~/api/apiKeys.

  • Step 3: Run tests

Run: cd frontend && npx vitest run src/__tests__/pages/settings.test.tsx Expected: all PASS

  • Step 4: Commit
git add frontend/src/__tests__/pages/settings.test.tsx
git commit -m "test: add Settings page interaction tests"

Task 5: Frontend Page Tests — Sources

Files:

  • Create: frontend/src/__tests__/pages/sources.test.tsx

  • Step 1: Read Sources.tsx

Read /Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/Sources.tsx.

  • Step 2: Write tests

Write ~8 tests covering: list rendering, empty state, add form validation, add form submit, delete confirmation, bulk import, CSV export trigger, CSV import trigger. Mock ~/api/sources.

  • Step 3: Run and commit

Run: cd frontend && npx vitest run src/__tests__/pages/sources.test.tsx

git add frontend/src/__tests__/pages/sources.test.tsx
git commit -m "test: add Sources page interaction tests"

Task 6: Frontend Page Tests — Login + Register

Files:

  • Create: frontend/src/__tests__/pages/login.test.tsx

  • Create: frontend/src/__tests__/pages/register.test.tsx

  • Step 1: Read Login.tsx and Register.tsx

Read both files to understand Turnstile integration, form submission, success/error states, resend cooldown.

  • Step 2: Write Login tests (~4 tests)

Renders email input + submit, submit calls API, success shows "check inbox", error shows message. Mock ~/api/auth. Stub window.turnstile in setup.

  • Step 3: Write Register tests (~4 tests)

Renders email + display name + submit, submit calls API, success shows confirmation, error shows message.

  • Step 4: Run and commit

Run: cd frontend && npx vitest run src/__tests__/pages/login.test.tsx src/__tests__/pages/register.test.tsx

git add frontend/src/__tests__/pages/login.test.tsx frontend/src/__tests__/pages/register.test.tsx
git commit -m "test: add Login and Register page interaction tests"

Task 7: Frontend Page Tests — GenerateSynthesis

Files:

  • Create: frontend/src/__tests__/pages/generate.test.tsx

  • Step 1: Read GenerateSynthesis.tsx

Read to understand SSE state machine, step progression, provider display, completion redirect.

  • Step 2: Write tests (~6 tests)

Renders launch button with settings info, launch calls POST, progress bar updates from mocked SSE events, step checklist shows states, completion triggers redirect, error shows retry. Mock ~/api/syntheses, ~/api/settings, ~/api/config. Stub EventSource globally.

  • Step 3: Run and commit

Run: cd frontend && npx vitest run src/__tests__/pages/generate.test.tsx

git add frontend/src/__tests__/pages/generate.test.tsx
git commit -m "test: add GenerateSynthesis page interaction tests"

Task 8: Frontend JSDoc Documentation — API Layer

Files:

  • Modify: frontend/src/api/client.ts

  • Modify: frontend/src/api/auth.ts

  • Modify: frontend/src/api/settings.ts

  • Modify: frontend/src/api/sources.ts

  • Modify: frontend/src/api/syntheses.ts

  • Modify: frontend/src/api/admin.ts

  • Modify: frontend/src/api/config.ts

  • Modify: frontend/src/api/apiKeys.ts

  • Step 1: Add JSDoc to api/client.ts

Document: class purpose ("Fetch wrapper for authenticated API calls"), CSRF strategy ("Adds X-Requested-With header to all requests"), credential handling ("Uses same-origin credentials for session cookies"), 401 redirect ("Redirects to /login on 401 Unauthorized"), each public method.

  • Step 2: Add JSDoc to all other API modules

Brief /** ... */ on each exported function describing what endpoint it calls and what it returns. Example:

/** POST /auth/register — Create account with email + Turnstile token. */
  • Step 3: Verify no code changes

Run: cd frontend && npx tsc --noEmit && npx vitest run Expected: no errors, all tests pass (docs only)

  • Step 4: Commit
git add frontend/src/api/
git commit -m "docs: add JSDoc to all frontend API modules"

Task 9: Frontend JSDoc Documentation — Pages + Components

Files:

  • Modify: all files in frontend/src/pages/, frontend/src/components/, frontend/src/utils/, frontend/src/contexts/

  • Step 1: Document pages

Add JSDoc to each page component. Focus on:

  • Settings.tsx — export/import logic, provider auto-detection, rate limit null handling, dual model state

  • GenerateSynthesis.tsx — SSE state machine, step progression, reconnection

  • Home.tsx — delete confirmation timer pattern

  • Sources.tsx — bulk import parsing, CSV flow

  • Brief JSDoc on all other pages

  • Step 2: Document components

Add JSDoc to each component. Focus on:

  • ApiKeyManager.tsx — key CRUD flow, show/hide toggle, test button

  • Turnstile.tsx — widget lifecycle, polling initialization, cleanup

  • Brief JSDoc on all other components including ui/

  • Step 3: Document utilities and context

  • utils/sse.ts — reconnection backoff logic, event parsing, cleanup

  • utils/dates.ts — locale configuration

  • utils/providers.ts — capability detection

  • contexts/AuthContext.tsx — session check on mount, isAdmin derived signal

  • Step 4: Verify and commit

Run: cd frontend && npx tsc --noEmit && npx vitest run

git add frontend/src/pages/ frontend/src/components/ frontend/src/utils/ frontend/src/contexts/
git commit -m "docs: add JSDoc to all frontend pages, components, utilities"

Task 10: E2E Infrastructure

Files:

  • Create: e2e/package.json

  • Create: e2e/playwright.config.ts

  • Create: e2e/docker-compose.test.yml

  • Create: e2e/seed.ts

  • Create: e2e/helpers/auth.ts

  • Step 1: Create e2e/package.json

{
  "name": "ai-synth-e2e",
  "private": true,
  "scripts": {
    "test": "playwright test",
    "test:ui": "playwright test --ui",
    "seed": "npx tsx seed.ts"
  },
  "devDependencies": {
    "@playwright/test": "^1.50.0",
    "pg": "^8.13.0",
    "tsx": "^4.21.0"
  }
}
  • Step 2: Create playwright.config.ts

Base URL http://localhost:8080, 30s timeout, 1 retry, screenshots on failure, single worker (sequential for DB state).

  • Step 3: Create docker-compose.test.yml

Extends main docker-compose.yml. Uses test Postgres with fresh DB. Sets test environment variables (TEST turnstile/resend keys). Exposes on port 8080.

  • Step 4: Create seed.ts

Connects to Postgres. Creates admin user (email_verified=true, role=admin) with a known session token. Creates regular user with known session token. Both tokens stored in known-hashes table for cookie injection.

  • Step 5: Create helpers/auth.ts

loginAsAdmin(page) — sets session cookie. loginAsUser(page) — sets session cookie. registerAndVerify(page, email) — inserts magic link token in DB, navigates to verify URL.

  • Step 6: Install and verify
cd e2e && npm install && npx playwright install chromium
  • Step 7: Commit
git add e2e/
git commit -m "test: add E2E infrastructure (Playwright + Docker + seed)"

Task 11: E2E Tests — 5 Flows

Files:

  • Create: e2e/tests/registration.spec.ts

  • Create: e2e/tests/admin-providers.spec.ts

  • Create: e2e/tests/settings.spec.ts

  • Create: e2e/tests/sources.spec.ts

  • Create: e2e/tests/settings-export.spec.ts

  • Step 1: Write registration flow

Navigate to /register, fill email, submit, extract token from DB, visit verify URL, assert redirected to Home with user email in navbar.

  • Step 2: Write admin provider config flow

Login as admin, navigate to /admin/providers, enable provider, add model, save, verify API returns updated config.

  • Step 3: Write settings config flow

Login as user, go to /settings, change theme + select provider/models, save, reload, assert values persisted.

  • Step 4: Write sources CRUD flow

Login, add source, verify in list, bulk import 3, verify count = 4, delete one, verify count = 3, export CSV.

  • Step 5: Write settings export/import flow

Login, configure settings, export JSON, change settings to different values, save, import exported JSON, save, reload, assert original values restored.

  • Step 6: Run E2E (manual — requires Docker)
cd e2e
docker compose -f docker-compose.test.yml up -d --wait
npm run seed
npm test
docker compose -f docker-compose.test.yml down -v
  • Step 7: Commit
git add e2e/tests/
git commit -m "test: add 5 E2E test flows (registration, admin, settings, sources, export)"

Task 12: Final Verification

  • Step 1: Full backend check

Run: cd backend && cargo check && cargo test --lib Expected: compiles, all tests pass

  • Step 2: Full frontend check

Run: cd frontend && npx tsc --noEmit && npx vitest run Expected: type-checks, all tests pass (original 103 + ~39 new page tests)

  • Step 3: Verify frontend build

Run: cd frontend && npx vite build Expected: production build succeeds

  • Step 4: Final commit
git add -A
git commit -m "test: complete test coverage and documentation improvements"