test: cover state/run/graph handlers + state internals (#29)#35
Merged
Conversation
Adds createMockTemporalClient() and supporting types for testing chant
run handlers (and any other code that depends on @temporalio/client)
without spinning up a real cluster.
Configurable surface:
- describeByWorkflowId: per-workflow describe() responses
- historyByWorkflowId: per-workflow fetchHistory() events
- list: workflows yielded by workflow.list()
- describeError: if set, every describe() throws (simulates
UNAVAILABLE / no cluster)
Recorded call surface (mock.calls):
- startCalls: args to workflow.start()
- signalCalls: workflow id + signal name
- cancelCalls: workflow id
Six self-tests cover the recorder + the fixture surface.
Two new test files for the pure-function and orchestration parts of
the state pipeline:
state/digest.test.ts (11 tests)
- hashProps: deterministic, order-independent, distinct on different inputs
- computeBuildDigest: shape + missing-props default + manifest mirror
- diffDigests: no-prior, identical, changed, removed, mixed scenarios
state/snapshot.test.ts (6 tests)
- happy path: writeSnapshot called once per plugin with describeResources
- plugin without describeResources is skipped
- plugin throws -> per-plugin error, other plugins still proceed
- empty result -> error and no snapshot
- validation drops resources missing type/status with warning
- sensitive-data warnings for suspect attribute names
snapshot.test.ts uses vi.mock to stub git operations so tests run in
isolation. Both files use createMockPlugin from @intentius/chant-test-utils.
#29) Adds 7 tests using withTestDir() + a freshly-initialized git repo to exercise the plumbing in state/git.ts: - writeSnapshot creates the orphan branch and JSON is addressable - readSnapshot returns null for missing env/lexicon - subsequent writes preserve other env+lexicon entries - re-writing the same env+lexicon updates the entry (no duplicates) - readEnvironmentSnapshots returns all lexicons for an env - listSnapshots returns commit history of the orphan branch - getHeadCommit returns the working-branch HEAD sha Discovered a pre-existing dead call in writeSnapshot: a 'git hash-object -w --stdin' invocation with nothing piped into stdin that hung forever in the test context. Removed; the real blob write happens via the shell-pipeline call directly below it. The dead call was harmless in production where the chant CLI inherits a closed-stdin terminal but blocked test contexts where stdin stays open.
cli/handlers/graph.test.ts (5 tests):
- empty discovery -> 'No Ops found'
- ops with no depends -> 'No Op dependencies'
- prints 'dep -> name' edge per dependency
- multi-edge graph
- discovery errors forwarded to stderr
cli/handlers/state.test.ts grows from 3 -> 14 tests:
+ runStateSnapshot: missing env, unknown env, no plugins implementing
describeResources, happy path with mocked takeSnapshot
+ runStateShow: missing env, specific lexicon found / not found,
list-all when no lexicon specified
+ runStateLog: empty case, history with multiple entries
+ runStateUnknown: subcommand-list output
State tests use vi.mock to stub build, state/git, state/snapshot, and
config; no real I/O. Graph tests stub op/discover.
cli/handlers/run.test.ts (13 tests) covers the read-only and side-effect
run subcommands without spawning a real Temporal cluster:
runOpList:
- empty discovery -> warning + exit 0
- Temporal unavailable -> degrades gracefully, table still printed
- Temporal available -> annotates rows with workflow status
runOpStatus:
- missing op name -> exit 1
- connection error -> exit 1 with message
- happy path: prints workflow id/run id/status + activity counts
runOpLog:
- missing op name -> exit 1
- prints one row per workflow execution from client.workflow.list()
runOpSignal:
- missing op or signal name -> exit 1
- happy path: signal recorded against deterministic workflow id
runOpCancel:
- missing op name -> exit 1
- --force required to cancel
- --force present -> cancel recorded
runOp itself (the main 'chant run <name>' command) is intentionally not
covered: it spawns a worker subprocess and connects to a live Temporal
server, which would require either a real cluster or a much more
involved process-mock harness. Out of scope for this PR.
Also fixes a small TS spread-order warning in mock-temporal-client.ts
that surfaced when first added.
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.
Summary
Closes #29. Goes from 0 tests on
cli/handlers/{state,run,graph}.tsand the state internals to a layered, mock-backed test surface that makes the area refactorable. Six new test files + two mock factories + a small pre-existing-bug fix that surfaced during the work.Coverage delta
packages/core/src/state/digest.tspackages/core/src/state/snapshot.tspackages/core/src/state/git.tspackages/core/src/cli/handlers/state.tspackages/core/src/cli/handlers/graph.tspackages/core/src/cli/handlers/run.tsWorkspace test totals: 1434 → 1493 (+59 new tests).
Commits
chore(test-utils): add mockTemporalClient factory— configurable describe/history/list responses +mock.callsrecorder for signal/cancel/start. 6 self-tests.test(state): cover digest + snapshot internals— pure-function and orchestration tests using the existingmockPluginfactory.test(state/git): orphan-branch operations against a real temp git repo— useswithTestDir+git init. Found and fixed a pre-existing dead-call hang inwriteSnapshot: agit hash-object -w --stdininvocation with nothing piped to stdin that hung tests forever. The actual blob write happens via the shell-pipeline call directly below it; the dead call was harmless in production where the chant CLI inherits a closed-stdin terminal but blocked test contexts.test(cli): graph handler + extended state handler coverage— coversrunStateSnapshot/Show/Log/Unknown(existing--livediff tests preserved) and the smallrunGraphhandler.test(cli): cover run handlers with mockTemporalClient— coversrunOpList/Status/Log/Signal/Cancelhappy paths and error paths.Intentionally out of scope
runOpitself — the mainchant run <name>command — is not covered. It spawns a worker subprocess (spawn(['npx', 'tsx', ...])) and connects to a live Temporal server. Mocking those side effects is a much bigger harness than the rest of the run handlers needed, and would partly duplicateexamples/temporal-self-hostedsmoke testing. Worth a follow-up issue if priorities shift.Verification
just buildcleannpx vitest run→ 1493/1493 pass (workspace-wide)packages/core/src/cli/commands/build.test.tsMock factories now available from
@intentius/chant-test-utilscreateMockPlugin({ name, describeResources?, ... })— added in feat(state): chant state diff --live drift detection (#26) #32, expanded usage herecreateMockTemporalClient({ describeByWorkflowId?, historyByWorkflowId?, list?, describeError? })— new in this PR; consumed byrun.test.tsTest plan
npx vitest run --coverage)