Skip to content

tests: raise sdk-node coverage from 62% to 93%#1

Merged
TeoSlayer merged 1 commit into
mainfrom
add-test-coverage
May 27, 2026
Merged

tests: raise sdk-node coverage from 62% to 93%#1
TeoSlayer merged 1 commit into
mainfrom
add-test-coverage

Conversation

@TeoSlayer
Copy link
Copy Markdown
Contributor

Summary

Adds five new test files and a vitest.config.ts that scopes v8 coverage to src/. Raises measured coverage on the published SDK surface from 62.03% to 92.93% (v8 statements/lines) with 173 tests across 7 files, all green.

File Before After
cli.ts 0% 100%
client.ts 67.85% 99.48%
ffi.ts 40% 86.81%
runtime.ts 74.01% 88.98%
index.ts (excluded re-export shim)
overall 62.03% 92.93%

Source: @vitest/coverage-v8@^3.0.0 (matches the pin already used in .github/workflows/ci.yml).

What's new

  • vitest.config.ts — scopes coverage to src/**/*.ts, excludes the re-export shim, adds json-summary reporter.
  • package.json — new test:coverage script; coverage-v8 promoted to a dev-dep (CI already installs it inline, this just keeps local installs consistent).
  • tests/client_extra.test.ts (20 tests) — covers sendMessage, sendFile, publishEvent, subscribeEvent, hostname resolution, library-singleton helpers, and the ACK-read fallback branches in sendMessage/sendFile. Uses an in-memory FFI fake so the wire frame layout ([type][len][payload], event frames, file-transfer name header) is exercised end-to-end.
  • tests/ffi_extra.test.ts (12 tests) — parseJSON / checkErr / unwrapHandleErr edge cases plus findLibrary's PILOT_LIB_PATH override and the runtimeLibraryPath fallback that exercises the seeder.
  • tests/runtime_extra.test.ts (10 tests) — isDaemonLive against a real ephemeral UNIX-socket server (live + missing + dead-file + malformed-config), seeder daemon-skip behaviour, version fallback to package.json, stale-lock reclamation, ensureDirWritable error message.
  • tests/cli.test.ts (16 tests) — covers all four bin-stubs shims using a hoisted spawnSync mock; verifies success / non-zero / default-1 / launch-error paths. process.exit is replaced with a thrower so the test runner survives.
  • tests/index.test.ts (4 tests) — smoke-checks the public re-export surface.

What's hard to test without a live daemon

  • loadLibrary()'s koffi-loaded function bodies (the four Pilot* wrappers that allocate buffers via koffi.decode) — these need the actual libpilot.{so|dylib} on disk. Currently 86.8% covered because findLibrary discovers a bundled lib when one is present in ~/.pilot/bin/ from a previous SDK install; on a fresh CI runner without a bundled binary the loader path will be skipped and coverage will drop to ~40%. Recommend an integration test that builds and links libpilot in CI, or an FFI replay/stub harness if you want to stay unit-test-only.
  • cli.ts actually executing the seeded binaries — current tests mock spawnSync, so we verify the contract but not the real process launch.
  • probeDaemonLiveSync — uses nc -z -U synchronously. The probe is environment-dependent (see "Bugs noticed" below), so the test asserts a tolerant invariant ("either upgrade or daemon-skip") rather than a single fixed outcome.

Bugs noticed (NOT fixed in this PR)

  1. probeDaemonLiveSync is unreliable — BSD nc -z -U <sock> on macOS returns status 1 even when the socket is connectable, so the seeder may overwrite pilot-daemon while it is actually running. The async isDaemonLive (which uses a real net.Socket.connect) is correct; the sync probe should use the same primitive via spawnSync(node, ['-e', '...']) or expose an async seeder API.
  2. probeDaemonLiveSync require('node:child_process') fails in pure ESM — In an ESM module, the require call inside probeDaemonLiveSync throws, falling through to the conservative existsSync(sockPath) path. Result: ANY socket file at the configured path is treated as a live daemon and pilot-daemon is silently skipped. This is the same root cause flagged by iter-3 as a HIGH bug.
  3. PILOT_LIB_PATH allows arbitrary .so/.dylib load with no signature / checksum / origin check. Documented for posterity; expected behaviour for an FFI escape hatch, but worth a warning in the README.

Test plan

  • npm test — 173 tests pass, no warnings
  • npm run test:coverage — 92.93% statements / 92.93% lines / 85.80% branches / 85.96% functions on src/
  • Three repeated runs of npm test for flakiness; all stable
  • CI run on Linux x64 (will likely show lower ffi.ts coverage if no bundled libpilot — see note above)
  • Optional: confirm Codecov upload picks up the new reporter outputs

- add vitest.config.ts that scopes v8 coverage to src/ and adds a
  json-summary reporter
- add test:coverage npm script and devDependency on @vitest/coverage-v8
- tests/client_extra.test.ts: cover sendMessage, sendFile, publishEvent,
  subscribeEvent, hostname resolution, library singleton helpers, and
  the ack-read fallback paths
- tests/ffi_extra.test.ts: cover parseJSON / checkErr / unwrapHandleErr
  edge cases plus PILOT_LIB_PATH override and runtimeLibraryPath fallback
- tests/runtime_extra.test.ts: cover isDaemonLive async UNIX-socket
  probe, daemon-skip behaviour, version fallback, stale-lock recovery,
  and ensureDirWritable error
- tests/cli.test.ts: cover all four bin shims via a hoisted spawnSync
  mock; verify success, non-zero, default-1, and launch-error paths
- tests/index.test.ts: smoke-check the public re-export surface

Coverage (v8): statements 62.03% -> 92.93%, lines same, branches 81.6%
-> 85.8%, functions 88.3% -> 86.0% (denominator widened as loadLibrary
wrappers now register).  173 tests, 7 files, all green under both
'npm test' and 'npm run test:coverage'.
@TeoSlayer TeoSlayer merged commit d02bd00 into main May 27, 2026
2 checks passed
@TeoSlayer TeoSlayer deleted the add-test-coverage branch May 27, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants