Conversation
80ad3fe to
32254bd
Compare
Company asked me to do use AI for code generation, this the result (after heavily editing it).
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2166 +/- ##
==========================================
+ Coverage 62.27% 63.33% +1.06%
==========================================
Files 230 230
Lines 13753 13753
==========================================
+ Hits 8564 8711 +147
+ Misses 4534 4386 -148
- Partials 655 656 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
WalkthroughThis pull request adds comprehensive test coverage across multiple packages and propagates Changes
Sequence Diagram(s)sequenceDiagram
participant Test as Test File
participant Helper as testSubCommand Helper
participant Exec as exec.CommandContext
participant Plugin as Plugin Process
Test->>Helper: testSubCommand(ctx, "subcommand")
Helper->>Exec: CommandContext(ctx, pluginPath, args...)
Exec->>Plugin: Launch with context
Plugin-->>Exec: Execute command
Exec-->>Helper: Return result
Helper-->>Test: Return test case function
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (7)
internal/constants/const_test.go (3)
13-64: Consider the value of testing constant strings.While these tests verify that constants have expected values, they provide limited protection since any intentional change to a constant requires updating both the constant and the test. These tests are most useful for catching typos during initial development.
86-95: Remove redundant test.This test duplicates coverage from
TestStaticCacheOwner(line 79), which already verifies the UID. Testing the same value throughObjectMeta.UIDdoesn't add meaningful coverage.Apply this diff to remove the redundant test:
-func TestStaticCacheOwner_ObjectMeta(t *testing.T) { - t.Parallel() - - owner := StaticCacheOwner() - - // Verify ObjectMeta structure - assert.Equal(t, metav1.ObjectMeta{ - UID: "123-456", - }, owner.ObjectMeta) -} -
97-108: Remove redundant test.This test duplicates coverage from
TestStaticCacheOwner(line 78), which already verifies the return type usingassert.IsType. The manual type assertion here doesn't add meaningful coverage.Apply this diff to remove the redundant test:
-func TestStaticCacheOwner_ReturnType(t *testing.T) { - t.Parallel() - - owner := StaticCacheOwner() - - // Verify it's a pointer to ObjectDeployment - assert.NotNil(t, owner) - - // Verify the underlying type - _, ok := interface{}(owner).(*corev1alpha1.ObjectDeployment) - assert.True(t, ok, "StaticCacheOwner should return *corev1alpha1.ObjectDeployment") -}cmd/build/definitions_test.go (1)
51-106: Remove unusedshouldPanicfield.The test table includes a
shouldPanicfield and panic handling logic (lines 95-100), but no test case setsshouldPanicto true. This appears to be unused scaffolding.Apply this diff to simplify the test:
func TestImageRegistryHost(t *testing.T) { tests := []struct { name string registry string expectedHost string - shouldPanic bool }{ { name: "quay.io registry", registry: "quay.io/foobar", expectedHost: "quay.io", }, // ... other test cases ... } for _, tt := range tests { test := tt t.Run(test.name, func(t *testing.T) { // Save original value for this subtest originalValue, exists := os.LookupEnv(imageRegistryEnvvarName) defer func() { if exists { require.NoError(t, os.Setenv(imageRegistryEnvvarName, originalValue)) } else { require.NoError(t, os.Unsetenv(imageRegistryEnvvarName)) } }() require.NoError(t, os.Setenv(imageRegistryEnvvarName, test.registry)) - if test.shouldPanic { - assert.Panics(t, func() { - imageRegistryHost() - }) - return - } - result := imageRegistryHost() assert.Equal(t, test.expectedHost, result) }) } }internal/cmd/options_test.go (1)
321-325: Consider making mockClock deterministic.The
mockClock.Now()method returns the current wall-clock time viametav1.Now(), which makes tests non-deterministic. While current tests only verify that the clock is set (not its value), this is a testing anti-pattern that could cause flaky tests if future tests need to verify time-dependent behavior.Consider returning a fixed time:
type mockClock struct{} func (m *mockClock) Now() metav1.Time { - return metav1.Now() + return metav1.Time{Time: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)} }internal/controllers/config_test.go (2)
19-33: Avoid asserting pointer identity inTestBackoffConfig_Option.
assert.Equal(t, &initialBackoff, cfg.InitialBackoff)and the MaxBackoff equivalent require thatOptionstores the same variable address, not just the same value. That’s an implementation detail; many reasonable implementations would allocate their owntime.Durationand still be correct.Consider asserting on the values instead to keep the test focused on behavior rather than storage details:
- assert.Equal(t, &initialBackoff, cfg.InitialBackoff) - assert.Equal(t, &maxBackoff, cfg.MaxBackoff) + if assert.NotNil(t, cfg.InitialBackoff) { + assert.Equal(t, initialBackoff, *cfg.InitialBackoff) + } + if assert.NotNil(t, cfg.MaxBackoff) { + assert.Equal(t, maxBackoff, *cfg.MaxBackoff) + }
96-112:GetBackoffdefault test is reasonable, minor redundancy only.The test sensibly checks for non-nil and correct type for the constructed backoff. The second
assert.NotNilafter the comments is redundant but harmless; you can drop it if you want to reduce noise.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (21)
cmd/build/definitions_test.go(1 hunks)integration/kubectl-package/build_test.go(2 hunks)integration/kubectl-package/helpers_test.go(2 hunks)integration/kubectl-package/kubectl-package_suite_test.go(2 hunks)integration/kubectl-package/tree_test.go(1 hunks)integration/kubectl-package/update_test.go(1 hunks)integration/kubectl-package/validate_test.go(1 hunks)integration/kubectl-package/version_test.go(1 hunks)internal/apis/manifests/packagemanifest_types_test.go(1 hunks)internal/apis/manifests/packagemanifestlock_types_test.go(1 hunks)internal/apis/manifests/repository_types_test.go(1 hunks)internal/apis/manifests/repositoryentry_types_test.go(1 hunks)internal/cli/options_test.go(1 hunks)internal/cmd/build_test.go(1 hunks)internal/cmd/cmd_test.go(1 hunks)internal/cmd/options_test.go(1 hunks)internal/cmd/resolver_test.go(1 hunks)internal/constants/const_test.go(1 hunks)internal/controllers/config_test.go(1 hunks)internal/controllers/options_test.go(1 hunks)internal/packages/internal/packagevalidation/templatevalidation.go(3 hunks)
🧰 Additional context used
🪛 golangci-lint (2.5.0)
internal/controllers/config_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/cmd/build_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/cmd/resolver_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/apis/manifests/repositoryentry_types_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/controllers/options_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/apis/manifests/packagemanifest_types_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
cmd/build/definitions_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/cmd/cmd_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/cmd/options_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/apis/manifests/repository_types_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/apis/manifests/packagemanifestlock_types_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/cli/options_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
internal/constants/const_test.go
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
[warning] 1-1: var-naming: avoid meaningless package names
(revive)
🔇 Additional comments (41)
internal/packages/internal/packagevalidation/templatevalidation.go (3)
163-163: LGTM: Proper context propagation.The context is correctly passed from
runTestCasetorunDiff, enabling cancellation and timeout control for the diff operation.
244-265: LGTM: Proper use of CommandContext.The function correctly:
- Adds
context.Contextas the first parameter following Go conventions- Uses
exec.CommandContextto enable cancellation and timeout control- Maintains existing error handling logic
This change allows the
diffcommand to be cancelled if the parent context times out or is cancelled, preventing resource leaks.
1-265: Note: PR title vs. file content.The PR title mentions "Add AI generated unit tests," but this file contains validation logic improvements (context propagation) rather than test additions. If test files exist in other parts of this PR, they weren't included in this review batch.
The context propagation changes in this file are well-implemented and follow Go best practices.
internal/cli/options_test.go (6)
1-8: LGTM! Clean imports and package structure.The package declaration and imports are well-organized. The static analysis warning about "meaningless package names" is a false positive—
cliis a standard, descriptive name for command-line interface packages.
10-20: LGTM! Well-structured test.The test correctly verifies that
WithOut.ConfigurePrintersets the output writer on the config. Good use oft.Parallel()for concurrent test execution.
22-32: LGTM! Consistent test pattern.The test correctly verifies error writer configuration. The structure mirrors
TestWithOut_ConfigurePrinter, maintaining consistency across the test suite.
34-49: LGTM! Good integration test.This test properly verifies that
WithOutandWithErrcan be applied together without conflict, ensuring both output and error writers are configured independently.
51-60: LGTM! Important nil-handling coverage.Good defensive test that verifies nil writers are handled correctly. This edge case coverage helps prevent potential nil pointer dereferences downstream.
62-71: LGTM! Completes nil-handling coverage.Consistent with
TestWithOut_NilWriter, this test ensures nil error writers are properly handled, completing the edge case coverage for both output streams.integration/kubectl-package/version_test.go (1)
6-12: LGTM! Context properly threaded through test helper.The addition of the context import and passing
context.Background()totestSubCommandcorrectly aligns with the updated helper signature, enabling context-aware command execution.integration/kubectl-package/validate_test.go (1)
6-13: LGTM! Consistent context integration.The changes properly add context support to the validate subcommand tests, maintaining consistency with the broader test suite refactoring.
integration/kubectl-package/update_test.go (1)
6-13: LGTM! Context correctly propagated.The update subcommand tests now support context-aware execution, consistent with the test suite's refactoring pattern.
integration/kubectl-package/tree_test.go (1)
6-13: LGTM! Context integration complete.The tree subcommand tests now properly thread context through the test helper, enabling controlled command execution.
integration/kubectl-package/build_test.go (1)
6-15: LGTM! Build tests updated correctly.The build subcommand tests now use context-aware execution, maintaining consistency with the overall test suite refactoring.
integration/kubectl-package/kubectl-package_suite_test.go (1)
50-50: LGTM! Context properly integrated into suite setup.The
projectRootfunction signature is correctly updated to accept a context parameter, and the implementation properly usesCommandContextfor the git command. The call site inBeforeSuitecorrectly passescontext.Background().Also applies to: 110-113
integration/kubectl-package/helpers_test.go (1)
6-6: LGTM! Core helper correctly refactored for context support.The
testSubCommandhelper function signature is properly updated to acceptcontext.Context, and the implementation correctly usesexec.CommandContextfor command execution. This enables proper cancellation and timeout control for the kubectl-package plugin commands.Note: The context is currently always
context.Background()from callers, and the timeout is still controlled viatc.Timeoutfield (line 34). This is a reasonable approach—thetc.Timeoutcontrols the Ginkgo session timeout, while the context enables future command-level cancellation if needed.Also applies to: 28-31
internal/constants/const_test.go (1)
72-84: LGTM!This test properly verifies the behavior of
StaticCacheOwner(), including return type, UID value, and consistency across multiple invocations.cmd/build/definitions_test.go (4)
12-48: LGTM!The test correctly verifies both default and environment-driven behavior, with proper cleanup of environment variables using defer.
109-161: LGTM!The test provides comprehensive coverage of namespace extraction, including edge cases like registries without namespaces and multi-level namespace paths.
164-210: LGTM!The test correctly verifies that
localRegistry()combines the registry namespace with the provided host port across different registry formats.
223-257: LGTM!The edge case tests provide valuable additional coverage for registry host extraction with ports and simple hostnames.
internal/cmd/resolver_test.go (2)
9-30: LGTM!The option handling tests correctly verify that options are applied and that multiple options override each other appropriately (last one wins).
71-108: LGTM!Good use of table-driven tests to verify
ConfigureResolveDigestbehavior with both true and false values, and proper verification of secure defaults.internal/cmd/build_test.go (4)
11-43: LGTM!The tests correctly verify error message handling and Build construction with various option combinations.
45-86: LGTM!Good test coverage for option application and default initialization, including verification that provided values are preserved and nil fields are populated with defaults.
88-104: LGTM!Good verification that multiple options can be applied together and all fields are set correctly.
106-116: LGTM!The test correctly verifies error handling for invalid paths. The error message check ensures the right code path is executed.
internal/cmd/options_test.go (1)
14-317: Verify necessity of nolint directive.The test coverage for option configurators looks good overall. However, at line 315, the
//nolint:gocriticcomment appears unnecessary—the append patternappend(existingTags, newTags...)is idiomatic Go and shouldn't trigger legitimate linter warnings.Consider removing the directive unless there's a specific linter configuration requiring it:
- expectedTags := append(existingTags, newTags...) //nolint:gocritic + expectedTags := append(existingTags, newTags...)Otherwise, the comprehensive coverage of Configure* methods effectively verifies the option wiring pattern used throughout the package.
internal/cmd/cmd_test.go (3)
15-45: LGTM!Good verification of scheme creation with GVK recognition check, error constant value, and factory construction.
47-83: LGTM!Well-structured table-driven test covering both successful client creation and error handling scenarios.
135-141: LGTM!Simple but correct constructor validation.
internal/controllers/options_test.go (3)
88-112: Type-level tests for options look fine.The compile-time interface checks and duration alias assertions are clear and low-risk; they document the intended option types nicely.
114-158: Integration tests cover key backoff option behaviors.The integration tests with
BackoffConfig.Default()exercise defaulting, single-option and both-option flows, and implicitly verify non-nil pointers via dereference. This is solid coverage.
160-179: Override behavior test is clear and effective.
TestOptions_OverrideValuesclearly expresses that later options should win and uses pointer dereferences to ensure the config is actually populated.internal/controllers/config_test.go (3)
11-17: Constants test is straightforward and helpful.The constants check directly guards against accidental changes to default timings and is simple enough to maintain.
113-128: Custom-valuesGetBackofftest looks good.This verifies that user-supplied backoff settings still yield a valid
flowcontrol.Backoffinstance without relying on unexported internals.
130-142: Chained operations scenario is well covered.The chained
Option→Default→GetBackoffpath is nicely exercised, and the assertions capture both config state and successful backoff construction.internal/apis/manifests/repository_types_test.go (1)
10-61: Comprehensive Repository accessor coverageValidating the promoted TypeMeta/ObjectMeta getters/setters (including the empty-state check) gives solid signal if future schema tweaks break these helpers. Nicely focused fixture.
internal/apis/manifests/packagemanifestlock_types_test.go (1)
10-209: Thorough lock Spec scenario coverageExercising full/empty/partial Specs for images and dependencies hits every accessor path we usually risk during schema edits—great defensive net around the CRD surface.
internal/apis/manifests/repositoryentry_types_test.go (1)
10-173: RepositoryEntry constraint matrix looks solidMixing full, empty, minimal, and complex constraint scenarios mirrors real manifests and will catch regressions in constraint wiring early.
internal/apis/manifests/packagemanifest_types_test.go (1)
14-293: Deep PackageManifest fixtureCovering scopes/phases, probes, filters, repos/deps, and the templating/test context in one place gives high confidence against refactors in this large type.
| func TestDefaultKubeClientFactory_GetKubeClient(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| configFactory RestConfigFactory | ||
| expectError bool | ||
| }{ | ||
| { | ||
| name: "config factory error", | ||
| configFactory: &mockRestConfigFactory{err: errors.New("config error")}, | ||
| expectError: true, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(test.name, func(t *testing.T) { | ||
| t.Parallel() | ||
| scheme := runtime.NewScheme() | ||
| factory := NewDefaultKubeClientFactory(scheme, test.configFactory) | ||
|
|
||
| client, err := factory.GetKubeClient() | ||
|
|
||
| if test.expectError { | ||
| require.Error(t, err) | ||
| require.Nil(t, client) | ||
| } else if err != nil { | ||
| // Note: In real scenarios without mocking the underlying client.New, | ||
| // this would likely still error due to invalid config, but we're | ||
| // testing the factory behavior | ||
|
|
||
| require.Error(t, err) | ||
| } | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Clarify test logic for non-error case.
Lines 124-130 contain confusing test logic:
} else if err != nil {
// Note: In real scenarios...
require.Error(t, err)
}When expectError is false, the test should verify success with require.NoError(err) and require.NotNil(client). The current else-if branch still requires an error when expectError is false, which doesn't align with the test's intent.
Consider fixing the logic:
if test.expectError {
require.Error(t, err)
require.Nil(t, client)
} else {
- // Note: In real scenarios without mocking the underlying client.New,
- // this would likely still error due to invalid config, but we're
- // testing the factory behavior
-
- require.Error(t, err)
+ require.NoError(t, err)
+ require.NotNil(t, client)
}Or add a test case with expectError: false and a mock config that returns valid config.
🤖 Prompt for AI Agents
In internal/cmd/cmd_test.go around lines 98 to 133 the test logic is inverted
for the non-error case: the else-if branch still asserts an error when
expectError is false. Fix by replacing the else-if block with an else that
asserts success (require.NoError(t, err) and require.NotNil(t, client)), or
alternatively add a separate table test case with expectError: false and a mock
RestConfigFactory that returns a valid config and assert no error and non-nil
client.
| func TestDefaultWaiter_WaitForCondition(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| mockClient := &Client{client: &mockClient{}} | ||
| scheme := runtime.NewScheme() | ||
| waiter := NewDefaultWaiter(mockClient, scheme) | ||
|
|
||
| require.NotNil(t, waiter) | ||
| require.NotNil(t, waiter.waiter) | ||
|
|
||
| // Test that the waiter has the expected timeout and interval | ||
| // This is more of a constructor test since the actual waiting | ||
| // would require complex mocking of the wait.Waiter | ||
| } |
There was a problem hiding this comment.
Rename or remove misleading test.
TestDefaultWaiter_WaitForCondition is misleadingly named—it doesn't test any waiting behavior, as the comment acknowledges. This is essentially a duplicate of TestNewDefaultWaiter (lines 158-168).
Consider one of the following:
-
Remove as duplicate:
-func TestDefaultWaiter_WaitForCondition(t *testing.T) { - t.Parallel() - - mockClient := &Client{client: &mockClient{}} - scheme := runtime.NewScheme() - waiter := NewDefaultWaiter(mockClient, scheme) - - require.NotNil(t, waiter) - require.NotNil(t, waiter.waiter) - - // Test that the waiter has the expected timeout and interval - // This is more of a constructor test since the actual waiting - // would require complex mocking of the wait.Waiter -} -
-
Or rename to reflect actual behavior:
-func TestDefaultWaiter_WaitForCondition(t *testing.T) { +func TestDefaultWaiter_Construction(t *testing.T) {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func TestDefaultWaiter_WaitForCondition(t *testing.T) { | |
| t.Parallel() | |
| mockClient := &Client{client: &mockClient{}} | |
| scheme := runtime.NewScheme() | |
| waiter := NewDefaultWaiter(mockClient, scheme) | |
| require.NotNil(t, waiter) | |
| require.NotNil(t, waiter.waiter) | |
| // Test that the waiter has the expected timeout and interval | |
| // This is more of a constructor test since the actual waiting | |
| // would require complex mocking of the wait.Waiter | |
| } | |
| func TestNewDefaultWaiter(t *testing.T) { | |
| // ... rest of the file |
| func TestDefaultWaiter_WaitForCondition(t *testing.T) { | |
| t.Parallel() | |
| mockClient := &Client{client: &mockClient{}} | |
| scheme := runtime.NewScheme() | |
| waiter := NewDefaultWaiter(mockClient, scheme) | |
| require.NotNil(t, waiter) | |
| require.NotNil(t, waiter.waiter) | |
| // Test that the waiter has the expected timeout and interval | |
| // This is more of a constructor test since the actual waiting | |
| // would require complex mocking of the wait.Waiter | |
| } | |
| func TestDefaultWaiter_Construction(t *testing.T) { | |
| t.Parallel() | |
| mockClient := &Client{client: &mockClient{}} | |
| scheme := runtime.NewScheme() | |
| waiter := NewDefaultWaiter(mockClient, scheme) | |
| require.NotNil(t, waiter) | |
| require.NotNil(t, waiter.waiter) | |
| // Test that the waiter has the expected timeout and interval | |
| // This is more of a constructor test since the actual waiting | |
| // would require complex mocking of the wait.Waiter | |
| } |
🤖 Prompt for AI Agents
In internal/cmd/cmd_test.go around lines 143 to 156, the test named
TestDefaultWaiter_WaitForCondition does not exercise any waiting behavior and
duplicates TestNewDefaultWaiter; either delete this test to remove the
duplicate, or rename and adjust it to reflect its true intent (e.g.,
TestNewDefaultWaiter_Constructor) and assert the constructed waiter's fields
(timeout/interval) or add proper expectations by exposing/mock the internal
wait.Waiter to verify waiting behavior; pick one approach and update the test
name and assertions accordingly so the test name matches what it actually
verifies.
| func TestDefaultDigestResolver_ResolveDigest_Insecure(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| resolver := &defaultDigestResolver{} | ||
|
|
||
| // Test with invalid reference to ensure we get the expected error format | ||
| // without actually making network calls | ||
| _, err := resolver.ResolveDigest("invalid-reference", WithInsecure(true)) | ||
|
|
||
| // We expect an error since we're using an invalid reference | ||
| assert.Error(t, err) | ||
| } | ||
|
|
||
| func TestDefaultDigestResolver_ResolveDigest_Secure(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| resolver := &defaultDigestResolver{} | ||
|
|
||
| // Test with invalid reference to ensure we get the expected error format | ||
| // without actually making network calls | ||
| _, err := resolver.ResolveDigest("invalid-reference", WithInsecure(false)) | ||
|
|
||
| // We expect an error since we're using an invalid reference | ||
| assert.Error(t, err) | ||
| } | ||
|
|
||
| func TestDefaultDigestResolver_ResolveDigest_NoOptions(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| resolver := &defaultDigestResolver{} | ||
|
|
||
| // Test with invalid reference to ensure we get the expected error format | ||
| // without actually making network calls | ||
| _, err := resolver.ResolveDigest("invalid-reference") | ||
|
|
||
| // We expect an error since we're using an invalid reference | ||
| assert.Error(t, err) | ||
| } |
There was a problem hiding this comment.
Strengthen test assertions to verify insecure option behavior.
These three tests are essentially identical—they only verify that an invalid reference produces an error, but they don't verify that the insecure option actually affects resolver behavior differently. Consider:
- Using a mock resolver to verify the insecure flag is passed correctly
- Testing valid but different scenarios for insecure vs. secure mode
- Verifying error message differences between modes
As written, these tests would pass even if the insecure option was completely ignored by the resolver.
| func TestBackoffConfig_Default(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| cfg BackoffConfig | ||
| expected BackoffConfig | ||
| }{ | ||
| { | ||
| name: "empty config gets defaults", | ||
| cfg: BackoffConfig{}, | ||
| expected: BackoffConfig{ | ||
| InitialBackoff: func() *time.Duration { d := DefaultInitialBackoff; return &d }(), | ||
| MaxBackoff: func() *time.Duration { d := DefaultMaxBackoff; return &d }(), | ||
| }, | ||
| }, | ||
| { | ||
| name: "config with initial backoff preserves it", | ||
| cfg: BackoffConfig{ | ||
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | ||
| }, | ||
| expected: BackoffConfig{ | ||
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | ||
| MaxBackoff: func() *time.Duration { d := DefaultMaxBackoff; return &d }(), | ||
| }, | ||
| }, | ||
| { | ||
| name: "config with max backoff preserves it", | ||
| cfg: BackoffConfig{ | ||
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | ||
| }, | ||
| expected: BackoffConfig{ | ||
| InitialBackoff: func() *time.Duration { d := DefaultInitialBackoff; return &d }(), | ||
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | ||
| }, | ||
| }, | ||
| { | ||
| name: "config with both values preserves them", | ||
| cfg: BackoffConfig{ | ||
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | ||
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | ||
| }, | ||
| expected: BackoffConfig{ | ||
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | ||
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(test.name, func(t *testing.T) { | ||
| t.Parallel() | ||
| cfg := test.cfg | ||
| cfg.Default() | ||
|
|
||
| assert.Equal(t, *test.expected.InitialBackoff, *cfg.InitialBackoff) | ||
| assert.Equal(t, *test.expected.MaxBackoff, *cfg.MaxBackoff) | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix range variable capture in parallel TestBackoffConfig_Default subtests.
The table-driven Default tests use t.Run + t.Parallel() and capture the test loop variable, which can lead to all subtests observing the last case. Rebinding per-iteration avoids this.
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
+ for _, test := range tests {
+ test := test // capture range variable for this subtest
+ t.Run(test.name, func(t *testing.T) {
t.Parallel()
cfg := test.cfg
cfg.Default()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func TestBackoffConfig_Default(t *testing.T) { | |
| t.Parallel() | |
| tests := []struct { | |
| name string | |
| cfg BackoffConfig | |
| expected BackoffConfig | |
| }{ | |
| { | |
| name: "empty config gets defaults", | |
| cfg: BackoffConfig{}, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := DefaultInitialBackoff; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := DefaultMaxBackoff; return &d }(), | |
| }, | |
| }, | |
| { | |
| name: "config with initial backoff preserves it", | |
| cfg: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| }, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := DefaultMaxBackoff; return &d }(), | |
| }, | |
| }, | |
| { | |
| name: "config with max backoff preserves it", | |
| cfg: BackoffConfig{ | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := DefaultInitialBackoff; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| }, | |
| { | |
| name: "config with both values preserves them", | |
| cfg: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| }, | |
| } | |
| for _, test := range tests { | |
| t.Run(test.name, func(t *testing.T) { | |
| t.Parallel() | |
| cfg := test.cfg | |
| cfg.Default() | |
| assert.Equal(t, *test.expected.InitialBackoff, *cfg.InitialBackoff) | |
| assert.Equal(t, *test.expected.MaxBackoff, *cfg.MaxBackoff) | |
| }) | |
| } | |
| } | |
| func TestBackoffConfig_Default(t *testing.T) { | |
| t.Parallel() | |
| tests := []struct { | |
| name string | |
| cfg BackoffConfig | |
| expected BackoffConfig | |
| }{ | |
| { | |
| name: "empty config gets defaults", | |
| cfg: BackoffConfig{}, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := DefaultInitialBackoff; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := DefaultMaxBackoff; return &d }(), | |
| }, | |
| }, | |
| { | |
| name: "config with initial backoff preserves it", | |
| cfg: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| }, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := DefaultMaxBackoff; return &d }(), | |
| }, | |
| }, | |
| { | |
| name: "config with max backoff preserves it", | |
| cfg: BackoffConfig{ | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := DefaultInitialBackoff; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| }, | |
| { | |
| name: "config with both values preserves them", | |
| cfg: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| expected: BackoffConfig{ | |
| InitialBackoff: func() *time.Duration { d := 5 * time.Second; return &d }(), | |
| MaxBackoff: func() *time.Duration { d := 60 * time.Second; return &d }(), | |
| }, | |
| }, | |
| } | |
| for _, test := range tests { | |
| test := test // capture range variable for this subtest | |
| t.Run(test.name, func(t *testing.T) { | |
| t.Parallel() | |
| cfg := test.cfg | |
| cfg.Default() | |
| assert.Equal(t, *test.expected.InitialBackoff, *cfg.InitialBackoff) | |
| assert.Equal(t, *test.expected.MaxBackoff, *cfg.MaxBackoff) | |
| }) | |
| } | |
| } |
🤖 Prompt for AI Agents
internal/controllers/config_test.go lines 35-94: the table-driven test launches
parallel subtests while using the loop variable `test`, which causes a race
where all subtests may see the final iteration; rebind the loop variable
per-iteration (e.g., `tc := test` or rename the loop variable in the range)
before calling t.Run and t.Parallel so each subtest captures its own copy;
ensure t.Parallel remains inside the subtest closure after the rebind.
| func TestWithInitialBackoff_ConfigureBackoff(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| duration time.Duration | ||
| }{ | ||
| { | ||
| name: "5 seconds", | ||
| duration: 5 * time.Second, | ||
| }, | ||
| { | ||
| name: "1 minute", | ||
| duration: 1 * time.Minute, | ||
| }, | ||
| { | ||
| name: "30 seconds", | ||
| duration: 30 * time.Second, | ||
| }, | ||
| { | ||
| name: "zero duration", | ||
| duration: 0, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(test.name, func(t *testing.T) { | ||
| t.Parallel() | ||
| option := WithInitialBackoff(test.duration) | ||
| config := &BackoffConfig{} | ||
|
|
||
| option.ConfigureBackoff(config) | ||
|
|
||
| assert.NotNil(t, config.InitialBackoff) | ||
| assert.Equal(t, test.duration, *config.InitialBackoff) | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix range variable capture in parallel subtests (InitialBackoff).
Using test from the range loop inside a t.Run that calls t.Parallel() can cause all subtests to see the last table entry due to how Go reuses the loop variable. Rebind per-iteration before starting the subtest.
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
- option := WithInitialBackoff(test.duration)
+ for _, test := range tests {
+ test := test // capture range variable for this subtest
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ option := WithInitialBackoff(test.duration)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func TestWithInitialBackoff_ConfigureBackoff(t *testing.T) { | |
| t.Parallel() | |
| tests := []struct { | |
| name string | |
| duration time.Duration | |
| }{ | |
| { | |
| name: "5 seconds", | |
| duration: 5 * time.Second, | |
| }, | |
| { | |
| name: "1 minute", | |
| duration: 1 * time.Minute, | |
| }, | |
| { | |
| name: "30 seconds", | |
| duration: 30 * time.Second, | |
| }, | |
| { | |
| name: "zero duration", | |
| duration: 0, | |
| }, | |
| } | |
| for _, test := range tests { | |
| t.Run(test.name, func(t *testing.T) { | |
| t.Parallel() | |
| option := WithInitialBackoff(test.duration) | |
| config := &BackoffConfig{} | |
| option.ConfigureBackoff(config) | |
| assert.NotNil(t, config.InitialBackoff) | |
| assert.Equal(t, test.duration, *config.InitialBackoff) | |
| }) | |
| } | |
| } | |
| func TestWithInitialBackoff_ConfigureBackoff(t *testing.T) { | |
| t.Parallel() | |
| tests := []struct { | |
| name string | |
| duration time.Duration | |
| }{ | |
| { | |
| name: "5 seconds", | |
| duration: 5 * time.Second, | |
| }, | |
| { | |
| name: "1 minute", | |
| duration: 1 * time.Minute, | |
| }, | |
| { | |
| name: "30 seconds", | |
| duration: 30 * time.Second, | |
| }, | |
| { | |
| name: "zero duration", | |
| duration: 0, | |
| }, | |
| } | |
| for _, test := range tests { | |
| test := test // capture range variable for this subtest | |
| t.Run(test.name, func(t *testing.T) { | |
| t.Parallel() | |
| option := WithInitialBackoff(test.duration) | |
| config := &BackoffConfig{} | |
| option.ConfigureBackoff(config) | |
| assert.NotNil(t, config.InitialBackoff) | |
| assert.Equal(t, test.duration, *config.InitialBackoff) | |
| }) | |
| } | |
| } |
🤖 Prompt for AI Agents
In internal/controllers/options_test.go around lines 10 to 47, the table-driven
subtests call t.Run with t.Parallel() while using the loop variable `test`
directly, which causes a range variable capture bug; rebind the loop variable
per-iteration (e.g., `tc := test`) immediately inside the loop and use `tc` in
the t.Run closure (and in any nested goroutines) so each parallel subtest sees
its own copy of the test data.
| func TestWithMaxBackoff_ConfigureBackoff(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| duration time.Duration | ||
| }{ | ||
| { | ||
| name: "5 minutes", | ||
| duration: 5 * time.Minute, | ||
| }, | ||
| { | ||
| name: "10 minutes", | ||
| duration: 10 * time.Minute, | ||
| }, | ||
| { | ||
| name: "1 hour", | ||
| duration: 1 * time.Hour, | ||
| }, | ||
| { | ||
| name: "zero duration", | ||
| duration: 0, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(test.name, func(t *testing.T) { | ||
| t.Parallel() | ||
| option := WithMaxBackoff(test.duration) | ||
| config := &BackoffConfig{} | ||
|
|
||
| option.ConfigureBackoff(config) | ||
|
|
||
| assert.NotNil(t, config.MaxBackoff) | ||
| assert.Equal(t, test.duration, *config.MaxBackoff) | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix range variable capture in parallel subtests (MaxBackoff).
Same issue here: the test loop variable is captured by parallel subtests, so cases can all run with the last table entry. Rebind inside the loop.
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
- option := WithMaxBackoff(test.duration)
+ for _, test := range tests {
+ test := test // capture range variable for this subtest
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ option := WithMaxBackoff(test.duration)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func TestWithMaxBackoff_ConfigureBackoff(t *testing.T) { | |
| t.Parallel() | |
| tests := []struct { | |
| name string | |
| duration time.Duration | |
| }{ | |
| { | |
| name: "5 minutes", | |
| duration: 5 * time.Minute, | |
| }, | |
| { | |
| name: "10 minutes", | |
| duration: 10 * time.Minute, | |
| }, | |
| { | |
| name: "1 hour", | |
| duration: 1 * time.Hour, | |
| }, | |
| { | |
| name: "zero duration", | |
| duration: 0, | |
| }, | |
| } | |
| for _, test := range tests { | |
| t.Run(test.name, func(t *testing.T) { | |
| t.Parallel() | |
| option := WithMaxBackoff(test.duration) | |
| config := &BackoffConfig{} | |
| option.ConfigureBackoff(config) | |
| assert.NotNil(t, config.MaxBackoff) | |
| assert.Equal(t, test.duration, *config.MaxBackoff) | |
| }) | |
| } | |
| } | |
| func TestWithMaxBackoff_ConfigureBackoff(t *testing.T) { | |
| t.Parallel() | |
| tests := []struct { | |
| name string | |
| duration time.Duration | |
| }{ | |
| { | |
| name: "5 minutes", | |
| duration: 5 * time.Minute, | |
| }, | |
| { | |
| name: "10 minutes", | |
| duration: 10 * time.Minute, | |
| }, | |
| { | |
| name: "1 hour", | |
| duration: 1 * time.Hour, | |
| }, | |
| { | |
| name: "zero duration", | |
| duration: 0, | |
| }, | |
| } | |
| for _, test := range tests { | |
| test := test // capture range variable for this subtest | |
| t.Run(test.name, func(t *testing.T) { | |
| t.Parallel() | |
| option := WithMaxBackoff(test.duration) | |
| config := &BackoffConfig{} | |
| option.ConfigureBackoff(config) | |
| assert.NotNil(t, config.MaxBackoff) | |
| assert.Equal(t, test.duration, *config.MaxBackoff) | |
| }) | |
| } | |
| } |
🤖 Prompt for AI Agents
internal/controllers/options_test.go around lines 49 to 86: the table-driven
tests spawn parallel subtests while capturing the loop variable `test`, causing
all subtests to potentially see the last table entry; rebind the loop variable
inside the for loop (e.g., assign `tt := test`) and use that new variable in the
t.Run closure and assertions so each subtest gets its own copy of the test case
before calling t.Parallel().
Company asked me to do use AI for code generation, this the result
(after heavily editing it).