Skill: stevenobiajulu/unit-test-philosophy (version 0.1.0)

--- name: unit-test-philosophy description: >- Risk-based unit testing and Allure-readable behavioral spec style for open-agreements. Use when user says "add tests," "test quality," "coverage expansion," "unit test style," or "Allure test spec." Applies when adding/updating tests, expanding coverage, or reviewing test quality across src, integration-tests, and workspace packages. metadata: short-description: Open Agreements testing philosophy version: "0.1.0" catalog_group: Developer Workflows catalog_order: 20 --- # Unit Test Philosophy (Open Agreements) ## Security model - This skill is **guidance only** — it does not execute tests or modify code directly. - All test commands are provided for the user or agent to run in their local environment. - No network access, credentials, or external services are required. ## Use this skill when - A request asks to add tests, improve coverage, or harden regressions. - A change touches `src/`, `integration-tests/`, `packages/contracts-workspace`, or [REDACTED_SECRET]. - You need readable Allure behavior specs and OpenSpec traceability. ## Core philosophy 1. Test highest-risk behavior first. Focus first on mutating flows, parser/validator boundaries, and policy/safety checks. 2. Optimize for regression prevention, not just line coverage. Prioritize branches where failures could produce wrong legal output or unsafe automation behavior. 3. Treat Allure as test style, not test type. Use normal unit/integration tests with Allure labels, steps, and attachments in the same files. 4. Keep spec and test effectively coextensive. If behavior is important enough to test, map it to canonical OpenSpec scenarios or active change-package scenarios. 5. Keep assertions behavior-oriented. Verify user-observable outputs, diagnostics, and mutation outcomes before internals. 6. Make failures easy to debug. Attach structured context for inputs, normalized outputs, and error payloads. ## Repo standards ### Test structure - Use Given/When/Then/And wording in Allure step titles. - Keep scenario steps as top-level test steps; avoid wrapping the full test body in synthetic container steps like `Execute test body`. - Prefer one assertion per step where practical. - Multiple assertions in one step are acceptable when they validate one cohesive invariant. - Keep tests deterministic (fixed fixtures, explicit env flags, no timing assumptions). ### Allure API - Prefer repo helpers over direct raw Allure calls: - `integration-tests/helpers/allure-test.ts` - Common helpers: `itAllure`, `testAllure`, `allureStep`, `allureJsonAttachment`, `allurePrettyJsonAttachment`, `allureWordLikeTextAttachment`, `allureParameter`, `allureSeverity` - Do not import from `allure-vitest` in tests. - Keep helper usage consistent across `src/**/*.test.ts` and `integration-tests/**/*.test.ts`. - For complex fixtures/stateful flows, attach: - a pretty JSON artifact for request/result payloads - a human-readable “Word-like” artifact for document/checklist state before and after mutation when relevant - Rendering note: HTML preview attachments rely on the report post-process sanitizer allowlist patch (`scripts/patch_allure_html_sanitizer.mjs`, invoked by `npm run report:allure`). Do not bypass this pipeline when generating reports for review. ### File naming and placement - Use collocated test files like `src/<module>.test.ts`. - Add Allure style inside these tests; do not split by "allure-only" test types by default. - Keep one test file focused on one module or capability. - Migration policy: gradually rename legacy `*.allure.test.ts` files to `*.test.ts`; do not introduce new `*.allure.test.ts` files. ### OpenSpec traceability - Use `.openspec('OA-###')` whenever a matching scenario ID exists for the behavior. - Scenario IDs may come from either canonical specs (`openspec/specs/open-agreements/spec.md`) or active change-package specs (`openspec/changes/<change-id>/specs/open-agreements/spec.md`). - Pre-canonical IDs from active change packages are valid during implementation and should remain stable when promoted into canonical specs. - For new important behavior, add scenario IDs in the active change package first, map tests immediately, then promote those IDs into canonical specs when archiving. ## Coverage expansion workflow 1. Read coverage summaries and identify branch-heavy modules in `src/core/**` and integration flows. 2. Rank by blast radius and mutation risk. 3. Add tests in this order: - Validation and error branches - Strict vs permissive behavior - No-partial-mutation / transactional guarantees - Invariants (deterministic outputs, schema safety, idempotency) 4. Run targeted tests first, then full suite and coverage. ## Severity recommendation rubric - `critical`: mutation correctness, legal-output integrity, data-loss risk, security/policy guardrails. - `normal`: standard behavior and compatibility scenarios. - `minor`: narrow edge cases with low production impact. - Apply severity based on failure impact, not module ownership. ## Command checklist ```bash npm run test:run npm run test:coverage npm run check:spec-coverage npm run check:allure-labels ``` ## Minimal test template (TypeScript) ```ts import { describe, expect } from 'vitest'; import { itAllure as it, allureStep, allureJsonAttachment } from '../../../integration-tests/helpers/allure-test.js'; describe('checklist patch behavior', () => { it('applies replacement deterministically', async () => { let result: { ok: boolean }; await allureStep('Given a valid patch payload', async () => { await allureJsonAttachment('patch-input.json', { patch_id: 'patch_001', operations: [{ op: 'replace', path: '/issues/0/status', value: 'CLOSED' }], }); }); await allureStep('When patch validation runs', async () => { result = { ok: true }; }); await allureStep('Then validation succeeds', async () => { expect(result!.ok).toBe(true); }); }); }); ``` ## Extended reference - See `references/allure-test-spec-writing-guide.md` for full Allure step-writing guidance.

path: references/allure-test-spec-writing-guide.md
content: # Allure Test Spec Writing Guide (Safe DOCX Adaptation)

Convert tests into Allure-readable behavioral specs that are easy to scan in reports and easy to debug on failure.

## Quick Reference (TypeScript + Vitest)

Use repo wrappers where available.

```ts
import { describe, expect } from 'vitest';
import { itAllure as it, allureStep, allureJsonAttachment } from '../testing/allure-test.js';

describe('Feature Name', () => {
  it('Human-readable test title', async () => {
    let result: { success: boolean };

    await allureStep('Given some precondition', async () => {
      await allureJsonAttachment('input', { key: 'value' });
    });

    await allureStep('When I perform an action', async () => {
      result = { success: true };
    });

    await allureStep('Then the result is correct', async () => {
      expect(result!.success).toBe(true);
    });
  });
});
```

If direct Allure calls are required:
- import from `allure-js-commons`
- `await` every Allure API call
- never import test APIs from `allure-vitest`

## Golden Rule: One Assertion Per Step

### Bad
```ts
await allureStep('Then everything works', async () => {
  expect(result.status).toBe('success');
  expect(result.count).toBe(5);
  expect(result.items[0].name).toBe('foo');
});
```

### Good
```ts
await allureStep('Then status is success', async () => {
  expect(result.status).toBe('success');
});

await allureStep('And count is 5', async () => {
  expect(result.count).toBe(5);
});

await allureStep("And first item is 'foo'", async () => {
  expect(result.items[0].name).toBe('foo');
});
```

## Given/When/Then Pattern

Use explicit step prefixes for report readability:
- Given
- And (additional setup)
- When
- Then
- And (additional expectations)

```ts
await allureStep('Given a logged-in user', async () => {
  user = createUser();
});

await allureStep('When updating the profile name', async () => {
  result = await updateProfile(user, { name: 'New Name' });
});

await allureStep('Then the update succeeds', async () => {
  expect(result.success).toBe(true);
});
```

## Attachments for Debugging

Add context that makes failures diagnosable without rerunning locally.

```ts
await allureJsonAttachment('request', requestPayload);
await allureJsonAttachment('response', responsePayload);
```

Recommended attachment content:
- input fixtures
- mutation params
- normalized output
- conflict diagnostics
- error payloads

## Severity Guidance

Default severity is `normal` in wrappers.
Use elevated severity selectively:
- `critical`: mutation correctness, data loss risk, security/path-policy guardrails
- `minor`: narrow edge-case behavior

## File Naming and Scope

- `*.test.ts`: standard collocated tests (default)
- Use Allure labels/steps/attachments inside these tests.
- Reserve `*.allure.test.ts` only for legacy files or explicit migration constraints.

Keep test files focused:
- one module or one capability per file
- avoid unrelated assertions in the same file

## OpenSpec Traceability Rules

Use `.openspec('...')` only when the test maps directly to a spec scenario.

Matching rules:
- copy scenario text exactly
- case-sensitive after whitespace normalization
- keep backticks if the spec includes them

## Allure Report Hierarchy Notes

This repo uses package-level compat reporters and setup hooks to keep Allure 3 hierarchy stable.

Follow existing package conventions:
- `packages/safe-docx/src/testing/*`
- `packages/docx-primitives/test/helpers/*`
- `packages/docx-comparison/src/testing/*`

Do not replace compat reporter wiring in `vitest.config.ts` unless explicitly requested.

## Coverage-First Prioritization

When increasing coverage:
1. Rank by uncovered branches in high-blast-radius code.
2. Prioritize mutators, parser/serializer boundaries, and safety checks.
3. Add tests for strict/permissive modes and transactionality.
4. Verify targeted tests first, then package coverage.

## Commands

```bash
npm run test:run -w @usejunior/safe-docx
npm run test:run -w @usejunior/docx-primitives
npm run test:run -w @usejunior/docx-comparison
npm run test:coverage:packages
node scripts/report_package_coverage.mjs
```

## Quick Checklist Before Committing

- Test title is behavior-readable.
- Steps are Given/When/Then/And.
- One assertion per step where practical.
- Attachments added for nontrivial data paths.
- Error code/hint text validated for failure-path tests.
- If traceability test: `.openspec()` text matches spec exactly.


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)