Building and Testing see history edit this page

Talks about: , , and

The host needs no Go toolchain. .breto.edn names the metio/toolchains groups that pin each tool to an exact version, and breto installs them and puts them first on PATH in the project’s shell:

eval "$(breto env)"

CI installs the same versions with the breto action, so a gate that is green locally is green there as well. breto’s prompt hook does the same on entering the project.

The toolchain carries the Go toolchain, the static-analysis suite, the docs and dashboard tooling, and the envtest assets. KUBEBUILDER_ASSETS points at the etcd + kube-apiserver + kubectl bundle of controller-tools’ envtest releases, so the envtest-backed tests run offline once it is installed.

Building

go build -o jaas .

The Dockerfile builds the production image. It accepts VERSION and COMMIT build args:

docker build -t ghcr.io/metio/jaas:dev .
docker build --build-arg VERSION=v1 --build-arg COMMIT=abc123 -t ghcr.io/metio/jaas:dev .

Regenerating generated code

The CRD manifests under config/crd/bases/ and api/v1/zz_generated.deepcopy.go are produced by controller-gen. Regenerate both after touching api/v1/ types, and commit the result:

generate

verify.yml’s generated job runs that same command and fails on any diff, so stale manifests cannot ship. The command is declared in .breto.edn and runs scripts/generate.sh — one definition the gate and this page both point at.

controller-gen itself comes from the go.mod tool directive, so its version lives there and Renovate bumps it.

Static analysis

golangci-lint is not used. The standalone tools below run directly, both in CI and in the development shell:

go vet ./...
staticcheck ./...     # config: staticcheck.conf, checks = ["all"]
gofumpt -l .          # empty output means clean; any output is a failure
gosec ./...           # inline #nosec justifications silence false positives
govulncheck ./...     # reachable-from-code advisories only
arch-go               # architecture rules; config: arch-go.yml
modernize ./...       # newer-Go idiom check

Test layers

Pure unit tests

Table-driven tests with no external state. They live next to the code they cover across internal/... and api/v1/. Several act as drift gates: conditions_test.go verifies that every Reason* constant has a matching docs/runbooks/<reason>.md, and TestErrorResponse_StableCodeValues pins the wire-level ErrCode* strings against accidental rename.

go test -count=1 -race -cover ./...

To run a single test by name:

go test -count=1 -v -run TestName ./internal/handler/

Envtest-backed operator tests

Files named envtest_*_test.go (in internal/operator/, internal/webhook/selfsigned/, and main_envtest_test.go at the repo root) boot a real kube-apiserver and etcd via controller-runtime’s envtest package and run the reconciler, webhook, and full run(...) function against them.

The tests share one apiserver instance per test binary, guarded by a sync.Once, so the startup cost is paid once. Each test t.Skips when KUBEBUILDER_ASSETS is unset — there is no build tag. The development shell exports it, so these tests run by default; on a host without the assets they silently skip.

The envtest harness sets Config.SkipImpersonation (the only place that setting is allowed) and defaults MetricsBindAddress to "0" so parallel test cases do not fight over the metrics port.

go test -count=1 -race -cover ./...

Golden / example end-to-end tests

examples_test.go boots the full binary via runInBackground and asserts HTTP responses against golden files under testdata/golden/. Comparison is semantic — both sides are parsed as JSON and compared on the parsed values, so whitespace and key ordering are irrelevant.

After changing an example or adding a new one, regenerate the golden files:

go test -update ./...

Inspect and commit the diff in testdata/golden/.

Fuzz tests

Fuzz targets in internal/handler/, internal/sources/, and internal/urlguard/ harden the request path, the tar/gzip artifact unpacker, and the SSRF URL/IP parser against adversarial input. CI exercises their seed corpus as ordinary unit tests. To fuzz interactively:

go test -fuzz=FuzzName -fuzztime=30s ./internal/urlguard/

Benchmarks

Throughput benchmarks in internal/eval/, internal/storage/, and internal/operator/ cover reconcile throughput, watch mapping, and the tenant-client cache. They are baselines, not merge gates. The reconcile benchmark is envtest-backed and skips without KUBEBUILDER_ASSETS.

go test -bench=. -benchmem -run=^$ ./internal/operator/

Kind operator smoke tests

The cluster-level layer runs outside go test. Pure-kubectl bash scenarios in hack/smoke/ run against a real kind cluster via .github/workflows/kind-smoke.yml. To run a scenario locally against any reachable cluster, deploy JaaS and invoke the scenario scripts directly:

hack/smoke/scenario-basic.sh

See CI and releases for how the smoke layer fits into the two-angle end-to-end strategy.