Skip to content

feat: API roundtrip testing library #636

Merged
erhancagirici merged 11 commits intocrossplane:mainfrom
erhancagirici:roundtrip-test-library
May 7, 2026
Merged

feat: API roundtrip testing library #636
erhancagirici merged 11 commits intocrossplane:mainfrom
erhancagirici:roundtrip-test-library

Conversation

@erhancagirici
Copy link
Copy Markdown
Collaborator

@erhancagirici erhancagirici commented Apr 28, 2026

Description of your changes

This PR introduces pkg/apitesting/roundtrip, a reusable testing utility library, that providers built on upjet
can drop into their test suites to automatically verify the roundtrip correctness of their generated API types.

Similar to the approach in the Kubernetes ecosystem, the library utilizes fuzz-based round-trip testing: generate
randomly filled objects and assert they survive the full encode/decode or conversion cycle unchanged.
This library extracts that common boilerplate into a single, maintained place so providers can opt in with minimal setup.

Two categories of invariants are covered:

  • Serialization — every registered API type can be JSON-encoded and decoded back to an identical object. This catches incorrect field types, and other schema mismatches that would cause silent data loss when Kubernetes stores or returns a resource.
  • Version conversion — every multi-version type survives a full hub↔spoke round trip (spoke → hub → spoke and hub → spoke → hub) with no data loss.
  • This ensures that upjet's generated conversion functions faithfully preserve all fields across API versions.

Note: The tests focus on the serialization/conversion machinery aspect.
The test data is fuzz-based and does not focus on "semantically valid" API objects of the corresponding external provider API.

How it works

Serialization test

This test utilizes upstream k8s "k8s.io/apimachinery/pkg/api/apitesting/roundtrip" for serialization tests.

  1. Discover all API types registered in the scheme (respecting include/exclude filters).
  2. For each type, generate a randomly filled instance using the fuzzer.
  3. Encode the object to JSON and decode it back into a new instance.
  4. Assert the two instances are equal.

Conversion test

  1. Discover all API groups and GroupKinds registered in the scheme.
  2. For each GroupKind with more than one version, identify which version is the hub and which are spokes (based on the conversion.Hub / conversion.Convertible interfaces).
    • Skips GroupKinds with one available version
  3. For every hub↔spoke pair, run two sub-tests:
    • spoke → hub → spoke: fill a spoke object, convert to hub, convert back to a new spoke, compare.
    • hub → spoke → hub: fill a hub object, convert to spoke, convert back to a new hub, compare.
  4. Each sub-test repeats the cycle N times with freshly fuzzed/randomized data (configurable, default 5 iterations).
  5. Assert the final object is identical to the original using go-cmp with any registered comparison options.

Wiring at providers

Provider wires the library once in a test file, supplying the upjet provider object and a runtime scheme with types registered:
The test runner discovers all types and API versions registered in the scheme automatically — no per-resource configuration is needed.
Optionally, some test behavior can be customized. See Customization section.

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

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

    rt, _ := roundtrip.NewRoundTripTest(provider, providerNamespaced, testScheme
	// ... customization options 
	)
    t.Run("Serialization", rt.TestSerializationRoundtrip)
    t.Run("Conversion",    rt.TestConversionRoundtrip)
}

Customisation

When provider-specific quirks require it, the library exposes options to tune behavior without modifying shared code:

  • API Filtering — include or exclude specific API groups or GroupKinds from testing.
  • Fuzzer tuning — configure nil probability, slice sizes, iteration count, or register custom fill functions for fields that must match a specific value domain (e.g. enum strings).
  • Multiple fuzzer passes — register several fuzzer configurations that run in sequence, e.g. one dense pass and one sparse pass, to improve coverage.
  • Comparison options — plug in go-cmp options to handle known legitimate asymmetries in conversions

Changes

  • New package pkg/apitesting/roundtrip with three files:
    • roundtrip.go — core test runner, options, and parallelism-safe sub-test structure
    • fuzzer.go — Fuzzer helpers
    • cmp.go — k8s object comparison helpers
  • Documentation added under docs/api-roundtrip-testing.md.

I have:

  • Read and followed Upjet's contribution process.
  • Run make reviewable to ensure this PR is ready for review.
  • Added backport release-x.y labels to auto-backport this PR if necessary.

How has this code been tested

@erhancagirici erhancagirici force-pushed the roundtrip-test-library branch from 7030a72 to 97b875e Compare April 28, 2026 20:51
@erhancagirici erhancagirici marked this pull request as ready for review April 29, 2026 13:29
@erhancagirici erhancagirici force-pushed the roundtrip-test-library branch from 97b875e to f9e11b6 Compare May 5, 2026 13:27
@erhancagirici erhancagirici changed the title [draft] feat: API roundtrip testing library feat: API roundtrip testing library May 5, 2026
Copy link
Copy Markdown
Member

@sergenyalcin sergenyalcin left a comment

Choose a reason for hiding this comment

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

Thank you very much, @erhancagirici, for this library. This is very helpful. It will increase the confidence level in the generated APIs. I've left a few comments to discuss and consider.

Comment thread pkg/apitesting/roundtrip/roundtrip.go Outdated
Comment thread docs/api-roundtrip-testing.md
Comment thread pkg/apitesting/roundtrip/roundtrip.go
Comment thread pkg/apitesting/roundtrip/roundtrip.go Outdated
Comment thread pkg/apitesting/roundtrip/roundtrip.go Outdated
Comment thread pkg/apitesting/roundtrip/roundtrip.go Outdated
Comment thread pkg/apitesting/roundtrip/roundtrip.go
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Review Change Stack

Warning

Rate limit exceeded

@erhancagirici has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 4 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ae60b9f2-9702-45dc-9c34-3a21e7fe8aa3

📥 Commits

Reviewing files that changed from the base of the PR and between fecb1d3 and 6b06ca1.

⛔ Files ignored due to path filters (1)
  • go.mod is excluded by none and included by none
📒 Files selected for processing (7)
  • docs/api-roundtrip-testing.md
  • pkg/apitesting/roundtrip/cmp.go
  • pkg/apitesting/roundtrip/cmp_test.go
  • pkg/apitesting/roundtrip/fuzzer.go
  • pkg/apitesting/roundtrip/fuzzer_test.go
  • pkg/apitesting/roundtrip/roundtrip.go
  • pkg/apitesting/roundtrip/roundtrip_test.go
📝 Walkthrough

Walkthrough

Adds a RoundTripTest framework, randfill-based fuzzers, metadata-normalizing cmp helpers (normalizeMeta, EquateEmptyAndSingleZeroSlice, EquateNilAndZeroValuePtr), comprehensive tests, and documentation for serialization and conversion round-trips.

Changes

Round-Trip API Testing Framework

Layer / File(s) Summary
Comparison utilities
pkg/apitesting/roundtrip/cmp.go, pkg/apitesting/roundtrip/cmp_test.go
Adds metadata normalization (normalizeMeta), recursive "effectively zero" predicate, EquateEmptyAndSingleZeroSlice() and EquateNilAndZeroValuePtr() cmp.Options, plus tests for normalization and comparison semantics.
Fuzzer implementations
pkg/apitesting/roundtrip/fuzzer.go, pkg/apitesting/roundtrip/fuzzer_test.go
Adds ASCIIStringFuzzer, objectMetaNamespaceFuzzer(namespaced bool), dnsLetters charset, and tests validating generated string length/charset and namespace behavior for cluster-scoped vs namespaced modes.
Public Types & Constructors
pkg/apitesting/roundtrip/roundtrip.go
Adds RoundTripTest, NewRoundTripTest, TestOption and FuzzerOption types and many exported option helpers for codec overrides, include/exclude filters, comparison options, extra fuzzers, and fuzzer configs.
Defaults & Setup
pkg/apitesting/roundtrip/roundtrip.go
Adds default fuzzing constants, default ignored kinds, and setupTestInfrastructure to build codec factory and register conversions.
Fuzzer Configuration & Runtime
pkg/apitesting/roundtrip/roundtrip.go
Implements fuzzer option handling, getFuzzer, getFillers, and composition of per-(kind,version-pair) filler+iteration sets with namespaced metadata behavior.
Core Runners & Orchestration
pkg/apitesting/roundtrip/roundtrip.go
Implements TestSerializationRoundtrip, TestConversionRoundtrip, testKind, spokeHubSpoke, hubSpokeHub, groupsToKindFromScheme, and nonRoundTrippableTypes.
Tests: unit and integration
pkg/apitesting/roundtrip/*_test.go
Adds comprehensive tests: cmp tests (cmp_test.go), fuzzer tests (fuzzer_test.go), and a broad suite exercising options, filtering, filler generation, and conversion round-trips (roundtrip_test.go).
Documentation
docs/api-roundtrip-testing.md
New doc describing purpose, serialization vs conversion invariants, minimal usage example, all TestOption / FuzzerOption constructors, comparison options, and custom fuzzer examples.

Sequence Diagrams

sequenceDiagram
    actor TestSuite as Test Suite
    participant RRT as RoundTripTest
    participant Scheme as Scheme
    participant Fuzzer as Fuzzer
    participant Codec as CodecFactory
    participant Serializer as Serializer
    participant Comparer as Comparator

    TestSuite->>RRT: NewRoundTripTest(provider, scheme, opts...)
    RRT->>RRT: setupTestInfrastructure()
    TestSuite->>RRT: TestSerializationRoundtrip()
    RRT->>Fuzzer: getFuzzer(config)
    Fuzzer-->>RRT: randfill.Filler
    RRT->>Codec: Request encoder/decoder
    Codec-->>RRT: Encoder/Decoder
    RRT->>Serializer: Encode object -> bytes
    Serializer-->>RRT: bytes
    RRT->>Serializer: Decode bytes -> object
    Serializer-->>RRT: object
    RRT->>Comparer: cmpK8sObjects(original, deserialized)
    Comparer-->>RRT: diff/result
    RRT-->>TestSuite: Pass/Fail
Loading
sequenceDiagram
    actor TestSuite as Test Suite
    participant RRT as RoundTripTest
    participant Scheme as Scheme
    participant Spoke as SpokeVersion
    participant Hub as HubVersion
    participant Fuzzer as Fuzzer
    participant Comparer as Comparator

    TestSuite->>RRT: TestConversionRoundtrip()
    RRT->>Scheme: Discover group/kind versions
    Scheme-->>RRT: hub & spoke mappings
    RRT->>Fuzzer: Create fuzzed spoke object
    Fuzzer-->>Spoke: fuzzed spoke
    Spoke->>Hub: ConvertTo(hub)
    Hub-->>Spoke: hub object
    Hub->>Spoke: ConvertFrom(hub)
    Spoke-->>RRT: final spoke
    RRT->>Comparer: cmpK8sObjects(originalSpoke, finalSpoke)
    Comparer-->>RRT: diff/result
    RRT-->>TestSuite: Pass/Fail
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Thanks — would you like me to highlight specific areas for reviewers to focus on (determinism, fuzzer choices, comparison tolerances, or filtering logic)?

🚥 Pre-merge checks | ✅ 7
✅ Passed checks (7 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: API roundtrip testing library' is descriptive, follows the conventional commit format, stays well under 72 characters (36 chars), and clearly summarizes the main change—adding a reusable roundtrip testing utility library.
Description check ✅ Passed The description comprehensively explains the purpose, functionality, and implementation details of the roundtrip testing library, covering serialization and conversion invariants, customization options, and wiring examples—all directly relevant to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Configuration Api Breaking Changes ✅ Passed No modifications to existing pkg/config/** files. All pkg/config files are newly added (251 total added files, 0 modified). No breaking changes to Configuration API.
Generated Code Manual Edits ✅ Passed No files matching 'zz_*.go' pattern were modified. All changes are to handwritten source files and documentation for the new roundtrip testing library.
Template Breaking Changes ✅ Passed PR does not modify any pkg/controller/external*.go template files. Changes are limited to new pkg/apitesting/roundtrip/ library and documentation—no controller generation behavior affected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/api-roundtrip-testing.md`:
- Around line 132-136: The documented implicit fuzzer defaults are out of sync
with the code: update the prose for WithFuzzerConfig to match the implementation
(NilChance≈0.2, NumElements 0–1, iterations 5) instead of the current
"NumElements 0–3, 10 iterations", and make the same correction in the later
occurrence that mirrors lines 160–162 so both places reflect the actual defaults
used by the fuzzer.
- Around line 227-231: The example declares MyWorkload incorrectly; change the
type declaration to a proper Go struct by replacing "type MyWorkload {" with
"type MyWorkload struct {" and keep the fields Name string, State State, and
PendingJobCount int as-is (referencing MyWorkload, State, PendingJobCount) so
the example compiles when copy-pasted.

In `@pkg/apitesting/roundtrip/roundtrip.go`:
- Around line 486-505: The function nonRoundTrippableTypes currently returns nil
when no include/exclude filters are set which causes callers (e.g.,
TestSerializationRoundtrip) to treat that as "no skips" and exercise kinds this
package intends to ignore; change nonRoundTrippableTypes on RoundTripTest to
always build and return the skip map (i.e., compute allowed :=
rt.groupsToKindFromScheme() and populate result for every gvk from
rt.scheme.AllKnownTypes() except runtime.APIVersionInternal where the gvk.Group
is not in allowed or the GroupKind is not allowed) instead of returning nil when
hasFilter is false so the default exclusions remain active. Ensure callers that
expect a nil vs empty map still work by returning the constructed map (possibly
empty) rather than nil.
- Around line 122-124: Replace the public use of any by introducing a concrete
FuzzFunc type (e.g., type FuzzFunc interface or func wrapper representing the
randfill-compatible signature) and change the extraFuzzFns field from []any to
[]FuzzFunc; update the public constructor WithExtraFuzzFuncs(fns ...any) to
WithExtraFuzzFuncs(fns ...FuzzFunc) and adapt callers to pass the new FuzzFunc
wrappers, and update the customFuzzFuncs function signature/return type to
return []FuzzFunc so the dynamic registration is encapsulated internally while
keeping the public API typed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9d5eb625-7249-480e-adc3-fa9845de02c3

📥 Commits

Reviewing files that changed from the base of the PR and between c6d5213 and 1d9cea0.

⛔ Files ignored due to path filters (1)
  • go.mod is excluded by none and included by none
📒 Files selected for processing (4)
  • docs/api-roundtrip-testing.md
  • pkg/apitesting/roundtrip/cmp.go
  • pkg/apitesting/roundtrip/fuzzer.go
  • pkg/apitesting/roundtrip/roundtrip.go

Comment thread docs/api-roundtrip-testing.md Outdated
Comment thread docs/api-roundtrip-testing.md Outdated
Comment thread pkg/apitesting/roundtrip/roundtrip.go Outdated
Comment thread pkg/apitesting/roundtrip/roundtrip.go
@erhancagirici erhancagirici force-pushed the roundtrip-test-library branch 2 times, most recently from 3f5c950 to 6ba19c2 Compare May 6, 2026 17:17
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
pkg/apitesting/roundtrip/roundtrip_test.go (2)

201-201: ⚡ Quick win

Subtest names should not contain underscores — inconsistent with the rest of the suite.

Several t.Run(...) names use underscores as separators (_append, _true, _false, _appends, _sets_custom), while sibling subtests in the same test functions use pure PascalCase. Please use PascalCase without underscores throughout for consistency.

♻️ Proposed rename
-	t.Run("FuzzerSkipPatterns_append", func(t *testing.T) {
+	t.Run("FuzzerSkipPatternsAppend", func(t *testing.T) {

-	t.Run("FuzzerAllowUnexportedFields_true", func(t *testing.T) {
+	t.Run("FuzzerAllowUnexportedFieldsTrue", func(t *testing.T) {

-	t.Run("FuzzerAllowUnexportedFields_false", func(t *testing.T) {
+	t.Run("FuzzerAllowUnexportedFieldsFalse", func(t *testing.T) {

-	t.Run("WithFuzzerConfig_appends", func(t *testing.T) {
+	t.Run("WithFuzzerConfigAppends", func(t *testing.T) {

-	t.Run("WithComparisonOptions_appends", func(t *testing.T) {
+	t.Run("WithComparisonOptionsAppends", func(t *testing.T) {

-	t.Run("WithExtraFuzzFuncs_appends", func(t *testing.T) {
+	t.Run("WithExtraFuzzFuncsAppends", func(t *testing.T) {

-	t.Run("WithCodecFactory_sets_custom", func(t *testing.T) {
+	t.Run("WithCodecFactorySetsCustom", func(t *testing.T) {

As per coding guidelines: "PascalCase test names (no underscores)".

Also applies to: 212-212, 220-220, 232-232, 286-286, 295-295, 304-304

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/apitesting/roundtrip/roundtrip_test.go` at line 201, The subtest name
"FuzzerSkipPatterns_append" uses underscores and should be PascalCase like the
rest of the suite; rename the t.Run call in this file (and the sibling t.Run
calls at the other listed locations) to remove underscores and use PascalCase
(e.g., FuzzerSkipPatternsAppend) so all subtest names are consistent; update the
string passed to t.Run in the affected anonymous subtests (the ones invoking
t.Run with names ending in _append, _true, _false, _appends, _sets_custom) to
the new PascalCase names while leaving the test body and function identifiers
unchanged.

535-549: ⚡ Quick win

Consider strengthening the namespaced-filler assertion to check all iterations, not just the first.

The sawNonEmpty/break pattern verifies "at least once non-empty" in 50 runs, but objectMetaNamespaceFuzzer(true) will always produce a non-empty namespace (the "ns" prefix guarantees this). A tighter loop that asserts every run produces a non-empty namespace would catch any regression where the filler silently stops setting Namespace while still occasionally producing one.

♻️ Proposed fix
-	t.Run("namespaced filler produces non-empty Namespace in most runs", func(t *testing.T) {
+	t.Run("namespaced filler always produces non-empty Namespace", func(t *testing.T) {
 		fillers := rt.getFillers(true)
-		sawNonEmpty := false
 		for range 50 {
 			var meta metav1.ObjectMeta
 			fillers[0].filler.Fill(&meta)
-			if meta.Namespace != "" {
-				sawNonEmpty = true
-				break
+			if meta.Namespace == "" {
+				t.Error("namespaced filler produced an empty Namespace")
 			}
 		}
-		if !sawNonEmpty {
-			t.Error("namespaced filler never produced non-empty Namespace in 50 runs")
-		}
 	})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/apitesting/roundtrip/roundtrip_test.go` around lines 535 - 549, The test
currently only requires at least one non-empty Namespace across 50 iterations;
change it to assert that every iteration produces a non-empty Namespace by
calling rt.getFillers(true), using the first filler’s Fill method
(fillers[0].filler.Fill(&meta)) in a loop of 50 runs, and failing the test
immediately if any meta.Namespace == "" so the namespaced filler
(objectMetaNamespaceFuzzer via getFillers) is verified to always set Namespace.
docs/api-roundtrip-testing.md (3)

185-187: 💤 Low value

Add a blank line before the --- separator following the comparison-options table.

Without a blank line, some Markdown parsers (e.g., GitHub's) can interpret --- immediately after a line of text as an underline-style H2 heading. In this position the last table row could theoretically be misread. Adding one blank line makes the horizontal rule unambiguous.

♻️ Proposed fix
 | `EquateEmptyAndSingleZeroSlice` | Considers empty slices and slices of length 1 with zero-value elements equal |
+
 ---
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/api-roundtrip-testing.md` around lines 185 - 187, The Markdown lacks a
blank line before the horizontal rule after the comparison-options table row for
`EquateEmptyAndSingleZeroSlice`, which can cause some parsers to treat the `---`
as an H2 underline; add a single blank line between the last table row (the
`EquateEmptyAndSingleZeroSlice` entry) and the `---` separator so the horizontal
rule is unambiguous.

192-200: 💤 Low value

The WithExtraFuzzFuncs section duplicates its own description — consider trimming the table.

The prose at line 194–195 already explains what WithExtraFuzzFuncs does; the table at lines 198–200 repeats the same text verbatim. Removing the table (or collapsing the intro prose into the table row) would reduce redundancy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/api-roundtrip-testing.md` around lines 192 - 200, The docs duplicate the
description for WithExtraFuzzFuncs; remove the redundant table row or collapse
the intro into it so the behavior is described once. Locate the header and
paragraph mentioning WithExtraFuzzFuncs and either delete the table row for
`WithExtraFuzzFuncs(fns...)` or replace the prose with a single concise table
entry; ensure the unique identifier `WithExtraFuzzFuncs(fns...)` remains
documented exactly once and that its signature and effect (`func(*T,
randfill.Continue)` applied globally to every fuzzer configuration) are
preserved.

9-9: 💤 Low value

Minor grammar nit: hyphenate "provider-managed".

"provider managed resources" should be "provider-managed resources" — the compound modifier before the noun needs a hyphen.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/api-roundtrip-testing.md` at line 9, Replace the unhyphenated phrase
"provider managed resources" with the compound modifier "provider-managed
resources" in the docs text (look for the exact string "provider managed
resources" in docs/api-roundtrip-testing.md) so the compound adjective before
"resources" is correctly hyphenated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/api-roundtrip-testing.md`:
- Around line 234-236: The example in myCustomWorkloadFuzzer calls
c.FillNoCustom() without the required target argument; update the call to pass
the workload pointer (f) so use c.FillNoCustom(f) within the
myCustomWorkloadFuzzer function (randfill.Continue c, *MyWorkload f) to match
the real API and avoid the compile error.

---

Nitpick comments:
In `@docs/api-roundtrip-testing.md`:
- Around line 185-187: The Markdown lacks a blank line before the horizontal
rule after the comparison-options table row for `EquateEmptyAndSingleZeroSlice`,
which can cause some parsers to treat the `---` as an H2 underline; add a single
blank line between the last table row (the `EquateEmptyAndSingleZeroSlice`
entry) and the `---` separator so the horizontal rule is unambiguous.
- Around line 192-200: The docs duplicate the description for
WithExtraFuzzFuncs; remove the redundant table row or collapse the intro into it
so the behavior is described once. Locate the header and paragraph mentioning
WithExtraFuzzFuncs and either delete the table row for
`WithExtraFuzzFuncs(fns...)` or replace the prose with a single concise table
entry; ensure the unique identifier `WithExtraFuzzFuncs(fns...)` remains
documented exactly once and that its signature and effect (`func(*T,
randfill.Continue)` applied globally to every fuzzer configuration) are
preserved.
- Line 9: Replace the unhyphenated phrase "provider managed resources" with the
compound modifier "provider-managed resources" in the docs text (look for the
exact string "provider managed resources" in docs/api-roundtrip-testing.md) so
the compound adjective before "resources" is correctly hyphenated.

In `@pkg/apitesting/roundtrip/roundtrip_test.go`:
- Line 201: The subtest name "FuzzerSkipPatterns_append" uses underscores and
should be PascalCase like the rest of the suite; rename the t.Run call in this
file (and the sibling t.Run calls at the other listed locations) to remove
underscores and use PascalCase (e.g., FuzzerSkipPatternsAppend) so all subtest
names are consistent; update the string passed to t.Run in the affected
anonymous subtests (the ones invoking t.Run with names ending in _append, _true,
_false, _appends, _sets_custom) to the new PascalCase names while leaving the
test body and function identifiers unchanged.
- Around line 535-549: The test currently only requires at least one non-empty
Namespace across 50 iterations; change it to assert that every iteration
produces a non-empty Namespace by calling rt.getFillers(true), using the first
filler’s Fill method (fillers[0].filler.Fill(&meta)) in a loop of 50 runs, and
failing the test immediately if any meta.Namespace == "" so the namespaced
filler (objectMetaNamespaceFuzzer via getFillers) is verified to always set
Namespace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8857a51f-a53d-486f-87e5-d3357a06a65d

📥 Commits

Reviewing files that changed from the base of the PR and between 1d9cea0 and 2929a1f.

⛔ Files ignored due to path filters (1)
  • go.mod is excluded by none and included by none
📒 Files selected for processing (7)
  • docs/api-roundtrip-testing.md
  • pkg/apitesting/roundtrip/cmp.go
  • pkg/apitesting/roundtrip/cmp_test.go
  • pkg/apitesting/roundtrip/fuzzer.go
  • pkg/apitesting/roundtrip/fuzzer_test.go
  • pkg/apitesting/roundtrip/roundtrip.go
  • pkg/apitesting/roundtrip/roundtrip_test.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/apitesting/roundtrip/cmp_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/apitesting/roundtrip/cmp.go
  • pkg/apitesting/roundtrip/roundtrip.go

Comment thread docs/api-roundtrip-testing.md
Copy link
Copy Markdown
Member

@sergenyalcin sergenyalcin left a comment

Choose a reason for hiding this comment

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

Thanks @erhancagirici LGTM!

Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
- do not override user-supplied codecFactory
- fuzzer maxElement default to 1
- use error package for constructing error
- run SerializationRoundtrip tests for each fuzzer config

Signed-off-by: Erhan Cagirici <erhan@upbound.io>
- remove leftover obsolete info
- add warning about singleton lists at fuzzer options
- add references for custom comparison opts
- add example for custom fuzz function

Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
Signed-off-by: Erhan Cagirici <erhan@upbound.io>
@erhancagirici erhancagirici force-pushed the roundtrip-test-library branch from fecb1d3 to 6b06ca1 Compare May 7, 2026 11:21
@erhancagirici erhancagirici merged commit 480d366 into crossplane:main May 7, 2026
8 checks passed
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.

2 participants