myxce/.kiro/steering/testing.md
Rongsong Shen f5fbc6bcac
feat(eliminate-frontend-makefile): task 3.3 - update live documentation (11 files)
Removes every live current-fact reference to src/frontend/Makefile
targets or Makefile-specific fixed paths across 11 docs (steering,
frontend/CLAUDE.md, root CLAUDE.md, deployment docs, and two skill
docs), replacing with the Meson-based equivalents established by
tasks 1-3.2: /xce-build apiserver, meson test --suite apiserver,
direct tests/regression/run.sh invocation.

Round-1 review rejected the first pass: xce-deploy-ha/SKILL.md had
over-corrected by deleting all mention of deploy-ha.sh's still-real
/tmp/apiserver legacy fallback (that script's discovery logic is
unchanged, unlike run-resource-verify.sh's, which genuinely lost its
fallback in task 3.1 -- xce-test-resources/SKILL.md's identical-
looking edit was correct for that reason). Fixed by restoring an
accurate, Makefile-free description of the real fallback at all four
touched sites. Re-reviewed and approved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
2026-09-03 14:22:54 +00:00

4 KiB

Testing Standards

Philosophy

  • TDD mandatory: Write a failing test first, then the minimal implementation. The cycle is Red → Green → Refactor.
  • Test behavior, not implementation details.
  • Tests must run without a live server or external services.
  • A change is not complete until /xce-build apiserver's unit-test step (and tests/regression/run.sh where applicable) passes.

Go Tests (apiserver / xcecli)

Co-located alongside source: jobs.go → jobs_test.go. Integration tests use descriptive names: integration_test.go, host_control_test.go.

Table-driven for validation/filter logic:

tests := []struct {
    name     string
    input    SomeRequest
    wantErr  bool
}{
    {"valid request", validReq, false},
    {"missing field", missingReq, true},
}
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        got, err := preCheckRequest(tt.input)
        if (err != nil) != tt.wantErr { t.Errorf(...) }
    })
}

State isolation — reset global store state between tests:

func resetRBACState() { /* clear in-memory stores */ }

func TestPermissions(t *testing.T) {
    t.Cleanup(resetRBACState)
    // ...
}

Fixtures: use t.TempDir() for file-system state; never rely on absolute paths or external services.

Build targets:

  • /xce-build apiserver — unit tests only (CGO_ENABLED=0), plus the build
  • meson test -C <build_dir> --suite apiserver — CGO-backed backend tests (requires nix dev shell + build dir)
  • tests/regression/run.sh — full regression suite (requires XCE and URL env vars, run directly)

C Tests (controller / scheduler / lib)

Location: testsuite/ directory inside each C component.

Note: src/worker has no testsuite/ directory and no meson test() registrations today — it currently carries no C test coverage of its own.

Structure: one .c test program per unit:

src/controller/testsuite/
    test.c                       ← test runner harness
    test_ha_cluster.c            ← tests for ha.c
    test_persist_wal.c           ← WAL-specific tests
    test_e2e_checkpoint.c        ← end-to-end checkpoint flow
    test_p1_integration.c        ← P1 HA integration tier

File naming convention: test_<unit>.c. E2E tests: test_e2e_<topic>.c. Integration tiers: test_p1_*.c, test_p2_*.c, test_p3_hardening.c.

Test-only symbols are guarded to prevent production bloat:

#ifdef XCE_TESTING
extern void reset_ha_state(ha_state_t *s);
#endif

Add a test case in testsuite/ before modifying any C implementation.

Verifying a Test Run Actually Passed

Meson's aggregate Ok:N Fail:0 is not sufficient evidence. Meson can report a suite Fail:0 while Unity sub-groups inside it fail — meson only tracks the process exit code of each meson test unit, not each Unity RUN_TEST. Always cross-check the standalone test binary's own stdout (controller_unit_test, xcelib_test, etc., run directly) for its authoritative :FAIL: count / N Tests M Failures summary before treating a run as green. This was the root cause of a masked-failure bug in the Unity harness itself (nested UNITY_BEGIN()/UNITY_END() across 83 controller/testsuite/*.c files swallowed the real exit code — fixed 2026-07-17) and is now the standard verification step for every /kiro-validate-impl pass.

Coverage Targets

  • Go: 80% minimum on business logic (controllers, services, store)
  • C: every new function in a daemon must have at least one test case in testsuite/
  • Integration: all public REST endpoints must have at least one integration test case in src/frontend/tests/regression/

What Not to Mock

  • Mock external dependencies only (network calls, file I/O that crosses component boundaries)
  • Do not mock the system under test
  • In-memory stores (store/) are real — use them directly in unit tests
  • CGO bridge (backend/) is covered by meson test -C <build_dir> --suite apiserver — do not stub it in unit tests