Test Architecture

This repository keeps tests close to the code they verify. Do not add generated design packages, captured work logs, or ad hoc harness dumps to main; keep historical evidence in GitCode issues, pull requests, or wiki pages.

For the migration record of the old generated tests/design_package tree, see docs/test-intent-migration.md.

Test Categories

flowchart LR
  Unit["Unit tests\npackage-local *_test.go\nno env, no network"]
  Offline["Offline integration tests\nhttptest or internal/testnet\nrun by default"]
  Live["Live E2E tests\nbuild tag e2e\nexplicit credentials"]
  Fixtures["testdata/\nsanitary reusable inputs"]
  Evidence["Issues, PRs, wiki\nhistorical evidence"]

  Unit --> Default["go test ./..."]
  Offline --> Default
  Fixtures --> Unit
  Fixtures --> Offline
  Live --> Explicit["go test -tags=e2e ./internal/e2e"]
  Evidence -. informs .-> Unit
  Evidence -. informs .-> Offline
Category Location Default run Rules
Unit tests Next to the package under cmd/ or internal/ Yes No live network, credentials, Keychain, SSH agent, or machine-local paths. Use package fakes and t.TempDir().
Offline integration tests Package-local tests using httptest, internal/testnet, temp SQLite caches, or CLI/MCP entrypoints Yes May exercise multiple packages, but must remain loopback-only and deterministic.
Live E2E tests internal/e2e/ No Must use //go:build e2e, explicit environment variables, redacted logs, and skip when credentials are absent.
Opt-in local benchmarks Package-local _test.go harnesses guarded by an explicit environment variable No Use public-safe synthetic inputs, no credentials, bounded defaults, and report machine/model metadata needed to interpret results. Keep generated reports out of main.
Fixture inputs testdata/ Indirectly Store only sanitized reusable inputs. Do not store generated design artifacts or raw API captures.
Historical evidence GitCode wiki, issue comments, PR reports, git history No Use for research, dogfood notes, decisions, and exploratory output that should remain discoverable without living in main.

Naming Rules

  • Prefer package-local _test.go files over a top-level tests/ directory for Go behavior.
  • Name reusable HTTP fakes by behavior, such as mock_gitcode_api_test.go, and keep them in the package that owns the surface they verify unless another package needs the same helper.
  • Move reusable offline HTTP helpers to internal/testnet when more than one package can reasonably use them.
  • Use neutral fixture names such as offline-smoke, example-repo, or sanitized-*. Avoid names that describe local dogfood history.
  • Use TestE2E... only for live tests behind the e2e build tag.
  • Use TestIntegration... only when a test crosses package or process boundaries; it still must be offline unless it also lives behind e2e.

Live Test Contract

Live tests must be opt-in:

go test -tags=e2e ./internal/e2e

They must read credentials from explicit environment variables, redact tokens, repository coordinates, and authorization headers from logs, and skip instead of failing when the environment is not configured. The default suite must keep this contract:

go test ./...

go test ./... must pass without network access, credentials, Keychain access, or an SSH agent.

Fixture Boundary

Fixtures are code inputs, not evidence archives. Before adding or changing a fixture, check that it is:

  • public-safe and sanitized;
  • small enough to review in a pull request;
  • reusable by a deterministic test;
  • free of raw tokens, cookies, private repository names, internal URLs, and machine-local paths.

Captured research, migration notes, dogfood reports, and generated design packages belong in wiki pages or issue/PR comments when they are still useful.

Repository-document fixtures live under testdata/repository-docs/ and contain only public-safe committed policy/document examples. Tests create temporary Git repositories for revision, rename, and tracked-worktree behavior and inspect SQLite bytes for sentinels to prove that document text is not persisted. Historical revision, offline full-text, overlay staleness, alias canonicalization, daemon writer admission, resume/reuse, and deterministic retention are package-level release gates. Retention tests must cover the machine-local vector-byte ceiling, preserve the newest ready set under byte pressure, and prove that membership, chunks, and vectors have no orphans after GC. Maintenance tests register a private worktree, poll exact HEAD/policy identity, enqueue only on change, recover the registration after daemon restart, and assert that public JSON never contains the filesystem path. Maintenance identity tests also enroll canonical and alias repository ids concurrently, migrate compatible and conflicting version-1 registry entries, project legacy registration/job references to the canonical identity, preserve failed-stage retry evidence without reviving superseded failures, and prove that genuinely different repositories in one cache remain separate. Admin browser coverage gates conflict plan/confirm, legacy deep-link redirect, and recovery before the canonical-identity UX cohort is considered complete. Browser CI is semantic-only: assert DOM roles/text, ARIA state, API JSON, status/count values, navigation, and action outcomes. Screenshot capture, reference composition, and pixel baselines are local opt-in QA and must be disabled whenever CI is set, even if their local environment variables leak into the runner. Playwright trace capture is also disabled in CI because its archive embeds raster screenshots. A pure configuration unit test feeds every local visual-QA variable into the CI policy and asserts that output/reference paths and pixel baselines are disabled and trace mode is off; it generates and compares no raster artifact. After the browser suite, CI also scans its result directories and fails if any raster image, video, or Playwright trace archive was produced, including by a test that bypassed the shared policy.

Before Committing

Run:

go test ./...
git diff --check

For changes that touch live E2E behavior, also run or explicitly report why you could not run:

go test -tags=e2e ./internal/e2e