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.gofiles over a top-leveltests/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/testnetwhen more than one package can reasonably use them. - Use neutral fixture names such as
offline-smoke,example-repo, orsanitized-*. Avoid names that describe local dogfood history. - Use
TestE2E...only for live tests behind thee2ebuild tag. - Use
TestIntegration...only when a test crosses package or process boundaries; it still must be offline unless it also lives behinde2e.
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