How this monorepo proves correctness, and where coverage currently sits.
Tests live in five tiers. Each PR should add coverage at the lowest level that catches the regression, and only climb tiers when that's not possible.
| Tier | Scope | Tooling | Where |
|---|---|---|---|
| 1. Unit | Pure functions, value objects, mappers, single classes with mocked deps | vitest run (Node env) |
*.test.ts colocated with source |
| 2. Slice integration | Use-case + store + mapper exercised against in-memory deps; assert outbound bytes and signal state | vitest + createFakeTransport() |
*.test.ts in slice dirs |
| 3. Client integration | MeshClient end-to-end with a fake transport feeding canned FromRadio packets |
vitest + @meshtastic/sdk/testing |
packages/sdk/tests/integration/ |
| 4. Storage integration | Real Drizzle queries against sql.js in-memory; or against @vitest/browser for OPFS |
vitest (Node + browser) |
packages/sdk-storage-sqlocal/{src,tests} |
| 5. Hook / DOM | React hooks render under <MeshProvider> / <MeshRegistryProvider>, react to signals |
vitest + @testing-library/react + jsdom |
packages/sdk-react/tests/ |
| 6. E2E / simulator | Whole stack: SDK → transport → simulator/firmware. Catches protocol drift | @vitest/browser for OPFS; meshtasticd simulator over TCP for protocol |
future tests/e2e/ |
| Package | Required floor | Notes |
|---|---|---|
packages/sdk/core |
every primitive (signals, EventBus, Queue, packet-codec, identifiers) has a unit test; lifecycle covered by MeshClient.test.ts |
adopt c8 thresholds once stable |
packages/sdk/features/* |
each slice ships: 1 domain invariant test, 1 use-case test against fake transport, 1 mapper round-trip (where mappers exist) | Integration covered by tests/integration/fake-transport.test.ts |
packages/sdk-react |
each public hook has a renderHook test that asserts initial render + re-render on signal change |
uses jsdom; provider wrapper required |
packages/sdk-storage-sqlocal |
every repository method tested against sql.js in-memory; at least one OPFS-real test per repo runs in browser mode | sql.js validates SQL correctness; OPFS validates VFS / Worker plumbing |
packages/transport-* |
minimum: framing round-trip, disconnect cleans up streams, error path emits status | low-level; ship as is |
packages/web |
component tests at 34 baseline; new SDK-driven UI components must add a hook-mock test |
currently all green |
| Package | Test files | Tests pass | Gaps |
|---|---|---|---|
packages/sdk |
7 | 25 ✅ | nodes/channels/config/telemetry/position/traceroute/files slices have no tests; no MeshClient lifecycle test (only fake-transport integration); no schema migration test |
packages/sdk-react |
1 | 2 ✅ | only useMeshDevice + a stubbed useChat test; missing useNodes, useChannels, useConfig, useConnection, useTraceroute, useTelemetry, usePosition, useFileTransfer, useFavoriteNode, useIgnoreNode, useMeshRegistry, useClientById, useActiveClient. No registry-aware re-render coverage. |
packages/sdk-storage-sqlocal |
2 | 8 ✅ | sql.js only — no real OPFS test, no Worker boot test, no cross-tab BroadcastChannel test (mocked away), no migration v1→v2 test |
packages/web |
34 | 294 ✅ | no useConnections test; new meshRegistry + sdkStorage modules untested; chat persistence end-to-end not exercised |
packages/transport-* |
1 each (5 of 7) | varies | transport-deno + transport-mock have no tests; transports likely lack disconnect/error coverage |
packages/core |
0 | n/a | legacy, slated for deletion in Phase C; tolerable |
packages/ui |
0 | n/a | pure presentational; visual regression only |
packages/protobufs |
0 | n/a | generated code; upstream's responsibility |
packages/sdkslice tests — one Use-case + one Mapper test per slice (nodes,channels,config,telemetry,position,traceroute). Pattern: build a stubMeshClient, dispatch a synthetic event, assert signal value or outbound bytes.packages/sdk-reacthook tests — for every hook listed above, mount under<MeshProvider>, drive a signal change, assertresult.current. One file, ~15 cases.packages/sdkChatClientpersistence test — wireInMemoryMessageRepository, append messages, re-constructChatClient, assert hydration. Validates the lazy-load contract.packages/sdk-storage-sqlocalmigration test — bootstrap empty DB, runMIGRATIONS[], assert_schema.version. Then add a fakeversion: 2migration and prove it's applied idempotently.packages/sdk-storage-sqlocalbrowser mode — add a secondvitest.browser.config.tsusing@vitest/browser(Playwright provider) so we exercise real OPFS + Worker. CI runs both modes.packages/sdk-storage-sqlocalBroadcastChannel test — instantiate twoMultiTabCoordinatorinstances in same process; one broadcasts, the other observes. (Node has noBroadcastChannelglobal; useworker_threads'sBroadcastChannelpolyfill or jsdom env.)packages/webmeshRegistry+sdkStoragelazy-init test — assertgetStorageDb()returns the same promise on repeated calls and only opens the DB once.packages/sdk-reactregistry test — mount<MeshRegistryProvider>with two clients, switch active, confirm a hook re-renders against the new client.
Out of immediate scope; documenting for a follow-up PR.
- Run
meshtasticd(firmware simulator) in CI Docker viaservices:block. - Spin up
MeshClientwithTransportHTTPpointed at the simulator's HTTP endpoint. - Drive scripted scenarios: configure → send text → expect ack; channel update; node info exchange; traceroute.
- Use
@vitest/browserso we also exercise the real OPFS persistence path during E2E. - Run on
mainonly (cost). Smoke subset on PR.
Until that lands, createFakeTransport() covers the protocol layer at unit/integration speed.
- Test file colocated with source:
Foo.ts→Foo.test.ts. - Integration tests under
tests/integration/. - Browser-mode tests use the suffix
.browser.test.tsso they can be filtered. - Protobuf fixtures live in
__fixtures__/*.fixtures.tsnext to mappers; binary data committed as base64, not raw bytes. - Each test imports concrete classes from the source path (
./Foo.ts), not the package barrel — fast type-check, zero re-export drift. - No mocked SDK from inside SDK tests. Use
createFakeTransport()and realMeshClientinstances.
pnpm -r test # all packages, Node env
pnpm --filter @meshtastic/sdk test
pnpm --filter @meshtastic/sdk-storage-sqlocal test
pnpm --filter meshtastic-web test
# future:
pnpm --filter @meshtastic/sdk-storage-sqlocal test:browser