Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testing Conventions

Audience: contributors writing tests, regenerating goldens, or trying to figure out which CI gate to run locally before pushing.

From CLAUDE.md, CI gates and Common Claude Code Workflows.

Style

  • Table-driven tests are the default. Put cases in a []struct{...} with a name field, run with t.Run(tc.name, func(t *testing.T)).
  • Hermetic by construction: anything that touches the filesystem uses fs.NewMemMap() so tests don’t depend on disk state.
  • New code lands with tests in the same PR — TDD first, then implementation. A test that passes without the implementation is suspicious; the test is probably wrong.

Running tests

# Full suite
go test ./...

# Single package
go test ./processing/...

# Verbose, specific test
go test ./service/... -v -run TestProcess

# Coverage report
make cover

# Fuzz the .pulse header
go test ./encoding/... -fuzz FuzzPulseFileHeader -fuzztime 30s

Non-skippable CI gates

These tests guard structural invariants. If one of them fails, the underlying conventions (not the test) are what need re-thinking. Their full names appear in CLAUDE.md so the TestClaudeMdMentionsAllNonSkippableGates self-check can find them.

GateGuards
TestPredictNoExecutionImportsdescriptor/predict.go does not import service/ or processing/
TestDescriptorNoFmtSprintfdescriptor/ never builds JSON via fmt.Sprintf
TestGoldensNotHandEditeddescriptor/testdata/* hashes match the generator
TestClaudeMdMentionsFormatVersionCLAUDE.md references the current envelope format_version
TestClaudeMdMentionsAllEnvVarsEvery PULSE_* env var has a CLAUDE.md row
TestClaudeMdMentionsAllNonSkippableGatesThis very table is the source — CLAUDE.md must list every gate by name
TestUpdateDemandTableCoversThe Update Demand table covers every registered component category
TestPerPackageCoverageFloorsPackage directories exist and meet documented coverage floors
TestNoOrbitReferences, TestNoOrbitPrefix, TestNoOrbitPrefixesNo predecessor-project string prefixes leak in
TestSkillsCoverAll*Skill files mention every registered component, error code, distribution, CLI leaf, field type, MCP tool
TestSkillsManifestConsistentskills/index.json matches the .md files and frontmatter
TestSkillsFrontmatter_RequiredFieldsEvery skill has name, description, type, applies_to
TestRegistryStreamabilityMatchesTypesAggregator OnlineAggregator capability matches AggregationType.Streamable()
TestPredict_Streamable_MatchesRuntimePredictResult.Streamable mirrors processing.CanStreamRequest
TestStreamability_*KnownEvery All*Types() entry has a streamability table row
TestCanStreamRequest_RegressionMatrixRegression matrix on the exported CanStreamRequest helper
TestManifest*CompleteManifest enumerates every registered operator, test, distribution, MCP tool, error code
TestManifestStreamableMatchesTypesManifest Streamable flags mirror the type-level methods
TestCodesHaveFixups, TestSkillsErrorCodeFixupsDocumentedEach error code has a fixup template and the skill row to match
TestDefaults_AppliedSmart-default operator-type inference behaves as documented
TestNaturalQuery_HeuristicGrammarThe internal/query parser fixtures cover its documented shapes

(See CLAUDE.md “CI gates” for the full prose; this table is the quick-reference.)

Running a subset of gates locally

# All descriptor contract gates
go test ./descriptor/ -run 'TestPredictNoExecution|TestDescriptorNoFmtSprintf|TestGoldensNotHandEdited'

# Skill coverage gates
go test ./skills/ -run 'TestSkillsCoverAll|TestSkillsManifestConsistent|TestSkillsFrontmatter'

# CLAUDE.md gates
go test . -run 'TestClaudeMd|TestUpdateDemandTable'

# Predecessor-reference scrub
go test . -run TestNoOrbitReferences

Regenerating golden files

Golden files live in descriptor/testdata/. Each ends with a // golden-hash: <sha256> line; TestGoldensNotHandEdited verifies the hash. After a legitimate change to the generator:

go test ./descriptor/ -run 'Test.*Golden' -update
go test ./descriptor/ -run TestGoldensNotHandEdited   # confirms the new hash sticks

Never hand-edit a golden file — the gate will catch you.

Adding a new gate

If your change introduces a structural invariant, add a test for it under the same naming convention (TestX), and add it to the table in CLAUDE.md so TestClaudeMdMentionsAllNonSkippableGates recognises it. The Update Demand lists this as a trigger row.