Ctrl K
ComunidadGuías…

AGENTS/Testing.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Testing Guidance

This document defines how to split test coverage between service tests and API integration tests so we avoid duplicated effort and keep runtime under control.

## Goals

- Keep confidence high without duplicating the same business-rule matrix in multiple layers.
- Keep API integration suites focused on HTTP contract and runtime wiring behaviour.
- Keep business logic and edge-case matrices focused in service tests.

## Browser Test Principles

Browser tests protect critical user journeys; they are not the default place to test business-rule permutations.

### Structure by Responsibility

- Specs describe the user journey and expected outcome.
- Fixtures provide authenticated contexts, reusable capabilities, and isolated test data.
- UI drivers encapsulate repeated interactions and stable locators.
- API helpers arrange prerequisites quickly through supported application APIs.
- Runner and configuration code owns temporary storage, ports, server lifecycle, profiles, timeouts, and diagnostic artifacts.

Keep browser-test code organised by those responsibilities. Do not mix environment orchestration, API setup, detailed selectors, and outcome assertions into each spec.

### Maintainability Rules

1. Keep specs readable.
   - A spec should read as a short user journey.
   - Do not expose API payloads, server orchestration, or detailed selectors in the scenario.

2. Use small workflow objects.
   - Introduce focused drivers such as `BoardPage` or `CardEditor` only where interactions are reused or complex.
   - Do not mirror the Vue component tree, create a universal base page, or build page-object inheritance hierarchies.
   - Keep meaningful outcome assertions visible in the spec.

3. Prefer semantic locators.
   - Prefer roles, labels, and visible text.
   - Use an existing stable semantic attribute where appropriate.
   - Add a test id only when no meaningful accessible selector exists.
   - Do not couple tests to styling classes, DOM ancestry, generated ids, or positional selectors.

4. Keep every test independent.
   - Tests must run individually and in any order.
   - Use fresh browser contexts and unique, readable test data.
   - Do not depend on state created by another test.
   - Use a disposable database for the run and remove the complete environment afterwards instead of relying on fragile per-test cleanup.

5. Use fixtures for capabilities.
   - Good fixtures provide capabilities such as an authenticated page, test board, second browser context, or API client.
   - Scenario-specific arrangements remain visible in the spec.
   - Avoid fixtures whose names hide an entire preconstructed scenario.

6. Wait for behaviour, never time.
   - Use Playwright's web-first assertions, URLs, API responses, and observable application state.
   - Do not use arbitrary sleeps to stabilise tests.

7. Maintain a strict smoke budget.
   - A test belongs in the GitHub smoke profile only when its failure materially undermines confidence in everyday BoardOil use.
   - Broader coverage belongs in the Jenkins regression profile.

8. Treat flakiness as a defect.
   - Begin with zero retries.
   - Do not hide flakes with blanket retries or larger global timeouts.
   - Diagnose failures with traces and fix the cause.
   - Any temporarily disabled browser test must reference a board bug.

9. Test at the lowest appropriate layer.
   - Business-rule matrices, API authorization permutations, and store edge cases remain in backend or Vitest suites.
   - Browser tests are reserved for browser behaviour and multi-layer composition such as focus/caret handling, cookies, routing, drag-and-drop, canvas, realtime, and critical user journeys.

10. Make failures readable.
    - Use concise `test.step` phases for longer journeys.
    - Retain actionable traces and screenshots on failure.
    - Prefer behavioural assertions over broad visual snapshots.

### Running the Browser Smoke Profile

- Install Chromium once from `BoardOil.Web` with `npx playwright install chromium`.
- Run the profile from `BoardOil.Web` with `npm run test:e2e:smoke`.
- Name smoke specs `*.smoke.spec.ts`; the smoke command selects only that suffix.
- Pass Playwright CLI options after `--` when narrowing or repeating a run.
- The runner creates its own temporary SQLite database, image directory, API port, and frontend port, then removes the temporary environment.
- Browser tests remain separate from `test-fast.mjs`, `test-full.mjs`, and the normal Vitest run.

### Running the Browser Regression Profile

- Run every browser spec from `BoardOil.Web` with `npm run test:e2e:regression`.
- Princess Posse Jenkins owns the scheduled regression run; GitHub Actions continues to run only the smoke profile.
- Name broader or longer-running specs `*.regression.spec.ts` so their intended ownership is obvious.
- Set `BOARDOIL_E2E_JUNIT_OUTPUT` when the caller needs a JUnit XML report; normal local and GitHub smoke runs retain the list reporter only.
- Slow clean-build hosts may set `BOARDOIL_E2E_STARTUP_TIMEOUT_MS` to a positive integer; the default API/frontend readiness deadline remains 60000 ms.

### Running Browser Tests Against an External Runtime

- Use `npm run test:e2e:smoke:external` for the smoke profile or `npm run test:e2e:regression:external` for the complete regression profile.
- External mode requires `BOARDOIL_E2E_BASE_URL` to be an absolute HTTP(S) URL. It does not restore or start the API, start Vite, reserve local ports, create SQLite/image storage, or stop the supplied runtime.
- Supply a fresh BoardOil data volume. The existing Playwright fixtures perform initial-admin setup and authenticate subsequent tests against that disposable runtime.
- `BOARDOIL_E2E_JUNIT_OUTPUT`, Playwright traces/screenshots, command exit status, signal handling, and Playwright CLI arguments passed after `--` behave the same as managed-runtime runs.
- The caller owns image selection, container lifecycle, isolation, cleanup, resource limits, and diagnostics.

Princess Posse Jenkins invocation contract (run from `BoardOil.Web` after checking out the trusted source revision embedded in the selected image):

```sh
BOARDOIL_E2E_BASE_URL=http://127.0.0.1:8080 \
BOARDOIL_E2E_JUNIT_OUTPUT=test-results/playwright/junit.xml \
npm run test:e2e:regression:external -- --project=chromium
```

## Local Fast Loop

Use repository scripts to keep local feedback fast and consistent:

- `node scripts/test-fast.mjs`
  - default mode is changed-area detection from git diff
  - runs only impacted fast suites/checks (API, Dev orchestrator, Services, Web)
  - excludes slow API integration classes by design
  - supports suite overrides: `--api-only`, `--dev-only`, `--services-only`, `--web-only`, `--backend-only`, `--full`
  - supports output overrides: `--compact`, `--verbose`
- `node scripts/test-full.mjs`
  - CI-like full local run (backend restore/build/tests + web check/test)
  - supports suite overrides: `--backend-only`, `--web-only`
  - supports output overrides: `--compact`, `--verbose`
  - supports `--profile-api` to print the ten slowest API test classes by cumulative test time

Convenience wrappers:
- `scripts/test-fast.sh`, `scripts/test-full.sh`, `scripts/test-fast.ps1`, and `scripts/test-full.ps1` delegate to the `.mjs` scripts.

Recommended flow:

1. During implementation, run `node scripts/test-fast.mjs`.
2. If you changed only one area and want an explicit lane, use `node scripts/test-fast.mjs --api-only`, `--dev-only`, `--services-only`, `--web-only`, or `--backend-only`.
3. Before pushing risky backend/API auth/MCP/migration changes, run `node scripts/test-full.mjs --backend-only`.
4. Before pushing mixed backend+frontend changes, run full `node scripts/test-full.mjs`.
5. Avoid ad-hoc direct test commands during normal iteration; use the repository scripts so behavior stays consistent.

These commands are validation tools, not mechanical source-control gates. When deciding whether to pause an explicitly requested commit or push, assess whether the tests that have not run would meaningfully exercise the actual changes. Raise a missing-validation concern only when that coverage could realistically reveal a defect; otherwise proceed with the requested source-control operation and report the validation that was performed.

## Test Output Modes

The repository test scripts default to compact output when they detect an agent or CI environment (`CI`, `GITHUB_ACTIONS`, `CODEX_CI`, `CODEX_THREAD_ID`, or `CLAUDECODE`). Compact mode:

- hides successful restore/build/check command output
- prints concise pass summaries for xUnit and Vitest test runs
- prints total and named build/test phase timings after a successful script run
- replays full stdout/stderr for any failed command before reporting the failed suite
- passes `--progress off --ansi off` to xUnit test applications
- lets Vitest use its `agent` reporter with `silent: 'passed-only'`
- suppresses Vite's successful build asset table in agent/CI runs while preserving warnings and errors

Use `--verbose` or `BOARDOIL_TEST_OUTPUT=verbose` when investigating flaky tests, slow tests, or build output details. Use `--compact` or `BOARDOIL_TEST_OUTPUT=compact` to force low-noise output in a normal local shell.

`npm test` and the Vite build wrapper in `BoardOil.Web` also detect these environment variables, so direct web test/build runs stay compact in agent/CI environments. Prefer the repository scripts for normal iteration because they also choose the right backend/frontend lanes.

## Ownership by Layer

- Service tests (`BoardOil.Services.Tests`) own:
  - business rules and invariants
  - validation matrices and edge-case permutations
  - ordering/reassignment semantics
  - import/export/archive behaviour details
  - authorization decision logic at service boundary
- API integration tests (`BoardOil.Api.Tests`) own:
  - endpoint contract shape (route, status code, envelope shape)
  - auth boundary behaviour (anonymous/forbidden/allowed)
  - model binding/serialization/middleware behaviour (CSRF, cookies, multipart binding)
  - API-only cross-cutting wiring (Swagger schema exposure, endpoint registration)

## API Integration Scope Rules

For each endpoint family, target one test per endpoint concern, not one test per business-rule permutation.

Keep in API integration:

- one happy-path contract test per route family
- one or more permission boundary tests where policy differs by actor
- one validation mapping test per unique request-shape pattern
- middleware/wiring tests that cannot be proven in service tests

Move to service tests (or avoid adding in API integration):

- additional permutations of the same validation rule
- detailed state transition matrices already covered in services
- repeated business-rule checks that only differ by data setup

## Duplication Check Before Adding Tests

Before adding an API integration test:

1. Search `BoardOil.Services.Tests` for an equivalent business rule.
2. If service coverage exists, add API integration coverage only if we still need one of:
   - endpoint contract proof
   - permission boundary proof
   - middleware/binding proof
3. If none apply, add/extend service tests instead of API integration tests.

## Pruning Guidance

When runtime becomes slow, prune in this order:

1. remove duplicated API integration business-rule permutations
2. keep API contract/auth/middleware representatives intact
3. keep migrations/realtime/MCP integration suites intact unless explicitly superseded

## Auth and MCP Policy Patterns

For API auth coverage, prefer policy-level sentinels over endpoint-by-endpoint `401/403` assertions:

- Keep a small set of representative auth boundary tests per policy shape (`authenticated`, `admin`, board membership variations).
- Avoid duplicating anonymous/forbidden assertions on every endpoint when the route uses an already-covered policy.
- Keep one endpoint metadata guard test that inspects mapped `/api/*` routes and fails if protected routes are missing authorization metadata.

For security-behaviour suites, keep integration depth where transport/auth semantics are the behaviour:

- Keep MCP bearer/path/auth-mode suites as integration tests.
- Keep PAT scope behaviour and machine-token auth flow suites as integration tests.
- Keep CSRF and internal API-key boundary checks as integration tests.

## Changed-Area Mapping

`scripts/test-fast.mjs` maps changed paths to suites:

- `BoardOil.Services/**` -> `BoardOil.Services.Tests`
- `BoardOil.Api/**` or `BoardOil.Api.Tests/**` -> `BoardOil.Api.Tests`
- `BoardOil.Dev/**` or `BoardOil.Dev.Tests/**` -> `BoardOil.Dev.Tests`
- `BoardOil.Web/**` -> `npm run check` and `npm test` (in `BoardOil.Web`)
- shared backend layers (`BoardOil.Contracts`, `BoardOil.Abstractions`, `BoardOil.Ef`, `BoardOil.Data.Abstractions`, `BoardOil.Mcp.Contracts`) -> API + Services tests
- global tooling/workflow files (`BoardOil.slnx`, `Directory.Build.props`, `Directory.Packages.props`, `global.json`, `NuGet.config`, `.github/workflows/*`, `scripts/*`) -> API + Dev orchestrator + Services tests
- unknown/non-code paths default to no tests in fast mode

## Naming Expectations

Use names that make ownership clear:

- Contract/middleware intent in API tests (for example `...ShouldReturnBadRequest`, `...ShouldReturnForbidden`, `...ShouldMark...`)
- Rule/invariant intent in service tests (for example `...ShouldReassign...`, `...ShouldBeAtomic...`)

## Board Package Schema Compatibility

- Treat currently supported board package schema versions as a compatibility set unless a story explicitly introduces versioned divergence.
- When adding fields to board package payloads without a schema bump, add service tests that exercise those fields across all currently supported schema versions.
- For BoardOil board package imports where schema `1` and `2` are intentionally equivalent, preserve regression coverage for both versions before introducing `3`.