Black-box E2E tests for HyperFleet. Specs call the HyperFleet API, create ephemeral resources, check adapter execution and the K8s resources that result, then clean up. Go 1.26, Ginkgo v2, Gomega, and the generic HTTP client in pkg/client.
Spec suites live in e2e/<suite>/, one directory per suite. Each suite is registered by a blank import in e2e/e2e.go; add one there when you create a suite.
Run make check before you call work done, then make build.
make check:generate,fmt-check,vet,lint, unit tests,verify-tools(fails iftools/go.moddrifted)make fmt: pinnedgolangci-lint fmt ./...appliesgofmtandgoimports;fmt-checkchecks the same formatters without rewriting filesmake gofmt: alias formake fmt(used by thehyperfleet-gofmtpre-commit hook)make go-vet: alias formake vet(used by thehyperfleet-go-vetpre-commit hook)make install-hooks: installs the pre-commit hooks (.pre-commit-config.yaml: secret scanning, commitlint, Go tooling, file hygiene)make lint: verifies.golangci.ymlagainst the v2 schema, then runs golangci-lint pinned intools/go.modmake test: unit tests for./pkg/...and./cmd/..., with race detection and coverage. E2E specs need a live environment; seedocs/setup.mdmake list-tests: dry run that lists specs by tier. Use it to check labels and names without a cluster; dry runs skip the startup entity drift check
- Writing specs, full conventions:
docs/development.md - Architecture and package layout:
docs/architecture.md - Environment setup (kind, GCP):
docs/setup.md - Running tests, Prow jobs, label-to-CI mapping:
docs/runbook.md - CI failures and log locations:
docs/debugging.md - Test case documents and templates:
test-design/(start attest-design/README.md) - Which layer a test belongs in (unit / integration / E2E): test placement strategy
- Config defaults, struct, file:
pkg/config/defaults.go,pkg/config/config.go,configs/config.yaml. Per-kind adapters, timeouts and thresholds: the descriptor table inpkg/config/descriptor.go - Pollers and matchers:
pkg/helper/pollers.go,pkg/helper/matchers.go. Reuse these before writing new ones - Logging standard: HyperFleet logging specification
- IMPORTANT: spec files end in
.go, never_test.go. Specs are compiled into thehyperfleet-e2ebinary, not run bygo test. The only_test.gofile ine2e/ise2e/e2e_suite_test.go, the ginkgo CLI entry point formake e2e-ci. Put no spec code there. - Path:
e2e/<suite>/descriptive-name.go. The package name matches the directory. - Describe name:
[Suite: <suite>][<category>] Description, for example[Suite: cluster][baseline] Cluster Resource Type Lifecycle. Match the categories already used in that suite.
- Every spec carries exactly one severity label:
labels.Tier0(blocks release),labels.Tier1(important),labels.Tier2(edge case, can defer). make testenforces this:pkg/labels/validate_test.goparses every spec ine2e/.- Optional labels are in
pkg/labels/labels.go.
Use Eventually with a poller and a matcher in the spec itself. Do not write WaitFor* helpers that hide Eventually.
Eventually(h.Poll(ctx, h.Entities.MustGet("Cluster"), "", clusterID), h.Entities.MustGet("Cluster").Timeouts.Reconciled, h.Cfg.Polling.Interval).
Should(helper.HaveResourceCondition(client.ConditionTypeReconciled, client.ResourceConditionStatusTrue))- For a one-off compound check, use
Eventually(func(g Gomega) { g.Expect(...) }).Should(Succeed()). Inside the closure, callg.Expect, not bareExpect. - Take per-kind timeouts from the descriptor (
h.Entities.MustGet(kind).Timeouts.Reconciled,.Deleted), andh.Cfg.Timeouts.Adapter.Processingandh.Cfg.Polling.Intervalfrom config. Never hardcode durations. - Mark major steps with
ginkgo.By(), but never inside anEventuallyclosure.
- Register
ginkgo.DeferCleanupright after you create each resource. Useh.DeferCleanup(entity, parentID, resourceID)for warning-only cleanup. - Exception: in an
Orderedcontainer where a later spec uses a resource an earlier spec created, register the cleanup inBeforeAll. ADeferCleanupmade inside anItruns when thatItends, before the later specs run. Say so in a comment, ase2e/adapter/adapter_with_remote_transport.godoes.
- Resolve payload paths with
h.TestDataPath("payloads/..."). Never hardcode atestdata/prefix, because CI overridesTESTDATA_DIR. - Payloads in
testdata/payloads/are Go templates. The available variables are thetemplateVarsstruct inpkg/client/payload.go.
When a spec checks created_by / deleted_by, guard the check. Audit checks are skipped when no identity is configured:
if expected := h.ExpectedIdentity(); expected != "" {
Expect(cluster).To(helper.HaveAuditIdentity(expected))
}-
Every live run starts with a read-only drift check:
helm get values hyperfleet-api --all -n "$NAMESPACE"must match the descriptor table, or the suite fails inSynchronizedBeforeSuitebefore any spec runs. SetNAMESPACEto the namespace that holds the API release and keephelm/KUBECONFIGable to read it. Seedocs/debugging.md#startup-entity-drift-check. -
Resolve a known descriptor after
helper.New()withh.Entities.MustGet("Cluster"); useLookupfor runtime kind names. Generic resource helpers take a descriptor and parent ID (empty for roots). -
Adapter names come from the descriptor row at runtime:
h.Entities.MustGet("Cluster").RequiredAdapters. Never hardcode them. The row inpkg/config/descriptor.goholds the hyperfleet-infra release names as defaults. YAML (adapters.<kind>,timeouts.<kind>) and env (HYPERFLEET_ADAPTERS_<KIND>,HYPERFLEET_TIMEOUTS_<KIND>_RECONCILED) override them by kind, lower case in YAML and upper case in env; an unknown kind in YAML or an empty adapter list fails config loading. -
Config priority: CLI flags >
HYPERFLEET_*env vars >configs/config.yaml>pkg/config/defaults.go(per-kind values: the descriptor rows inpkg/config/descriptor.go). Config file path:--config>HYPERFLEET_CONFIG>./configs/config.yaml. -
helper.New()callslog.Fatalfwhen the suite config is nil; callhelper.SetSuiteConfigfirst.Config.Validate()returns an error rather than panicking. Outside--dry-runit requiresAPI.URLand aRunIDthat is a valid K8s label value (RUN_ID), and it always checksbrokerType. -
Logging uses
log/slogdirectly. There is no custom logger wrapper.
- Never import
e2e/*packages frompkg/. - Keep E2E specs to user journeys and critical operations. Field validation and single-component behavior belong in unit or integration tests (see the test placement strategy above).