roundtrip

package
v2.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

SPDX-FileCopyrightText: 2026 The Crossplane Authors <https://crossplane.io>

SPDX-License-Identifier: Apache-2.0

SPDX-FileCopyrightText: 2026 The Crossplane Authors <https://crossplane.io>

SPDX-License-Identifier: Apache-2.0

Package roundtrip provides a testing utility library for verifying that Crossplane provider managed resources correctly survive serialization and version-conversion round trips.

The library exercises two kinds of invariants:

  1. Serialization round-trip: an object fuzzed with random data can be encoded to JSON/YAML and decoded back to an identical object.

  2. Conversion round-trip: an object converted spoke→hub→spoke (and hub→spoke→hub) is bit-identical to the original, proving that no data is lost across API version conversions registered via pkg/controller/conversion.RegisterConversions.

Basic usage:

func TestRoundTrip(t *testing.T) {
    provider, _ := config.GetProvider(t.Context(), schema, false)
    providerNamespaced, _ := config.GetNamespacedProvider(t.Context(), schema, false)

    testScheme := runtime.NewScheme()
    clusterapis.AddToScheme(testScheme)
    namespacedapis.AddToScheme(testScheme)

    rt, _ := roundtrip.NewRoundTripTest(provider, providerNamespaced, testScheme)

    t.Run("Serialization", rt.TestSerializationRoundtrip)
    t.Run("Conversion",    rt.TestConversionRoundtrip)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ASCIIStringFuzzer

func ASCIIStringFuzzer(s *string, c randfill.Continue)

ASCIIStringFuzzer is a randfill-compatible fuzzer function that fills string fields with random lowercase-alphanumeric strings of up to 29 characters. Register it via WithExtraFuzzFuncs or rely on the default that NewRoundTripTest includes it automatically.

Restricting the character set avoids encoding issues and makes test output readable when a diff is printed.

func EquateEmptyAndSingleZeroSlice

func EquateEmptyAndSingleZeroSlice() cmp.Option

EquateEmptyAndSingleZeroSlice returns a cmp.Option that treats an empty (or nil) slice as equal to a slice containing exactly one zero-value element:

  • pointer slices: []*T{} ≡ []*T{nil}
  • value slices: []T{} ≡ []T{zero} (zero is the zero value of T)

This handles conversions that produce []T{zero} where the source had []T{}.

Conflict avoidance: cmpopts.EquateEmpty (already in the default options) registers its own Comparer for the case where both sides have Len==0. This option requires at least one side to have Len==1, so the two Comparers are mutually exclusive and go-cmp never sees an ambiguous pair.

Multi-element slices and slices whose single element is non-zero fall through to go-cmp's normal element-by-element comparison.

Use with WithComparisonOptions:

rt, _ := roundtrip.NewRoundTripTest(provider, nil,
    roundtrip.WithComparisonOptions(roundtrip.EquateEmptyAndSingleZeroSlice()))

func EquateNilAndZeroValuePtr

func EquateNilAndZeroValuePtr() cmp.Option

EquateNilAndZeroValuePtr returns a cmp.Option that treats a nil pointer as equal to a pointer to an effectively-zero struct value:

(*Foo)(nil)  ≡  &Foo{}
(*Foo)(nil)  ≡  &Foo{Nested: []Bar{{}}}   // nested slices also effectively zero

Only struct pointer types are intercepted; pointers to scalars, maps, or other non-struct types fall through to go-cmp's default comparison.

Use with WithComparisonOptions:

rt, _ := roundtrip.NewRoundTripTest(provider, nil,
    roundtrip.WithComparisonOptions(roundtrip.EquateNilAndZeroValuePtr()))

Types

type FuzzFunc

type FuzzFunc = any

FuzzFunc is a randfill-compatible fuzzer function. The concrete value must have the signature func(*T, randfill.Continue) where T is the type whose instances should be fuzz-filled. Values are registered with randfill.Filler.Funcs and called during fuzzing. The alias preserves full compatibility with the k8s fuzzer infrastructure, which expects []interface{}.

type FuzzerOption

type FuzzerOption func(*fuzzerOptions)

FuzzerOption configures a single fuzzerOptions entry. Pass one or more to WithFuzzerConfig to register a new fuzzer configuration.

func FuzzerAllowUnexportedFields

func FuzzerAllowUnexportedFields(allow bool) FuzzerOption

FuzzerAllowUnexportedFields allows this fuzzer to fill unexported struct fields.

func FuzzerIterations

func FuzzerIterations(n int) FuzzerOption

FuzzerIterations sets the number of fuzz-fill + round-trip cycles this fuzzer configuration will run per (kind, version-pair).

func FuzzerMaxDepth

func FuzzerMaxDepth(d int) FuzzerOption

FuzzerMaxDepth sets the maximum recursion depth the fuzzer will descend into nested structs.

func FuzzerNilChance

func FuzzerNilChance(p float64) FuzzerOption

FuzzerNilChance sets the probability [0, 1] that pointer fields are left nil. Use 0 to force all pointers to be non-nil.

func FuzzerNumElements

func FuzzerNumElements(min, max int) FuzzerOption

FuzzerNumElements sets the min and max number of elements generated for maps and slices.

func FuzzerRandSource

func FuzzerRandSource(src rand.Source) FuzzerOption

FuzzerRandSource sets a deterministic random source for this fuzzer configuration.

func FuzzerSkipPatterns

func FuzzerSkipPatterns(patterns ...*regexp.Regexp) FuzzerOption

FuzzerSkipPatterns registers regexp patterns; any struct field whose name matches a pattern will be skipped by this fuzzer.

type RoundTripTest

type RoundTripTest struct {
	// contains filtered or unexported fields
}

RoundTripTest holds all state needed to run serialization and conversion round-trip tests for a single provider. Create it once with NewRoundTripTest and reuse it across sub-tests.

func NewRoundTripTest

func NewRoundTripTest(provider *config.Provider, providerNamespaced *config.Provider, scheme *runtime.Scheme, opts ...TestOption) (*RoundTripTest, error)

NewRoundTripTest constructs a RoundTripTest and performs one-time setup (scheme codec factory, conversion registration).

provider is the cluster-scoped provider configuration. providerNamespaced is the namespaced variant; pass nil if the provider only exposes cluster-scoped resources. scheme must have all provider types registered before being passed here. opts customise test behaviour.

func (*RoundTripTest) TestConversionRoundtrip

func (rt *RoundTripTest) TestConversionRoundtrip(t *testing.T)

TestConversionRoundtrip iterates over every API group registered in the scheme, discovers hub and spoke versions for each GroupKind, and runs both spoke→hub→spoke and hub→spoke→hub conversions, asserting that the result is identical to the input.

All fuzzer configurations registered via WithFuzzerConfig are exercised for each (kind, version-pair). Groups and kinds can be narrowed or excluded with the WithInclude* and WithExclude* options. Any GroupKind with fewer than two registered versions is skipped with a log message.

func (*RoundTripTest) TestSerializationRoundtrip

func (rt *RoundTripTest) TestSerializationRoundtrip(t *testing.T)

TestSerializationRoundtrip verifies that every type registered in the scheme and passing the include/exclude filters survives a JSON encode→decode cycle with no data loss. It delegates to the upstream k8s roundtrip helper. The same WithIncludeGroups/WithExcludeGroups/WithIncludeGroupKinds/ WithExcludeGroupKinds filters that apply to TestConversionRoundtrip also apply here. The first fuzzer configuration is used to build the fuzzer.

type TestOption

type TestOption func(*RoundTripTest)

TestOption is a functional option that customises a RoundTripTest.

func WithCodecFactory

func WithCodecFactory(c serializer.CodecFactory) TestOption

WithCodecFactory overrides the codec factory derived from the scheme. Use this when you need to control codec negotiation (e.g. to add custom serializers).

func WithComparisonOptions

func WithComparisonOptions(cmpOpts ...cmp.Option) TestOption

WithComparisonOptions appends additional cmp.Options to those used when comparing objects after a round trip.

func WithExcludeGroupKinds

func WithExcludeGroupKinds(groupKinds ...schema.GroupKind) TestOption

WithExcludeGroupKinds excludes the given GroupKinds from the conversion round-trip test.

func WithExcludeGroups

func WithExcludeGroups(groups ...string) TestOption

WithExcludeGroups excludes the given API groups from the conversion round-trip test.

func WithExtraFuzzFuncs

func WithExtraFuzzFuncs(fns ...FuzzFunc) TestOption

WithExtraFuzzFuncs appends additional randfill-compatible fuzzer functions (signature: func(*T, randfill.Continue)) to every fuzzer built by the test suite. This is useful to restrict a field to a valid value domain (e.g. only valid enum strings).

func WithFuzzerConfig

func WithFuzzerConfig(opts ...FuzzerOption) TestOption

WithFuzzerConfig adds a new fuzzer configuration to the test suite. The conversion tests run every registered configuration in sequence for each (kind, version-pair), accumulating coverage across different fuzz parameters.

Multiple calls each add a distinct configuration:

rt, _ := roundtrip.NewRoundTripTest(provider, nil,
    roundtrip.WithFuzzerConfig(
        roundtrip.FuzzerNilChance(0),
        roundtrip.FuzzerIterations(20),
    ),
    roundtrip.WithFuzzerConfig(
        roundtrip.FuzzerNilChance(0.5),
        roundtrip.FuzzerNumElements(0, 5),
    ),
)

When no WithFuzzerConfig is provided, a single default configuration is used (NilChance≈0.2, NumElements 0–1, 10 iterations).

func WithIncludeGroupKinds

func WithIncludeGroupKinds(groupKinds ...schema.GroupKind) TestOption

WithIncludeGroupKinds restricts the conversion round-trip test to the given GroupKinds. When neither WithIncludeGroups nor WithIncludeGroupKinds is set, all kinds registered in the scheme are tested.

func WithIncludeGroups

func WithIncludeGroups(groups ...string) TestOption

WithIncludeGroups restricts the conversion round-trip test to the given API groups. When neither WithIncludeGroups nor WithIncludeGroupKinds is set, all groups registered in the scheme are tested.

Jump to

Keyboard shortcuts

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