2.9 KiB
2.9 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
make test(andmake test-integrationwhere 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:
make test— unit tests only (CGO_ENABLED=0)make test-backend— CGO-backed backend tests (requires nix dev shell + build dir)make test-integration— full regression suite (requiresXCE_BINandSRV_URLenv vars)
C Tests (controller / worker / scheduler / lib)
Location: testsuite/ directory inside each C component.
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.
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
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 bymake test-backend— do not stub it in unit tests