reconcilertest

package
v1.77.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 17, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

Documentation

Overview

Package reconcilertest is a reusable test framework for controller-runtime reconcilers. It factors out the scaffolding that used to be copy-pasted across every controller test in deckhouse-controller:

  • a shared -golden flag with write/compare logic (see golden.go);
  • scheme-based YAML fixture decoding and seeding (see seed.go), which replaces the hand-written `switch obj.Kind` blocks;
  • golden snapshots of cluster state (see snapshot.go);
  • registry/OCI and HTTP fakes (see regmock.go, httpmock.go);
  • an embeddable testify Suite (this file) that ties the above together.

A controller test embeds Suite, calls Init once with a Config describing which resources to seed and snapshot, and uses Seed/Client to drive its own private reconciler. The framework's TearDownSubTest compares the resulting cluster state against a golden file automatically.

Index

Constants

This section is empty.

Variables

View Source
var Update bool

Update reports whether golden files should be (re)generated instead of compared. It is bound to the shared -golden test flag, so every package that embeds this framework uses the same flag: `go test ./... -golden`.

Functions

func CompareOrUpdate

func CompareOrUpdate(t testing.TB, goldenPath string, got []byte, mode Mode)

CompareOrUpdate writes got to goldenPath when Update is set, otherwise it asserts that got matches the content stored at goldenPath using the given mode.

func Decode

func Decode(scheme *runtime.Scheme, raw []byte) ([]client.Object, error)

Decode turns a multi-document YAML blob into typed client objects using the supplied scheme. Unlike the hand-written `switch obj.Kind` blocks that this framework replaces, it relies on the scheme to map apiVersion/kind to a Go type, so any registered resource is supported automatically.

It instantiates the typed object from the scheme by GVK and then unmarshals with sigs.k8s.io/yaml (which, like the legacy `assembleInitObject` helpers, matches struct json tags case-insensitively). This preserves the exact decoding semantics the existing golden files were generated with.

func Image

func Image(files map[string]string) *crfake.FakeImage

Image returns a fake OCI image with a single layer carrying files. Pass nil when the test only needs the image to exist without specific content.

func LoadFixture

func LoadFixture(dir, name string) ([]byte, error)

LoadFixture reads a fixture file from dir. It returns an empty slice (and no error) when name is empty, which lets callers seed a cluster with no objects.

func RespondHTTPOK

func RespondHTTPOK()

RespondHTTPOK configures the shared test dependency container's HTTP client to answer every request with a 200 OK. This is the most common expectation across controller suites (module readiness probes, doc builder pings, ...).

func Snapshot

func Snapshot(ctx context.Context, cl client.Client, scheme *runtime.Scheme, spec SnapshotSpec) ([]byte, error)

Snapshot lists every requested kind from the cluster, normalizes the objects, marshals them to YAML and joins them with `---` separators. The output is byte-compatible with the hand-written `fetchResults` helpers it replaces: objects are listed in the same order, get their GVK set, and are marshalled with sigs.k8s.io/yaml.

func SplitDocuments

func SplitDocuments(doc []byte) []string

SplitDocuments splits a multi-document YAML blob on `---` lines, dropping empty documents.

Types

type BytesNormalizer

type BytesNormalizer func([]byte) []byte

BytesNormalizer post-processes the serialized snapshot, used for normalizations that are easier to express as text substitutions (e.g. regex on timestamps).

type Config

type Config struct {
	// Scheme used to decode fixtures, build the client and snapshot results.
	// Defaults to the shared project.Scheme() (v1alpha1, v1alpha2, core, apps,
	// coordination, apiextensions), which covers every deckhouse-controller CRD.
	Scheme *runtime.Scheme

	// StatusSubresources are registered on the fake client so that status
	// updates from the reconciler are persisted (mirrors WithStatusSubresource).
	StatusSubresources []client.Object

	// SeedStatusSubresources lists kinds whose .status must survive seeding.
	// The fake client strips the status of a status subresource on Create, so a
	// fixture that carries a non-empty status (e.g. a pre-existing release in a
	// terminal phase) would otherwise lose it. For every seeded object whose
	// kind is listed here and whose status is non-zero, the framework restores
	// the fixture status with a Status().Update right after Create, interleaved
	// in fixture order to keep resourceVersion sequencing identical to the
	// hand-written seeders this replaces. Only meaningful together with
	// SeedViaCreate; kinds listed here should also appear in StatusSubresources.
	SeedStatusSubresources []client.Object

	// SnapshotKinds lists the resource kinds dumped into the golden snapshot,
	// in order.
	SnapshotKinds []schema.GroupVersionKind

	// ObjectNormalizers / BytesNormalizers stabilise non-deterministic fields in
	// the snapshot before comparison.
	ObjectNormalizers []ObjectNormalizer
	BytesNormalizers  []BytesNormalizer

	// GoldenMode selects per-document or whole-document comparison.
	GoldenMode Mode

	// WithDynamic also builds a dynamic fake client and a static RESTMapper,
	// available via Dynamic() and RESTMapper(); needed by controllers that read
	// arbitrary resources (e.g. objectkeeper).
	WithDynamic bool

	// SeedViaCreate seeds the cluster by calling Create on an empty client
	// instead of pre-loading objects via WithObjects. This matches suites whose
	// golden files were generated that way (resourceVersion sequencing differs
	// between the two approaches).
	SeedViaCreate bool

	// TestdataDir is the fixtures directory (default "./testdata"). Golden files
	// live in <TestdataDir>/<GoldenSubdir>.
	TestdataDir  string
	GoldenSubdir string

	// SkipTestEnv disables setting D8_IS_TESTS_ENVIRONMENT=true during Init.
	SkipTestEnv bool
}

Config declares the static, per-suite behaviour of the framework.

type FakeLayer

type FakeLayer struct {
	crv1.Layer

	FilesContent map[string]string
}

FakeLayer is an in-memory OCI layer whose uncompressed content is a tar archive built from FilesContent (filename -> content). It mirrors the long-standing helper in module-controllers/utils so registry-backed controllers can be tested without a real registry.

func (FakeLayer) Size

func (fl FakeLayer) Size() (int64, error)

func (FakeLayer) Uncompressed

func (fl FakeLayer) Uncompressed() (io.ReadCloser, error)

type Mode

type Mode int

Mode controls how a golden snapshot is compared with its expected file.

const (
	// PerDocument splits both the actual and expected snapshots into YAML
	// documents and compares them one by one. This gives clearer diffs for
	// snapshots that contain many resources.
	PerDocument Mode = iota
	// WholeDocument compares the whole snapshot as a single YAML document.
	WholeDocument
)

type ObjectNormalizer

type ObjectNormalizer func(client.Object)

ObjectNormalizer mutates a listed object in place before it is marshalled, used to stabilise non-deterministic fields (timestamps, generated messages, ...).

type SnapshotSpec

type SnapshotSpec struct {
	// Kinds lists, in order, the resource kinds to include in the snapshot.
	Kinds []schema.GroupVersionKind
	// ObjectNormalizers are applied to every listed object before marshalling.
	ObjectNormalizers []ObjectNormalizer
	// BytesNormalizers are applied to the full serialized output.
	BytesNormalizers []BytesNormalizer
}

SnapshotSpec describes how to dump cluster state into a stable YAML snapshot.

type Suite

type Suite struct {
	suite.Suite
	// contains filtered or unexported fields
}

Suite is an embeddable testify suite providing the framework's building blocks.

func (*Suite) AssertGolden

func (s *Suite) AssertGolden()

AssertGolden snapshots the current cluster state and compares it (or updates the golden file when -golden is set). It is a no-op for skipped subtests.

func (*Suite) Client

func (s *Suite) Client() client.Client

Client returns the fake controller-runtime client seeded by Seed*.

func (*Suite) Decode

func (s *Suite) Decode(raw []byte) []client.Object

Decode turns a YAML blob into typed objects using the suite scheme.

func (*Suite) Dynamic

func (s *Suite) Dynamic() dynamic.Interface

Dynamic returns the dynamic fake client (only when Config.WithDynamic is set).

func (*Suite) FixtureName

func (s *Suite) FixtureName() string

FixtureName returns the name of the currently loaded fixture; it is also the golden file name.

func (*Suite) Init

func (s *Suite) Init(cfg Config)

Init stores the configuration, applies defaults, parses test flags and (unless disabled) marks the process as a test environment. Call it once, typically from the embedding suite's SetupSuite.

func (*Suite) RESTMapper

func (s *Suite) RESTMapper() meta.RESTMapper

RESTMapper returns the static RESTMapper (only when Config.WithDynamic is set).

func (*Suite) Request

func (s *Suite) Request(name, namespace string) ctrl.Request

Request builds a reconcile request for the given name/namespace.

func (*Suite) Scheme

func (s *Suite) Scheme() *runtime.Scheme

Scheme returns the scheme used by the suite.

func (*Suite) Seed

func (s *Suite) Seed(name string)

Seed loads <TestdataDir>/<name>, decodes it and builds the client. The name is also recorded as the golden file name. An empty or missing-content fixture results in an empty cluster, matching the legacy behaviour.

func (*Suite) SeedObjects

func (s *Suite) SeedObjects(name string, objs ...client.Object)

SeedObjects builds the client from pre-decoded objects, recording name as the golden file name.

func (*Suite) SeedRaw

func (s *Suite) SeedRaw(name string, raw []byte)

SeedRaw decodes an already-produced YAML blob (e.g. rendered from a template) and builds the client, recording name as the golden file name.

func (*Suite) TearDownSubTest

func (s *Suite) TearDownSubTest()

TearDownSubTest runs the golden assertion after every subtest. Embedding suites that need a custom TearDownSubTest should call AssertGolden() themselves.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL