Testing

September 10, 2026 · View on GitHub

Runner: node:test (Node 22 built-in). Property-based tests: fast-check 4. No root test script — each package owns its lane. Every package builds first, then tests the compiled output (dist/ or lib/). ESLint ignores **/test/ and **/scripts/; test files are type-checked by each package's tsc -p tsconfig.test.json inside the test lane.

Commands

PackageCommandCovers
uniterra-desktoppnpm --filter @uniterra-solutions/uniterra-desktop testprofile-bootstrap unit tests + built-ins/readiness PBT
uniterra-providerpnpm --filter @uniterra-solutions/uniterra-provider testcomposition + dual-protocol translate + reasoning-preservation regressions + seeded properties
uniterra-clipnpm --filter @uniterra-solutions/uniterra testCLI unit tests + install-logic PBT
uniterra-updaterpnpm --filter @uniterra-solutions/uniterra-updater testdecision semantics (fast-check)
uniterra-skillspnpm --filter @uniterra-solutions/uniterra-skills testprovisioning tests (every skill ships SKILL.md; idempotent)
uniterra-systempromptpnpm --filter @uniterra-solutions/uniterra-systemprompt testrule injection: registration, append, no cross-turn duplication
containerscripts/verify-cli-container/run.shclean-container installer replay + provisioning PBT + real dsh boot
windows-installscripts/verify-windows-install/verify.ps1real uniterra setup --source --move-source on windows-latest: package → move-embed (the closer-to-real user flow, no full-tree robocopy) → install → shortcut → Uniterra.exe boot smoke (runs in the release gate, not locally on macOS)

Static gates: pnpm run typecheck (tsc -b --noEmit), pnpm run lint (eslint ., max-warnings 0), pnpm format:check. Pre-commit hook runs lint-staged with --max-warnings 0.

PBT Invariants (the safety net)

SuiteInvariants
packages/uniterra-desktop/test/builtin-pbt.test.mjshasAllBuiltins true iff every expected bundle present (order/extras irrelevant, malformed → false); vendored/workspace staleness by version + shared-file content identity (drifted bytes under the SAME version still re-provision); readiness URL parse across chunk-boundary splits (port-completeness regex); dsh plugin add + boot to a reachable URL
packages/uniterra-cli/test/pbt.test.mtsparseArgs semantics (--no-open/--dry-run commute, --source consumes the next token, --version wins, first positional = command); installPlan stage order (update = CLI refresh → rebuild → relaunch, setup never touches the CLI, dry-run runs nothing, relaunch iff open); .app discovery over all mac-* shapes; win-unpacked discovery; install destinations / builder args / launch targets / shortcut script quoting per platform; remapJunctionTarget (staging prefix → embedded prefix with suffix intact, non-staging → undefined, no-suffix → undefined, \\?\/forward-slash/case normalization)
packages/uniterra-provider/test/reasoning-preservation.test.mjsNo loss / no duplication of reasoning, text, and tool calls across every gateway wire shape — per-shape regressions + seeded randomized interleavings (300 runs); thinking-mode reasoning passback — EVERY tool-call turn carries the marker (Chat reasoning_content: "" / Responses reasoning item, the placeholder when no chain of thought exists yet) because the gateway cannot recognize a replayed call id it did not mint, plus upstream call-id adoption by the Responses translator (call_id, item id only a fallback) — locked by deterministic agent-loop cases (translate → serialize round trip included) + seeded serialize/translate properties
packages/uniterra-provider/test/smoke.test.mjsRegistration faces, credentials seam, settings write-point validation, dual-protocol parity, models.dev matching, RPC channel, per-model protocol pinning
packages/uniterra-updater/test/decision.test.mtsSemver comparison incl. prereleases + unparseable→equal; update verdict merge; skip-prompt semantics; update-action mapping (Update Now → uniterra update, Skip → persisted version, otherwise none)
packages/uniterra-desktop/test/dsh-shortcuts-*.test.mjsEvery dsh seam the plugin uses exists in the PINNED family (vendor/dsh-harness): plugin-side surface table == recorded calls per feature; dsh-api oracle fails by name when a service/method/projection-key/slot/loading-protocol fact drifts; behavior: combo matching + shifted-punctuation normalization, self-match vs single-bit-change PBT, Tab-hold effort combos, permission cycle never targets custom and always lands on a provided preset (loopback route, no slash command), settings persistence/migration, recording UI, theme toggle keeps a registered skin active, new-session via sessions.create, copy-last-message via binding.eventSource; real dsh web boot smoke (scripts/verify-dsh-shortcuts-smoke/run.sh)
scripts/verify-cli-container/pbt/provisioning-pbt.test.mjsEvery workspace/vendored built-in ships the entry file its manifest points at; BUNDLES_SET; staleness detection; ensureBuiltinPlugins + real dsh --profile web boot to a 2xx readiness URL (300 s cap)

Test Conventions

  • PBT-first: business logic is pinned as properties before fixes (uniterra-pbt-debugging) and before implementation (uniterra-implement failing-PBT red phase).
  • Pure logic is extracted into packages without heavy imports (e.g. uniterra-updater has no Electron/fs imports) so semantics are unit-testable.
  • Deterministic randomness: seeded 32-bit PRNG for provider seeded properties.
  • Vendored community plugins are pinned against the vendored dsh family (vendor/dsh-harness) by a local white-box suite per plugin: the oracle reads the harness SOURCES, so an upstream rename/removal fails by name, and a customized plugin's patch must keep the suite green (see docs/modules/vendor-plugins.md).
  • The container harness is hermetic — pristine source archive, no .git, no node_modules, CI=true install; no macOS runner required. The Windows harness verifies the real installer on windows-latest; both gate a release.

Coverage

No coverage tooling configured. Invariant coverage is expressed through the PBT lanes above.

How to Update

  • New test lane / command → add a row to the Commands table.
  • New invariant suite → add a row to the PBT Invariants table.
  • Testing convention changes → update the Test Conventions section and AGENTS.md.

Find It Fast

find packages scripts -name '*.test.*' -not -path '*/node_modules/*'  # every test file