mirror of
https://github.com/meshtastic/web.git
synced 2026-07-30 22:46:30 -04:00
Adds TESTING.md documenting six tiers (unit / slice / client / storage / hook / E2E), per-package coverage gates, and the audit of where we currently sit. Slice tests in @meshtastic/sdk - NodeMapper proto round-trip; NodesClient list signal updates. - ChannelsClient indexes by channel number. - ConfigClient merges Config + ModuleConfig variants. - TelemetryClient latest + history per node. - PositionClient byNode + list. - ChatClient persistence: hydrate on first subscribe, paginate via loadOlder, persist inbound messages through the repository. Fixes a reverse-iteration bug in loadOlder discovered by the new test. Hook tests in @meshtastic/sdk-react - New tests/hooks.registry.test.tsx covers useMeshDevice, useNodes, useNode, useChannels under <MeshRegistryProvider>, plus an active- client switch round-trip. Storage tests in @meshtastic/sdk-storage-sqlocal - migrations.test.ts validates v1 DDL creates messages/nodes/telemetry/ _schema and indexes; CREATE IF NOT EXISTS is idempotent. - MultiTabCoordinator.broadcast.test.ts proves cross-tab BroadcastChannel delivery between two coordinators in the same process. - New vitest.browser.config.ts + tests/sqlocal-opfs.browser.test.ts run under @vitest/browser (Playwright provider) for real OPFS round-trip verification. Wired as `pnpm test:browser`. Browser test files end in `.browser.test.ts` and are excluded from the Node runner. E2E / firmware-simulator tier (TESTING.md §"E2E / simulator") is scoped for a follow-up — needs CI Docker for meshtasticd. Totals after this change: - @meshtastic/sdk: 36 tests (was 25) - @meshtastic/sdk-react: 8 tests (was 2) - @meshtastic/sdk-storage-sqlocal: 12 tests (was 8) - meshtastic-web: 294 tests (unchanged)
6.9 KiB
6.9 KiB
Testing strategy
How this monorepo proves correctness, and where coverage currently sits.
Levels
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/ |
Per-package coverage gates
| 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 |
Current state (audit)
| 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 |
Concrete additions queued (priority order)
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.
E2E / simulator (Tier 6) — scope only
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.
Conventions
- 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.
Running
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