Skip to content

test: make unit and envtest suites hermetic and deterministic (#67) - #79

Open
senolcolak wants to merge 7 commits into
mainfrom
feat/67-hermetic-test-foundation
Open

senolcolak wants to merge 7 commits into
mainfrom
feat/67-hermetic-test-foundation

Conversation

@senolcolak

Copy link
Copy Markdown
Collaborator

Summary

First PR of the testing-foundation series. Makes the operator's test suites hermetic (no network, no source mutation) and deterministic, and separates the pure-unit layer from the API-server integration layer.

Closes #67.

What changed

  • Build-tag split. Controller envtest suites are gated behind //go:build envtest. The unit layer (webhook defaulter/validator specs, fixture builders) runs with plain go test — no Kubernetes API server, no KUBEBUILDER_ASSETS.
  • Webhook suite → pure unit. Specs already call Default/Validate directly, so the envtest + webhook-server bootstrap (which nothing exercised through admission) is replaced by a minimal RunSpecs entrypoint with RandomizeAllSpecs enabled.
  • Typed builders replace captured YAML. test/builders/monitor.go reproduces only the fields the controller consumes, via functional options, with a contract test (monitor_test.go) that asserts the selector labels, mon container --fsid/--id=/--public-addr=/--setuser-match-path= args, the ROOK_CEPH_MON_HOST secretKeyRef, and the three named volumes — so the builders cannot silently drift from what the controller rewrites. Deletes the 4 fixtures under contrib/k8s/test/.
  • Hermetic make targets. test-unit, test-unit-repeat (loops 20×, -race, fresh shuffle/Ginkgo seed each run to expose order dependence), test-envtest, test-all, test. None mutate source or clone Rook over the network.
  • Design doc under docs/superpowers/specs/.

Verification

  • go build ./... and go vet ./... clean in both plain and -tags envtest modes.
  • Unit suites pass with -race -shuffle=on.
  • envtest controller suite passes 14/14 specs against the new Go builders.

Known follow-ups (not blockers)

Copilot AI lite review requested due to automatic review settings September 3, 2026 10:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new monitor keyring secret builder stores base64 text in Secret.Data instead of decoded bytes, which makes the fixture diverge from real Kubernetes behavior and can mask controller-copy semantics.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR introduces a hermetic, deterministic testing foundation by separating pure unit tests (webhooks + typed fixture builders) from controller envtest integration tests, and replacing file-based YAML fixtures with typed Go builders.

Changes:

  • Split envtest suites behind //go:build envtest and simplify webhook tests into pure unit specs with randomized order.
  • Replace captured Rook monitor YAML fixtures with typed builders plus a contract test to guard against drift.
  • Add hermetic make test-unit, test-unit-repeat, and test-envtest targets; update make test to run the layered suite without mutating source or cloning dependencies.
File summaries
File Description
test/builders/monitor.go Adds typed builders for monitor Deployment/Secrets/ConfigMap to replace YAML fixtures.
test/builders/monitor_test.go Adds contract tests to lock down the builder fields the controller relies on.
pkg/webhook/v1alpha1/suite_test.go Converts webhook suite to pure unit Ginkgo entrypoint (no envtest/bootstrap).
pkg/controller/suite_test.go Gates controller suite behind envtest build tag and switches fixtures to builders.
pkg/controller/remotecluster_controller_test.go Adds envtest build tag to exclude from unit runs.
pkg/controller/remotearbiter_controller_test.go Adds envtest build tag to exclude from unit runs.
Makefile Adds hermetic unit/envtest targets and makes test run them without pretty/deps.
docs/superpowers/specs/2026-09-03-testing-foundation-design.md Documents the testing-foundation architecture and rollout plan.
contrib/k8s/test/override-configmap.yaml Removes YAML fixture (replaced by typed builder).
contrib/k8s/test/mon-deployment.yaml Removes YAML fixture (replaced by typed builder).
contrib/k8s/test/keyring-secret.yaml Removes YAML fixture (replaced by typed builder).
contrib/k8s/test/env-var-secret.yaml Removes YAML fixture (replaced by typed builder).
Review details
  • Files reviewed: 12/12 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/builders/monitor.go
Comment thread test/builders/monitor.go
Comment thread test/builders/monitor_test.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The envtest path still depends on missing/unvendored Rook CRDs (contrib/k8s/3rdparty) and the new keyring builder currently mishandles YAML base64 Secret data unless decoding is added, breaking the “fresh checkout” hermetic goal.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

docs/superpowers/specs/2026-09-03-testing-foundation-design.md:38

  • This design doc states there is no build/test CI in the repo, but then claims #66 has already shipped .github/workflows/ci.yaml (and references shipped jobs). In this PR branch, .github/workflows/ci.yaml is not present, so these statements are inconsistent and will mislead readers about current status vs planned work.

test/builders/monitor.go:221

  • MonitorKeyringSecret currently stores the base64 string bytes into Secret.Data; once defaultKeyringData is decoded, this should use the decoded byte slice so the controller copies/mounts the real keyring content.
	return &corev1.Secret{
		TypeMeta:   metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
		ObjectMeta: metav1.ObjectMeta{Name: o.name, Namespace: o.namespace},
		Data:       map[string][]byte{"keyring": []byte(defaultKeyringData)},
	}

test/builders/monitor.go:34

  • defaultKeyringData is the base64 string from the YAML Secret data: field, but corev1.Secret.Data must contain decoded bytes. Decode the base64 string once at build time so envtests exercise the controller with a realistic keyring payload.
	// keyring value copied verbatim from the captured keyring-secret.yaml.
	defaultKeyringData = "Clttb24uXQoJa2V5ID0gQVFCVm1RTnAxSm94SkJBQW5GUHVZUGZhcUZORkJIWUhCb0N0VGc9PQoJY2FwcyBtb24gPSAiYWxsb3cgKiIKCgpbY2xpZW50LmFkbWluXQoJa2V5ID0gQVFCVm1RTnAxbEF2SlJBQVp0V3ZZdDZnYXpPMWdTR1M5SEtYSFE9PQoJY2FwcyBtZHMgPSAiYWxsb3cgKiIKCWNhcHMgbW9uID0gImFsbG93ICoiCgljYXBzIG9zZCA9ICJhbGxvdyAqIgoJY2FwcyBtZ3IgPSAiYWxsb3cgKiIK"
)

type options struct {
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread Makefile
Copilot AI review requested due to automatic review settings September 3, 2026 15:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new monitor keyring Secret builder stores base64-encoded fixture data as literal bytes, diverging from real Kubernetes-decoded Secret.Data and risking incorrect envtest behavior/fidelity.

Review details

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

docs/superpowers/specs/2026-09-03-testing-foundation-design.md:31

  • This status note claims #66/#68 already shipped and references .github/workflows/ci.yaml, but that workflow file is not present in this PR branch. As written, the doc reads as if CI work is already in-tree, which is misleading given this PR’s scope (#67 only).

This issue also appears on line 36 of the same file.

test/builders/monitor.go:233

  • MonitorKeyringSecret sets Secret.Data["keyring"] to the literal bytes of defaultKeyringData, but defaultKeyringData is copied from the deleted YAML fixture’s data: field (base64-encoded on the wire). When the YAML was deserialized previously, Kubernetes decoded the base64 into the real keyring bytes; the new builder currently double-encodes the keyring and will cause the controller to propagate the wrong secret contents in envtest (it copies s.monitorKeyringSecret.Data verbatim). Decode the base64 string (or store the decoded keyring text directly) before assigning to Data.
func MonitorKeyringSecret(opts ...Option) *corev1.Secret {
	o := resolve(defaultKeyringSecret, "", opts)
	return &corev1.Secret{
		TypeMeta:   metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
		ObjectMeta: metav1.ObjectMeta{Name: o.name, Namespace: o.namespace},
		Data:       map[string][]byte{"keyring": []byte(defaultKeyringData)},
	}

docs/superpowers/specs/2026-09-03-testing-foundation-design.md:36

  • The #66 deliverable row states .github/workflows/ci.yaml and its jobs are already shipped, but that workflow isn’t present in this PR. Mark this as planned/future work (or remove detailed job claims) to avoid documenting non-existent artifacts.
| #66 | `.github/workflows/ci.yaml` — SHA-pinned, least-privilege, concurrency-cancel, timeouts, artifact-on-failure. Shipped jobs: `generate` (gen/helm/imports diff), `verify` (fmt/vet/lint/unit-race-shuffle/build), `envtest`, `helm` (lint+template), `vuln` (govulncheck); REUSE runs from the separate `reuse.yaml`. Documented required-check names. Split fast (PR) vs scheduled (nightly). |
  • Files reviewed: 12/12 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Separate pure unit tests from envtest integration tests and remove the
non-hermetic, source-mutating test workflow.

- Gate controller envtest suites behind the `envtest` build tag; the unit
  layer (webhook defaulter/validator specs, fixture builders) now runs with
  plain `go test` and no Kubernetes API server or KUBEBUILDER_ASSETS.
- Convert the webhook suite to pure unit tests: the specs call
  Default/Validate directly, so the envtest + webhook-server bootstrap that
  nothing exercised is removed in favor of a minimal RunSpecs entrypoint with
  RandomizeAllSpecs enabled.
- Replace the captured mon-deployment.yaml (+ secret/configmap) fixtures with
  typed Go builders in test/builders, reproducing only the fields the
  controller consumes, with functional options and a contract test.
- Add make targets that do not mutate source or clone Rook over the network:
  test-unit (race, shuffle), test-unit-repeat (20x for order dependence),
  test-envtest (explicit assets, envtest tag), test-all; redefine `test`.

RandomizeAllSpecs is left off the controller suite pending #78 (a pre-existing
finalizer-cleanup order dependence surfaced by this work). Package-level
-shuffle=on is kept.

Refs #67

Signed-off-by: senol.colak <senol.colak@sap.com>
… the strip path

The mon fixture omitted --mon-host and --mon-initial-members, so the
controller's modifyContainers strip branches for those args were never
exercised by the envtest suite. The source mons this builder replaces carry
both. Add them to the fixture.

docs: reconcile the testing-foundation design doc with what shipped — mark #76
as planned (PR 4, not delivered), and correct the #66 job list to the jobs that
actually exist in ci.yaml (generate/verify/envtest/helm/vuln + REUSE from
reuse.yaml) instead of the aspirational names (crd-validate, race, etc.).

Signed-off-by: senol.colak <senol.colak@sap.com>
…ontract test

Address PR #79 review: the chown initContainer had no Args, so the controller's
modifyContainers rewrite of a /var/lib/ceph/mon/ceph- path arg was never
exercised for that container. Restore the real Rook chown args (chown -R
ceph:ceph over the ceph dirs, including the mon data dir) and add a contract-test
assertion that the initContainer carries a rewritable mon path.

Also correct the Makefile 'test' comment: the suite does not clone Rook, but the
envtest CRDs come from contrib/k8s/3rdparty/ (populated by 'make deps', a one-time
network clone) and 'make env' downloads kube binaries — so it is reproducible,
not fully network-free.

Signed-off-by: senol.colak <senol.colak@sap.com>
@senolcolak
senolcolak force-pushed the feat/67-hermetic-test-foundation branch from 448ac8f to e085375 Compare September 4, 2026 11:36
marcelradke
marcelradke previously approved these changes Sep 7, 2026

@marcelradke marcelradke left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
I could run the unit tests
Seems a good preparation for further unit tests:
Replacing the yaml files to go-structures avoids local files and enables possible variations more easily.

@jrse
jrse self-requested a review September 24, 2026 07:33
senolcolak and others added 4 commits September 24, 2026 09:51
* chore: add marcelradke and jsre as code owner

Signed-off-by: senol.colak <senol.colak@sap.com>

* chore: add jrse (Jan Radon) as code owner

Signed-off-by: senol.colak <senol.colak@sap.com>

---------

Signed-off-by: senol.colak <senol.colak@sap.com>
* fix(lima): replace flannel with cilium and fix containerd config

- Replace Flannel v0.24.0 with Cilium v1.20.1 as the CNI plugin
- Install Helm and Cilium CLI in the VM provisioning
- Fix containerd crash caused by duplicate TOML CRI plugin table:
  use a drop-in config in conf.d/ instead of appending to config.toml
- Update kubeadm config API from v1beta3 to v1beta4 for K8s 1.34+
- Set cilium-operator replicas to 1 for single-node cluster
- Increase CoreDNS probe timeout to 600s for Cilium bootstrap time
- Kubernetes 1.34.1 -> 1.37.0
- Rook 1.18.6 -> 1.20.7
- cert-manager v1.19.2 -> v1.21.1
- Ceph image quay.io/ceph/ceph:v19 -> v19.2.4 (test pin patch version)

Signed-off-by: Jan Radon <jan.fabian.radon@sap.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore(contrib): upgrade VM base image to Ubuntu 26.04 LTS

Signed-off-by: Jan Radon <jan.fabian.radon@sap.com>

* fix(lima): add netplanOptional workaround for Ubuntu 26.04 boot hang

Signed-off-by: Jan Radon <jan.fabian.radon@sap.com>

---------

Signed-off-by: Jan Radon <jan.fabian.radon@sap.com>
Bumps golang from 1.26.2 to 1.27.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.27.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@jrse

jrse commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

@dependabot rebase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make unit and envtest suites hermetic, deterministic, and diagnosable

5 participants