test: restore the suite after the hash-store migration and run it in CI - #56
Merged
Merged
Conversation
npm test was not wired into any workflow, so 20 tests failed from the hash-store migration (#41) onward without anyone noticing. Every failure was a stale test, not an engine bug: fixtures still put lastPulledHash / lastPushedHash into state, checkDriftForUpdate calls lacked the new env argument, and one test pinned both-diverged for the converged edge that classifyDrift now deliberately treats as clean. - add .github/workflows/ci.yml: typecheck + npm test on every PR and on pushes to main, on Node 20 and 22 - move test baselines into the hash store: spawn-based tests seed <tmp>/.vapi-state-hash, drift tests seed via writeBaseline under a throwaway org slug, and audit gains a baselineReader DI seam - push-stale-baseline-noop seeds a credential so push no longer runs a bootstrap pull that overwrote the stale baseline before the drift check, and asserts it; removing the agree-gate now fails this test - rewrite the classifier short-circuit regression around the hash store - tool-assistant-cycle deletes the baseline it wrote into the real store - improvements.md #32 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scott-lowe-vapi
force-pushed
the
fix/stale-tests-and-ci
branch
from
October 1, 2026 04:29
0a1cadc to
87f55f5
Compare
roshan-vapi
approved these changes
Oct 1, 2026
scott-lowe-vapi
added a commit
that referenced
this pull request
Oct 3, 2026
## Value **V.A.L.U.E. tier:** project — PR 1 of 10 for inline simulation PR checks ([TEST-141](https://linear.app/vapi/issue/TEST-141/gitops-run-simulation-suites-against-pr-changes-inline-as-ci-checks)); this PR is a small, behavior-preserving slice. - **Problem:** `tests/` was never type-checked (`tsconfig.json` included only `src/`), so 37 type errors piled up silently. That's the gap #56 named as follow-up in `improvements.md` #32. Separately, simulation runs started from gitops can't be told apart in the platform's analytics. - **Who it affects:** gitops maintainers and contributors, who get a compiler check on test fixtures; and the TEST-141 success measure, which needs a pre-release baseline of gitops-started runs. - **What changes:** - `npm run build`, which CI already runs on every PR, now type-checks `tests/`. - The 37 errors are fixed. - `npm run sim` sends `User-Agent: vapi-gitops-sim/<version>`. The API records this as `user_agent` on the `[simulation] run started` event. The CI workflow itself landed in #56, so this PR is smaller than PR 1 in the plan. ## Evidence of value | Check | `main` (69c7e83) | This branch | |---|---|---| | `tsc --noEmit` with `tests/` included | **37 errors** (6 test files) | **0 errors** | | `npm test` | 355 pass | 357 pass (2 new, 1 rewritten) | | `User-Agent` on `POST /eval/simulation/run` | none | `vapi-gitops-sim/1.0.0` (asserted against a local HTTP server) | What the 37 errors were: - Fixture drift after the hash-store migration: state entries still carrying `lastPulledHash`/`lastPushedHash`, bare-string state values, and an untyped `emptyLoaded()`. - One real gap: the `reconcile-state-key` harness never passed the required `formatError`, so any test reaching that error path would have thrown a `TypeError` instead of testing it. Tests that used the removed hash fields as markers (`state-merge`, `recanonicalize`) now mark "which copy won" with distinct UUIDs or object identity, so they still check the same behavior. ## Testing plan - `npm run build` (now covers `src/` and `tests/`) and `npm test` locally on Node 22: green. CI on this PR runs both on Node 20 and 22. - New `tests/user-agent.test.ts` covers the header format against `package.json`'s version, and the header actually sent on run create. - `sim.test.ts` now covers the legacy bare-string state value directly, replacing the old cast-based "forward-compat" test. - **Not tested:** a live run against the API (the header is asserted locally only), and Node 20 locally (left to CI). `src/` behavior is unchanged apart from the added header. Refs TEST-141 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
npm testisn't wired into any workflow (.github/workflows/only heldpromotion.yml), so nothing blocks a merge that breaks tests. Since the hash-store migration in #41, 20 of 357 tests have failed onmain, unnoticed.Bisecting first-parent
main: 0 failures at #42, 20 failures from #41's merge onward, and no new failures since then (#55 included).Diagnosis: stale tests, not engine bugs
#41 moved drift baselines out of the state file into
.vapi-state-hash/<org>/<uuid>, madeupsertState/asResourceStatestrip legacy fields, and made pull/push/apply refuse legacy-shaped state. The tests were never moved over.audit.test.tslastPulledHashin state;audit.tscorrectly reads the hash store nowstate-migration.test.ts, 2 indrift.test.tsreconcile-state-key.test.tslastPushedHashlands in state; the shared push path records the baseline in the hash store insteaddrift.test.ts(checkDriftForUpdate)envargument, so the hash-store path wasundefineddrift.test.ts(converged edge)both-divergedfor local == platform ≠ baseline, whichclassifyDriftnow deliberately returns asclean(the documented invariant behind the phantom-drift fix, improvements.md #23)The last row matters most. Three of those were the regression guards #41 added for #22 (rename keeps the local filename; same-name clobber) and #23 (a stale baseline must not block a push). They have never passed, so those fixes had no working coverage.
Changes
.github/workflows/ci.ymlrunsnpm run build+npm teston every PR and on pushes tomain, on Node 20 and 22 (theenginesrange). It sets a job timeout and read-only permissions.src/into a temp dir, so the engine's store resolves there; they seed<tmp>/.vapi-state-hash/<env>/<uuid>.drift.test.tsseeds throughwriteBaselineunder a throwawaydrift-test-<pid>org and removes it afterwards.audit.tsgains an optionalbaselineReaderDI seam next tostateLoader/listLocalIds. That is the only production-code change, and the default is the existingreadBaseline(VAPI_ENV, uuid).push-stale-baseline-noopnow tests what it says. With emptycredentials,maybeBootstrapStatetreats state as uninitialized and its bootstrap pull rewrote the stale baseline before the drift check ever ran. Migrating the fixture alone would have produced a green test that exercises nothing. It now seeds a dummy credential and asserts no bootstrap ran.clean, with the rationale fromdrift.ts.tool-assistant-cycle.test.tsdeleted nothing it wrote:updateToolAssistantRefsrecords a baseline into the developer's real.vapi-state-hash/test-fixture-org/on every run. It now removes it.improvements.mdfix(push,pull): recanonicalize stale UUID-suffixed state keys — root-cause duplicate generation #32.Evidence
npm test: 355 / 355 pass (was 337 / 357; two fewer tests because Section J's four tests became one and onestate-migrationtest was split in two).npm run buildis clean.localHash === platformHashin bothclassifyDriftandcheckDriftForUpdate) failspush-stale-baseline-noopand the converged-edge test. Restoring it passes both..vapi-state-hash/holds no test-written baseline. Thedrift-test-<pid>folder is removed, and thetool-assistant-cyclebaseline file is deleted, leaving only an empty gitignoredtest-fixture-org/folder.Not in this PR
tsconfig.jsonstill includes onlysrc/**/*, so the typecheck never seestests/. That's why stale fixture shapes compiled silently. Includingtests/surfaces 37 existing type errors; that's its own change.🤖 Generated with Claude Code