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 ¶
- Variables
- func CompareOrUpdate(t testing.TB, goldenPath string, got []byte, mode Mode)
- func Decode(scheme *runtime.Scheme, raw []byte) ([]client.Object, error)
- func Image(files map[string]string) *crfake.FakeImage
- func LoadFixture(dir, name string) ([]byte, error)
- func RespondHTTPOK()
- func Snapshot(ctx context.Context, cl client.Client, scheme *runtime.Scheme, ...) ([]byte, error)
- func SplitDocuments(doc []byte) []string
- type BytesNormalizer
- type Config
- type FakeLayer
- type Mode
- type ObjectNormalizer
- type SnapshotSpec
- type Suite
- func (s *Suite) AssertGolden()
- func (s *Suite) Client() client.Client
- func (s *Suite) Decode(raw []byte) []client.Object
- func (s *Suite) Dynamic() dynamic.Interface
- func (s *Suite) FixtureName() string
- func (s *Suite) Init(cfg Config)
- func (s *Suite) RESTMapper() meta.RESTMapper
- func (s *Suite) Request(name, namespace string) ctrl.Request
- func (s *Suite) Scheme() *runtime.Scheme
- func (s *Suite) Seed(name string)
- func (s *Suite) SeedObjects(name string, objs ...client.Object)
- func (s *Suite) SeedRaw(name string, raw []byte)
- func (s *Suite) TearDownSubTest()
Constants ¶
This section is empty.
Variables ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
SplitDocuments splits a multi-document YAML blob on `---` lines, dropping empty documents.
Types ¶
type BytesNormalizer ¶
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 ¶
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) 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 ¶
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 ¶
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) Dynamic ¶
Dynamic returns the dynamic fake client (only when Config.WithDynamic is set).
func (*Suite) FixtureName ¶
FixtureName returns the name of the currently loaded fixture; it is also the golden file name.
func (*Suite) Init ¶
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) Seed ¶
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 ¶
SeedObjects builds the client from pre-decoded objects, recording name as the golden file name.
func (*Suite) SeedRaw ¶
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.