Skip to main content

Testing Guide

Overview

Clawker uses a multi-tier testing strategy with no build tags — test categories are separated by directory.

Running Tests

Additional Makefile Targets

The Makefile prefers gotestsum (if installed) for human-friendly output with icons and colors, falling back to go test.

Running Specific Tests

Golden File Testing

Standard Golden Files

Some tests compare output against golden files or recorded data. To update after intentional changes:
Current golden file tests:

Firewall Corefile Golden

The firewall package has a golden file test for CoreDNS config generation (controlplane/firewall/coredns_config_test.go). The golden file at controlplane/firewall/testdata/corefile_basic.golden must be hand-edited to update.

Storage Oracle + Golden Strategy

The internal/storage package uses a defense-in-depth approach with two independent guards for merge correctness: Golden values are code (struct literals), not files — they must be hand-edited to change. make storage-golden prints new values with interactive confirmation. The STORAGE_GOLDEN_BLESS env var is specific to this one test (no global sweep risk).

Local Development Environment

The make localenv target creates an isolated XDG directory tree for manual UAT without polluting your real config:
This creates bare XDG parent dirs only (.config/, .local/share/, .local/state/, .cache/). The CLI creates its own clawker/ subdirectories on first use (e.g., clawker project init). The exported env vars point to the app-level paths so the storage resolver picks them up.

Writing Tests

Isolated Test Environments (internal/testenv)

The testenv package provides unified, progressively-configured test environments for any test that needs XDG directory isolation. It eliminates duplicated directory setup across test helpers.

Writing Config Files in Tests

Use WriteYAML to place config files at canonical locations:
Available ConfigFile constants: ProjectConfig, ProjectConfigLocal, Settings, EgressRules, ProjectRegistry.

Delegation

Higher-level helpers delegate to testenv:
  • configmocks.NewIsolatedTestConfig(t)testenv.New(t, testenv.WithConfig())
  • projectmocks.NewTestProjectManager(t, gf)testenv.New(t, testenv.WithProjectManager(gf))
  • test/e2e/harness.NewIsolatedFS()testenv.New(h.T) + project dir + chdir

Test Infrastructure

Each package in the dependency DAG provides test utilities so dependents can mock the entire chain: Rule: If a dependency node lacks test infrastructure, add it before writing tests that depend on it.

Command Test Pattern

Commands are tested using the Cobra+Factory pattern with mocks.FakeClient. Each command’s test file typically defines a testFactory helper that wires the minimum closures needed (Config, Logger, Client, etc.). The pattern looks like:

Three Test Tiers for Commands

E2E Test Harness (test/e2e/harness/)

For E2E tests exercising the full stack with real Docker:
Pass real constructors for any dependency you want to exercise against Docker. Some nil fields use test fakes (configmocks.NewBlankConfig, mocks.FakeClient, hostproxytest.MockManager, adminv1mocks.AdminServiceClientMock), while Logger always creates a real, settings-driven file logger via logcfg.New (mirroring production), and ProjectManager, GitManager, and SocketBridge default to nil.

Harness Types

Harness Functions

Cleanup

NewIsolatedFS registers a single cleanup chain:
  1. Stop daemons (firewall down, host-proxy stop)
  2. Remove shared firewall infrastructure containers (by purpose=firewall label)
  3. Remove control plane container (by purpose=controlplane label)
  4. Remove test-labeled containers, volumes, networks (by dev.clawker.test.name label)
  5. Remove test-labeled images (by dev.clawker.test.name label)
On failure, dumps clawker.log, hostproxy.log, and clawker-controlplane.log from the test’s state dir.

Project Test Double Scenarios

Use internal/project/mocks/stubs.go to pick the lightest project dependency double: Example:

Key Conventions

  1. All tests must pass before any change is completemake test at minimum
  2. No build tags — test categories separated by directory
  3. Always use t.Cleanup() for resource cleanup
  4. Use context.Background() in cleanup functions — parent context may be cancelled
  5. Unique agent names — include timestamp + random suffix for parallel safety
  6. Never import test/e2e/harness in co-located unit tests — too heavy (pulls Docker SDK)
  7. Never call factory.New() in tests — construct &cmdutil.Factory{} struct literals directly
  8. Docker resource labeling — all test resources carry dev.clawker.test=true + dev.clawker.test.name=TestName; whail tests use com.whail.test.managed=true
  9. Use make test-clean to remove leaked Docker resources from failed test runs