Skip to main content

Overview

Every user-data feature in the High IQ mobile app is covered by automated, simulator-free tests organized into three tiers. Each tier proves something the others structurally cannot see, and each costs a different amount to run. Cannabis and strain data (Supabase) is out of scope except where a user feature depends on it. User data in Convex is the focusapps/mobile/convex/schema.ts is the source of truth for what exists.
This platform is separate from the Revyl cloud-device suite. Revyl drives a real iOS simulator through the UI; the tiers below never boot a simulator and run in seconds to minutes instead.

The three tiers

Tiers A and B are free, fast, and hermetic — they never touch a deployment. Tier C hits real infrastructure: the shared staging Convex deployment, the production Hono API, and Trigger.dev’s prod environment. It writes real rows and spends real credits, which is why it is invoked explicitly and never runs on push.
Tier C is deliberately not part of pnpm test, pnpm check, or turbo run test. The Turbo test task is cached — a cached pass would defeat a suite whose entire job is detecting deployed-backend regressions — and its strict env mode would strip every credential the suite needs.

Running each tier

All commands run from apps/mobile (or with pnpm --filter @highiq/mobile from anywhere in the monorepo).
Tier C requires environment variables and skips cleanly without them, naming exactly what is missing. See Tier C — Staging Integration for the full setup, the isolated-user pattern, and the three-layer production guard.

Adding a test for a new feature

1

Pick the tier by what you need to prove

  • Function logic, a validator, an auth check, a scheduled effect → Tier A
  • The arguments a hook sends, or how a screen renders a backend state → Tier B
  • “Does this actually work on the deployed backend? Does the pipeline complete?” → Tier C
Tier C is the expensive tier. Use it only for what A and B structurally cannot see: real JWT verification, outbound network calls from actions, scheduled side effects on a real deployment, and pipeline completion.
2

Name the file

New Tier A/B suites use the *.featuretests.test.ts(x) suffix in a new file, so parallel authors never collide with the existing suites.
3

Tag it (Tier C)

Vitest -t matches the concatenated describe/test title, so tags live in titles: @smoke, @slow, @ai, @trigger, @shared-account, and @P0@P3. A case with no tag runs in every lane.
4

Add the matrix row

Give the case an area-prefixed ID (ORD-231, NB-118, SUB-115, …) and add it to the master matrix with tier, priority, status, and implementing file. Do not leave a test out of the matrix — the matrix is what the weekly regression run is read against.
5

Scope every Tier C assertion to your own test user

Staging is shared by every non-production build plus a daily cron. Never assert on a global count, a “latest N” listing, or a cross-user aggregate — those pass or fail depending on what someone else did five minutes ago.

Interpreting results

Per-area builder results and run evidence live in the repo under docs/testing/feature-audit/. Statuses have fixed meanings:
A skipped row is never evidence that a feature works. Report pass, fail, skipped-blocked, and skipped-paid as four separate counts. Collapsing skips into “not failing” is how a run where an entire pipeline is dead still looks green.

Two lessons worth keeping

  • A pin must assert behaviour, never a spelling. it.fails only self-flips when the assertion observes the effect of the defect. A negative source scan (not.toContain('…')) fails open — it goes green when the literal is merely reworded while the defect remains. Invert unavoidable source assertions into a positive binding.
  • The in-memory fake and a real deployment differ on time. On a real Convex deployment Date.now() is frozen for the duration of a mutation, so every row written in one handler shares a timestamp; the in-memory fake advances per call. A Tier A test asserting timestamp equality across rows can flake in the harness while being correct in production — hoist const now = Date.now() in the handler rather than quarantining the test.

Tier C — Staging Integration

Identity model, production guard, tags, and cost controls.

Subscription-State Testing

How free, active-Pro, and lapsed-Pro states are proven now that staging enforces the gate.

Weekly Feature Regression

Lanes, local reproduction, and why the schedule is still off.

Launch Testing Plan

Where this platform sits among Revyl, Argent, and manual QA.