Skip to main content

Testing Strategy

How OpenHuman tests its product. The definitive source for "where does my test go?" Companion TEST-COVERAGE-MATRIX.md.

Test Tiers

TierLocationWhat It TestsDriven By
Rust Unit#[cfg(test)] mod tests within source *.rs files,同级 tests.rs, or tests/ subdirectory under a domainPure domain logic, schemas, RPC handler shapes, in-memory state machinescargo test
Rust Integrationtests/*.rs at repo rootFull domain connections with real Tokio runtime, mock external services, JSON-RPC end-to-endpnpm test:rust
Vitest UnitSource-collocated *.test.ts(x) or under app/src/**/__tests__/React components, hooks, store slices, pure utilities, service layer adapterspnpm test:unit
WDIO E2Eapp/test/e2e/specs/*.spec.tsFull desktop flow: UI → Tauri → core sidecar → JSON-RPC; user-visible behaviorLinux CI: tauri-driver (port 4444). macOS local: Appium Mac2 (port 4723)
Manual Smokedocs/RELEASE-MANUAL-SMOKE.mdOS-level surfaces the driver can't assert: TCC permission prompts, Gatekeeper, code signing, DMG install, OS-native toastsHuman confirmation at release time

Test Placement Decision Tree

Is the change behind the JSON-RPC boundary (in `src/`)?
├─ Yes - Does it cross domains or talk to external services?
│ ├─ Yes → Rust Integration (tests/*.rs)
│ └─ No → Rust Unit (next to source)
└─ No - The change is in `app/`
├─ Is it a pure function, hook, slice, or isolated component?
│ └─ Yes → Vitest Unit (*.test.tsx collocated)
└─ Is it user-visible and crosses UI ⇄ Tauri ⇄ sidecar ⇄ JSON-RPC?
├─ Yes → WDIO E2E (app/test/e2e/specs/*.spec.ts)
└─ Is it OS-level (TCC, Gatekeeper, install, OS toasts)?
└─ Yes → Manual smoke checklist

If your change touches multiple tiers, write tests at each tier you touch. Don't substitute one tier for another.

Failure Path Requirements

Every feature leaf in the coverage matrix must have at least one failure/edge assertion in addition to the happy path. Examples:

  • File write tool: happy path = write bytes; failure path = path restriction rejection
  • OAuth flow: happy path = issue token; edge = expired refresh token recovery
  • Memory storage: happy path = store + recall; edge = forget then recall returns empty

Mock Strategy

  • No real network in unit/integration/E2E. Use the shared mock backend
  • External services (Telegram, Slack, Gmail, Notion, Ollama, OpenAI, etc.) are stubbed at the mock backend level
  • The only acceptable exception is documented manual smoke steps at release time

Determinism Rules

  • Don't use wall-clock waits; use waitForApp, waitForAppReady, waitForWebView helpers or explicit element-ready predicates
  • Don't use shared filesystem state; each E2E spec runs in an isolated OPENHUMAN_WORKSPACE
  • Don't depend on ordering-related specs; each spec must pass when run alone
  • Don't depend on absolute coordinates or animation timing

Pre-Merge Checks

# Rust core
cargo fmt --check
cargo check --manifest-path Cargo.toml
cargo clippy --manifest-path Cargo.toml -- -D warnings
cargo test --manifest-path Cargo.toml

# Tauri shell
cargo check --manifest-path app/src-tauri/Cargo.toml

# Frontend
pnpm typecheck
pnpm lint
pnpm format:check
pnpm test:unit

# Rust integration
pnpm test:rust

# E2E (slow — only run when behavior changes visibly)
pnpm test:e2e:build

What the Driver Cannot Automate

Some surfaces cannot be driven by WDIO / Appium driver because they cross OS-level trust boundaries or hardware paths:

  • macOS TCC permission prompts (Accessibility, Input Monitoring, Screen Recording, Microphone)
  • Gatekeeper signature verification
  • Code signature integrity
  • DMG install / drag-to-Applications flow
  • Auto-update download + restart
  • OS-native notification toasts on Linux

Coverage Matrix as Contract

Every feature leaf in the coverage matrix maps to:

  1. A test path, or
  2. A 🚫 with justification and a manual smoke entry

When you add/delete/rename a feature, update the matrix row in the same PR.

Next Steps