tests: Add/Fix tests for new flag and fix docker wait, make fmt issue
Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
		
							parent
							
								
									1c59d0a539
								
							
						
					
					
						commit
						acaaa34d69
					
				|  | @ -9,6 +9,7 @@ import ( | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| 	"go.uber.org/zap" | 	"go.uber.org/zap" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/helmfile/helmfile/pkg/common" | ||||||
| 	"github.com/helmfile/helmfile/pkg/exectest" | 	"github.com/helmfile/helmfile/pkg/exectest" | ||||||
| 	"github.com/helmfile/helmfile/pkg/filesystem" | 	"github.com/helmfile/helmfile/pkg/filesystem" | ||||||
| 	"github.com/helmfile/helmfile/pkg/helmexec" | 	"github.com/helmfile/helmfile/pkg/helmexec" | ||||||
|  | @ -416,3 +417,138 @@ releases: | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestDiffWithIncludeCRDs(t *testing.T) { | ||||||
|  | 	type fields struct { | ||||||
|  | 		includeCRDs common.BoolFlag | ||||||
|  | 		skipCRDs    common.BoolFlag | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	type testcase struct { | ||||||
|  | 		fields    fields | ||||||
|  | 		ns        string | ||||||
|  | 		error     string | ||||||
|  | 		selectors []string | ||||||
|  | 		diffed    []exectest.Release | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	check := func(t *testing.T, tc testcase) { | ||||||
|  | 		t.Helper() | ||||||
|  | 
 | ||||||
|  | 		wantDiffs := tc.diffed | ||||||
|  | 
 | ||||||
|  | 		var helm = &exectest.Helm{ | ||||||
|  | 			FailOnUnexpectedList: true, | ||||||
|  | 			FailOnUnexpectedDiff: true, | ||||||
|  | 			DiffMutex:            &sync.Mutex{}, | ||||||
|  | 			ChartsMutex:          &sync.Mutex{}, | ||||||
|  | 			ReleasesMutex:        &sync.Mutex{}, | ||||||
|  | 			Helm3:                true, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bs := runWithLogCapture(t, "debug", func(t *testing.T, logger *zap.SugaredLogger) { | ||||||
|  | 			t.Helper() | ||||||
|  | 
 | ||||||
|  | 			valsRuntime, err := vals.New(vals.Options{CacheSize: 32}) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Errorf("unexpected error creating vals runtime: %v", err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			files := map[string]string{ | ||||||
|  | 				"/path/to/helmfile.yaml": ` | ||||||
|  | releases: | ||||||
|  | - name: include-crds | ||||||
|  |   chart: incubator/raw | ||||||
|  |   namespace: default | ||||||
|  | `, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			app := appWithFs(&App{ | ||||||
|  | 				OverrideHelmBinary:  DefaultHelmBinary, | ||||||
|  | 				fs:                  filesystem.DefaultFileSystem(), | ||||||
|  | 				OverrideKubeContext: "default", | ||||||
|  | 				Env:                 "default", | ||||||
|  | 				Logger:              logger, | ||||||
|  | 				helms: map[helmKey]helmexec.Interface{ | ||||||
|  | 					createHelmKey("helm", "default"): helm, | ||||||
|  | 				}, | ||||||
|  | 				valsRuntime: valsRuntime, | ||||||
|  | 			}, files) | ||||||
|  | 
 | ||||||
|  | 			if tc.ns != "" { | ||||||
|  | 				app.Namespace = tc.ns | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if tc.selectors != nil { | ||||||
|  | 				app.Selectors = tc.selectors | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			diffConfig := NewApplyConfigWithDefaults(&applyConfig{ | ||||||
|  | 				// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
 | ||||||
|  | 				concurrency: 1, | ||||||
|  | 				logger:      logger, | ||||||
|  | 				includeCRDs: tc.fields.includeCRDs, | ||||||
|  | 				skipCRDs:    tc.fields.skipCRDs, | ||||||
|  | 			}) | ||||||
|  | 			diffErr := app.Diff(diffConfig) | ||||||
|  | 
 | ||||||
|  | 			var gotErr string | ||||||
|  | 			if diffErr != nil { | ||||||
|  | 				gotErr = diffErr.Error() | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if d := cmp.Diff(tc.error, gotErr); d != "" { | ||||||
|  | 				t.Fatalf("unexpected error: want (-), got (+): %s", d) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			require.Equal(t, wantDiffs, helm.Diffed) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		testhelper.RequireLog(t, "app_diff_test", bs) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t.Run("include-crds", func(t *testing.T) { | ||||||
|  | 		includeCRDs := common.NewBoolFlag(false) | ||||||
|  | 		includeCRDs.Set(true) | ||||||
|  | 
 | ||||||
|  | 		check(t, testcase{ | ||||||
|  | 			fields: fields{ | ||||||
|  | 				skipCRDs:    common.NewBoolFlag(false), | ||||||
|  | 				includeCRDs: includeCRDs, | ||||||
|  | 			}, | ||||||
|  | 			diffed: []exectest.Release{ | ||||||
|  | 				{Name: "include-crds", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values", "--include-crds"}}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("include-or-skip-crds-unset", func(t *testing.T) { | ||||||
|  | 		includeCRDs := common.NewBoolFlag(false) | ||||||
|  | 		includeCRDs.Set(true) | ||||||
|  | 
 | ||||||
|  | 		check(t, testcase{ | ||||||
|  | 			fields: fields{ | ||||||
|  | 				skipCRDs:    common.NewBoolFlag(false), | ||||||
|  | 				includeCRDs: common.NewBoolFlag(false), | ||||||
|  | 			}, | ||||||
|  | 			diffed: []exectest.Release{ | ||||||
|  | 				{Name: "include-crds", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values"}}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("skip-crds", func(t *testing.T) { | ||||||
|  | 		skipCRDs := common.NewBoolFlag(false) | ||||||
|  | 		skipCRDs.Set(true) | ||||||
|  | 
 | ||||||
|  | 		check(t, testcase{ | ||||||
|  | 			fields: fields{ | ||||||
|  | 				skipCRDs:    skipCRDs, | ||||||
|  | 				includeCRDs: common.NewBoolFlag(false), | ||||||
|  | 			}, | ||||||
|  | 			diffed: []exectest.Release{ | ||||||
|  | 				{Name: "include-crds", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values", "--skip-crds"}}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2172,11 +2172,18 @@ func (c configImpl) IncludeCRDs() bool { | ||||||
| 	return c.includeCRDs.Value() | 	return c.includeCRDs.Value() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ShouldIncludeCRDs determines if CRDs should be included in the operation.
 | ||||||
|  | // It returns true only when:
 | ||||||
|  | //   - includeCRDs flag is explicitly provided on the command line and set to true
 | ||||||
|  | //   - AND skipCRDs flag is not provided on the command line
 | ||||||
|  | //
 | ||||||
|  | // This ensures that CRDs are only included when explicitly requested and not
 | ||||||
|  | // contradicted by the skipCRDs flag.
 | ||||||
| func (c configImpl) ShouldIncludeCRDs() bool { | func (c configImpl) ShouldIncludeCRDs() bool { | ||||||
| 	includeCRDsExplicit := c.includeCRDs.WasExplicitlySet() && c.includeCRDs.Value() | 	includeCRDsExplicit := c.includeCRDs.WasExplicitlySet() && c.includeCRDs.Value() | ||||||
| 	skipCRDsExplicit := c.skipCRDs.WasExplicitlySet() && !c.skipCRDs.Value() | 	skipCRDsNotProvided := !c.skipCRDs.WasExplicitlySet() | ||||||
| 
 | 
 | ||||||
| 	return includeCRDsExplicit || skipCRDsExplicit | 	return includeCRDsExplicit && skipCRDsNotProvided | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c configImpl) SkipRefresh() bool { | func (c configImpl) SkipRefresh() bool { | ||||||
|  | @ -2324,203 +2331,210 @@ func NewApplyConfigWithDefaults(existing *applyConfig) *applyConfig { | ||||||
| 	return existing | 	return existing | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Args() string { | func (c applyConfig) Args() string { | ||||||
| 	return a.args | 	return c.args | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Cascade() string { | func (c applyConfig) Cascade() string { | ||||||
| 	return a.cascade | 	return c.cascade | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Wait() bool { | func (c applyConfig) Wait() bool { | ||||||
| 	return a.wait | 	return c.wait | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) WaitRetries() int { | func (c applyConfig) WaitRetries() int { | ||||||
| 	return a.waitRetries | 	return c.waitRetries | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) WaitForJobs() bool { | func (c applyConfig) WaitForJobs() bool { | ||||||
| 	return a.waitForJobs | 	return c.waitForJobs | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Values() []string { | func (c applyConfig) Values() []string { | ||||||
| 	return a.values | 	return c.values | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Set() []string { | func (c applyConfig) Set() []string { | ||||||
| 	return a.set | 	return c.set | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Validate() bool { | func (c applyConfig) Validate() bool { | ||||||
| 	return a.validate | 	return c.validate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipCleanup() bool { | func (c applyConfig) SkipCleanup() bool { | ||||||
| 	return a.skipCleanup | 	return c.skipCleanup | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipCRDs() bool { | func (c applyConfig) SkipCRDs() bool { | ||||||
| 	return a.skipCRDs.Value() | 	return c.skipCRDs.Value() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) IncludeCRDs() bool { | func (c applyConfig) IncludeCRDs() bool { | ||||||
| 	return a.includeCRDs.Value() | 	return c.includeCRDs.Value() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ShouldIncludeCRDs determines if CRDs should be included in the operation.
 | ||||||
|  | // It returns true only when:
 | ||||||
|  | //   - includeCRDs flag is explicitly provided on the command line and set to true
 | ||||||
|  | //   - AND skipCRDs flag is not provided on the command line
 | ||||||
|  | //
 | ||||||
|  | // This ensures that CRDs are only included when explicitly requested and not
 | ||||||
|  | // contradicted by the skipCRDs flag.
 | ||||||
| func (c applyConfig) ShouldIncludeCRDs() bool { | func (c applyConfig) ShouldIncludeCRDs() bool { | ||||||
| 	includeCRDsExplicit := c.includeCRDs.WasExplicitlySet() && c.includeCRDs.Value() | 	includeCRDsExplicit := c.includeCRDs.WasExplicitlySet() && c.includeCRDs.Value() | ||||||
| 	skipCRDsExplicit := c.skipCRDs.WasExplicitlySet() && !c.skipCRDs.Value() | 	skipCRDsNotProvided := !c.skipCRDs.WasExplicitlySet() | ||||||
| 
 | 
 | ||||||
| 	return includeCRDsExplicit || skipCRDsExplicit | 	return includeCRDsExplicit && skipCRDsNotProvided | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipDeps() bool { | func (c applyConfig) SkipDeps() bool { | ||||||
| 	return a.skipDeps | 	return c.skipDeps | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipRefresh() bool { | func (c applyConfig) SkipRefresh() bool { | ||||||
| 	return a.skipRefresh | 	return c.skipRefresh | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipNeeds() bool { | func (c applyConfig) SkipNeeds() bool { | ||||||
| 	return a.skipNeeds | 	return c.skipNeeds | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) IncludeNeeds() bool { | func (c applyConfig) IncludeNeeds() bool { | ||||||
| 	return a.includeNeeds || a.IncludeTransitiveNeeds() | 	return c.includeNeeds || c.IncludeTransitiveNeeds() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) IncludeTransitiveNeeds() bool { | func (c applyConfig) IncludeTransitiveNeeds() bool { | ||||||
| 	return a.includeTransitiveNeeds | 	return c.includeTransitiveNeeds | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) IncludeTests() bool { | func (c applyConfig) IncludeTests() bool { | ||||||
| 	return a.includeTests | 	return c.includeTests | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Suppress() []string { | func (c applyConfig) Suppress() []string { | ||||||
| 	return a.suppress | 	return c.suppress | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SuppressSecrets() bool { | func (c applyConfig) SuppressSecrets() bool { | ||||||
| 	return a.suppressSecrets | 	return c.suppressSecrets | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) ShowSecrets() bool { | func (c applyConfig) ShowSecrets() bool { | ||||||
| 	return a.showSecrets | 	return c.showSecrets | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) NoHooks() bool { | func (c applyConfig) NoHooks() bool { | ||||||
| 	return a.noHooks | 	return c.noHooks | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SuppressDiff() bool { | func (c applyConfig) SuppressDiff() bool { | ||||||
| 	return a.suppressDiff | 	return c.suppressDiff | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Color() bool { | func (c applyConfig) Color() bool { | ||||||
| 	return a.color | 	return c.color | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) NoColor() bool { | func (c applyConfig) NoColor() bool { | ||||||
| 	return a.noColor | 	return c.noColor | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Context() int { | func (c applyConfig) Context() int { | ||||||
| 	return a.context | 	return c.context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) DiffOutput() string { | func (c applyConfig) DiffOutput() string { | ||||||
| 	return a.diffOutput | 	return c.diffOutput | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Concurrency() int { | func (c applyConfig) Concurrency() int { | ||||||
| 	return a.concurrency | 	return c.concurrency | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) DetailedExitcode() bool { | func (c applyConfig) DetailedExitcode() bool { | ||||||
| 	return a.detailedExitcode | 	return c.detailedExitcode | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) StripTrailingCR() bool { | func (c applyConfig) StripTrailingCR() bool { | ||||||
| 	return a.stripTrailingCR | 	return c.stripTrailingCR | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Interactive() bool { | func (c applyConfig) Interactive() bool { | ||||||
| 	return a.interactive | 	return c.interactive | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) Logger() *zap.SugaredLogger { | func (c applyConfig) Logger() *zap.SugaredLogger { | ||||||
| 	return a.logger | 	return c.logger | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipDiffOnInstall() bool { | func (c applyConfig) SkipDiffOnInstall() bool { | ||||||
| 	return a.skipDiffOnInstall | 	return c.skipDiffOnInstall | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SyncArgs() string { | func (c applyConfig) SyncArgs() string { | ||||||
| 	return a.syncArgs | 	return c.syncArgs | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) DiffArgs() string { | func (c applyConfig) DiffArgs() string { | ||||||
| 	return a.diffArgs | 	return c.diffArgs | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // helmfile-template-only flags
 | // helmfile-template-only flags
 | ||||||
| func (a applyConfig) SkipTests() bool { | func (c applyConfig) SkipTests() bool { | ||||||
| 	return a.skipTests | 	return c.skipTests | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) OutputDir() string { | func (c applyConfig) OutputDir() string { | ||||||
| 	return a.outputDir | 	return c.outputDir | ||||||
| } | } | ||||||
| func (a applyConfig) OutputDirTemplate() string { | func (c applyConfig) OutputDirTemplate() string { | ||||||
| 	return a.outputDirTemplate | 	return c.outputDirTemplate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) ReuseValues() bool { | func (c applyConfig) ReuseValues() bool { | ||||||
| 	return a.reuseValues | 	return c.reuseValues | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) ResetValues() bool { | func (c applyConfig) ResetValues() bool { | ||||||
| 	return !a.reuseValues | 	return !c.reuseValues | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) PostRenderer() string { | func (c applyConfig) PostRenderer() string { | ||||||
| 	return a.postRenderer | 	return c.postRenderer | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) PostRendererArgs() []string { | func (c applyConfig) PostRendererArgs() []string { | ||||||
| 	return a.postRendererArgs | 	return c.postRendererArgs | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SuppressOutputLineRegex() []string { | func (c applyConfig) SuppressOutputLineRegex() []string { | ||||||
| 	return a.suppressOutputLineRegex | 	return c.suppressOutputLineRegex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) KubeVersion() string { | func (c applyConfig) KubeVersion() string { | ||||||
| 	return a.kubeVersion | 	return c.kubeVersion | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SkipSchemaValidation() bool { | func (c applyConfig) SkipSchemaValidation() bool { | ||||||
| 	return a.skipSchemaValidation | 	return c.skipSchemaValidation | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) ShowOnly() []string { | func (c applyConfig) ShowOnly() []string { | ||||||
| 	return a.showOnly | 	return c.showOnly | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) HideNotes() bool { | func (c applyConfig) HideNotes() bool { | ||||||
| 	return a.hideNotes | 	return c.hideNotes | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) TakeOwnership() bool { | func (c applyConfig) TakeOwnership() bool { | ||||||
| 	return a.takeOwnership | 	return c.takeOwnership | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a applyConfig) SyncReleaseLabels() bool { | func (c applyConfig) SyncReleaseLabels() bool { | ||||||
| 	return a.syncReleaseLabels | 	return c.syncReleaseLabels | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type depsConfig struct { | type depsConfig struct { | ||||||
|  | @ -2528,19 +2542,19 @@ type depsConfig struct { | ||||||
| 	includeTransitiveNeeds bool | 	includeTransitiveNeeds bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d depsConfig) SkipRepos() bool { | func (c depsConfig) SkipRepos() bool { | ||||||
| 	return d.skipRepos | 	return c.skipRepos | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d depsConfig) IncludeTransitiveNeeds() bool { | func (c depsConfig) IncludeTransitiveNeeds() bool { | ||||||
| 	return d.includeTransitiveNeeds | 	return c.includeTransitiveNeeds | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d depsConfig) Args() string { | func (c depsConfig) Args() string { | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d depsConfig) Concurrency() int { | func (c depsConfig) Concurrency() int { | ||||||
| 	return 2 | 	return 2 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -75,151 +75,158 @@ func NewDiffConfigWithDefaults(existing *diffConfig) *diffConfig { | ||||||
| 	return existing | 	return existing | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Args() string { | func (c diffConfig) Args() string { | ||||||
| 	return a.args | 	return c.args | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) DiffArgs() string { | func (c diffConfig) DiffArgs() string { | ||||||
| 	return a.diffArgs | 	return c.diffArgs | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Values() []string { | func (c diffConfig) Values() []string { | ||||||
| 	return a.values | 	return c.values | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Set() []string { | func (c diffConfig) Set() []string { | ||||||
| 	return a.set | 	return c.set | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Validate() bool { | func (c diffConfig) Validate() bool { | ||||||
| 	return a.validate | 	return c.validate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SkipCRDs() bool { | func (c diffConfig) SkipCRDs() bool { | ||||||
| 	return a.skipCRDs.Value() | 	return c.skipCRDs.Value() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) IncludeCRDs() bool { | func (c diffConfig) IncludeCRDs() bool { | ||||||
| 	return a.includeCRDs.Value() | 	return c.includeCRDs.Value() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) ShouldIncludeCRDs() bool { | // ShouldIncludeCRDs determines if CRDs should be included in the operation.
 | ||||||
| 	includeCRDsExplicit := a.includeCRDs.WasExplicitlySet() && a.includeCRDs.Value() | // It returns true only when:
 | ||||||
| 	skipCRDsExplicit := a.skipCRDs.WasExplicitlySet() && !a.skipCRDs.Value() | //   - includeCRDs flag is explicitly provided on the command line and set to true
 | ||||||
|  | //   - AND skipCRDs flag is not provided on the command line
 | ||||||
|  | //
 | ||||||
|  | // This ensures that CRDs are only included when explicitly requested and not
 | ||||||
|  | // contradicted by the skipCRDs flag.
 | ||||||
|  | func (c diffConfig) ShouldIncludeCRDs() bool { | ||||||
|  | 	includeCRDsExplicit := c.includeCRDs.WasExplicitlySet() && c.includeCRDs.Value() | ||||||
|  | 	skipCRDsNotProvided := !c.skipCRDs.WasExplicitlySet() | ||||||
| 
 | 
 | ||||||
| 	return includeCRDsExplicit || skipCRDsExplicit | 	return includeCRDsExplicit && skipCRDsNotProvided | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SkipDeps() bool { | func (c diffConfig) SkipDeps() bool { | ||||||
| 	return a.skipDeps | 	return c.skipDeps | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SkipRefresh() bool { | func (c diffConfig) SkipRefresh() bool { | ||||||
| 	return a.skipRefresh | 	return c.skipRefresh | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) IncludeTests() bool { | func (c diffConfig) IncludeTests() bool { | ||||||
| 	return a.includeTests | 	return c.includeTests | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SkipNeeds() bool { | func (c diffConfig) SkipNeeds() bool { | ||||||
| 	return a.skipNeeds | 	return c.skipNeeds | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) IncludeNeeds() bool { | func (c diffConfig) IncludeNeeds() bool { | ||||||
| 	return a.includeNeeds || a.IncludeTransitiveNeeds() | 	return c.includeNeeds || c.IncludeTransitiveNeeds() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) IncludeTransitiveNeeds() bool { | func (c diffConfig) IncludeTransitiveNeeds() bool { | ||||||
| 	return a.includeTransitiveNeeds | 	return c.includeTransitiveNeeds | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Suppress() []string { | func (c diffConfig) Suppress() []string { | ||||||
| 	return a.suppress | 	return c.suppress | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SuppressSecrets() bool { | func (c diffConfig) SuppressSecrets() bool { | ||||||
| 	return a.suppressSecrets | 	return c.suppressSecrets | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) ShowSecrets() bool { | func (c diffConfig) ShowSecrets() bool { | ||||||
| 	return a.showSecrets | 	return c.showSecrets | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) NoHooks() bool { | func (c diffConfig) NoHooks() bool { | ||||||
| 	return a.noHooks | 	return c.noHooks | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SuppressDiff() bool { | func (c diffConfig) SuppressDiff() bool { | ||||||
| 	return a.suppressDiff | 	return c.suppressDiff | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Color() bool { | func (c diffConfig) Color() bool { | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) NoColor() bool { | func (c diffConfig) NoColor() bool { | ||||||
| 	return a.noColor | 	return c.noColor | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Context() int { | func (c diffConfig) Context() int { | ||||||
| 	return a.context | 	return c.context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) DiffOutput() string { | func (c diffConfig) DiffOutput() string { | ||||||
| 	return a.diffOutput | 	return c.diffOutput | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Concurrency() int { | func (c diffConfig) Concurrency() int { | ||||||
| 	return a.concurrency | 	return c.concurrency | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) DetailedExitcode() bool { | func (c diffConfig) DetailedExitcode() bool { | ||||||
| 	return a.detailedExitcode | 	return c.detailedExitcode | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) StripTrailingCR() bool { | func (c diffConfig) StripTrailingCR() bool { | ||||||
| 	return a.stripTrailingCR | 	return c.stripTrailingCR | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Interactive() bool { | func (c diffConfig) Interactive() bool { | ||||||
| 	return a.interactive | 	return c.interactive | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SkipDiffOnInstall() bool { | func (c diffConfig) SkipDiffOnInstall() bool { | ||||||
| 	return a.skipDiffOnInstall | 	return c.skipDiffOnInstall | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) Logger() *zap.SugaredLogger { | func (c diffConfig) Logger() *zap.SugaredLogger { | ||||||
| 	return a.logger | 	return c.logger | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) RetainValuesFiles() bool { | func (c diffConfig) RetainValuesFiles() bool { | ||||||
| 	return a.retainValuesFiles | 	return c.retainValuesFiles | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) ReuseValues() bool { | func (c diffConfig) ReuseValues() bool { | ||||||
| 	return a.reuseValues | 	return c.reuseValues | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) ResetValues() bool { | func (c diffConfig) ResetValues() bool { | ||||||
| 	return !a.reuseValues | 	return !c.reuseValues | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) PostRenderer() string { | func (c diffConfig) PostRenderer() string { | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) PostRendererArgs() []string { | func (c diffConfig) PostRendererArgs() []string { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SkipSchemaValidation() bool { | func (c diffConfig) SkipSchemaValidation() bool { | ||||||
| 	return a.skipSchemaValidation | 	return c.skipSchemaValidation | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a diffConfig) SuppressOutputLineRegex() []string { | func (c diffConfig) SuppressOutputLineRegex() []string { | ||||||
| 	return a.suppressOutputLineRegex | 	return c.suppressOutputLineRegex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestDiff(t *testing.T) { | func TestDiff(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | processing file "helmfile.yaml" in directory "." | ||||||
|  | changing working directory to "/path/to" | ||||||
|  | merged environment: &{default  map[] map[]} | ||||||
|  | 1 release(s) found in helmfile.yaml | ||||||
|  | 
 | ||||||
|  | processing 1 groups of releases in this order: | ||||||
|  | GROUP RELEASES | ||||||
|  | 1     default/default/include-crds | ||||||
|  | 
 | ||||||
|  | processing releases in group 1/1: default/default/include-crds | ||||||
|  | changing working directory back to "/path/to" | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | processing file "helmfile.yaml" in directory "." | ||||||
|  | changing working directory to "/path/to" | ||||||
|  | merged environment: &{default  map[] map[]} | ||||||
|  | 1 release(s) found in helmfile.yaml | ||||||
|  | 
 | ||||||
|  | processing 1 groups of releases in this order: | ||||||
|  | GROUP RELEASES | ||||||
|  | 1     default/default/include-crds | ||||||
|  | 
 | ||||||
|  | processing releases in group 1/1: default/default/include-crds | ||||||
|  | changing working directory back to "/path/to" | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | processing file "helmfile.yaml" in directory "." | ||||||
|  | changing working directory to "/path/to" | ||||||
|  | merged environment: &{default  map[] map[]} | ||||||
|  | 1 release(s) found in helmfile.yaml | ||||||
|  | 
 | ||||||
|  | processing 1 groups of releases in this order: | ||||||
|  | GROUP RELEASES | ||||||
|  | 1     default/default/include-crds | ||||||
|  | 
 | ||||||
|  | processing releases in group 1/1: default/default/include-crds | ||||||
|  | changing working directory back to "/path/to" | ||||||
|  | @ -7,6 +7,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"github.com/helmfile/helmfile/pkg/helmexec" | 	"github.com/helmfile/helmfile/pkg/helmexec" | ||||||
| 	"github.com/helmfile/helmfile/pkg/testutil" | 	"github.com/helmfile/helmfile/pkg/testutil" | ||||||
|  | 	"github.com/helmfile/helmfile/pkg/version" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestAppendWaitForJobsFlags(t *testing.T) { | func TestAppendWaitForJobsFlags(t *testing.T) { | ||||||
|  | @ -432,6 +433,74 @@ func TestAppendTakeOwnershipFlags(t *testing.T) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestAppendCRDFlags(t *testing.T) { | ||||||
|  | 	type args struct { | ||||||
|  | 		flags    []string | ||||||
|  | 		helm     helmexec.Interface | ||||||
|  | 		helmSpec HelmSpec | ||||||
|  | 		opt      *DiffOpts | ||||||
|  | 		expected []string | ||||||
|  | 	} | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name string | ||||||
|  | 		args args | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "no include-crds nor skip-crds provided", | ||||||
|  | 			args: args{ | ||||||
|  | 				flags:    []string{}, | ||||||
|  | 				helm:     testutil.NewVersionHelmExec(version.HelmRequiredVersion), | ||||||
|  | 				opt:      &DiffOpts{}, | ||||||
|  | 				expected: []string{}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "include-crds set but no skip-crds", | ||||||
|  | 			args: args{ | ||||||
|  | 				flags: []string{}, | ||||||
|  | 				helm:  testutil.NewVersionHelmExec(version.HelmRequiredVersion), | ||||||
|  | 				opt: &DiffOpts{ | ||||||
|  | 					SkipCRDs:    false, | ||||||
|  | 					IncludeCRDs: true, | ||||||
|  | 				}, | ||||||
|  | 				expected: []string{"--include-crds"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "include-crds and skip-crds set", | ||||||
|  | 			args: args{ | ||||||
|  | 				flags: []string{}, | ||||||
|  | 				helm:  testutil.NewVersionHelmExec(version.HelmRequiredVersion), | ||||||
|  | 				opt: &DiffOpts{ | ||||||
|  | 					SkipCRDs:    true, | ||||||
|  | 					IncludeCRDs: true, | ||||||
|  | 				}, | ||||||
|  | 				expected: []string{"--skip-crds"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "include-crds not set but skip-crds is", | ||||||
|  | 			args: args{ | ||||||
|  | 				flags: []string{}, | ||||||
|  | 				helm:  testutil.NewVersionHelmExec(version.HelmRequiredVersion), | ||||||
|  | 				opt: &DiffOpts{ | ||||||
|  | 					SkipCRDs:    true, | ||||||
|  | 					IncludeCRDs: false, | ||||||
|  | 				}, | ||||||
|  | 				expected: []string{"--skip-crds"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  | 			st := &HelmState{} | ||||||
|  | 			st.HelmDefaults = tt.args.helmSpec | ||||||
|  | 			got := st.appendCRDFlags(tt.args.flags, tt.args.opt.SkipCRDs, tt.args.opt.IncludeCRDs) | ||||||
|  | 			require.Equalf(t, tt.args.expected, got, "appendCRDFlags() = %v, want %v", got, tt.args.expected) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestFormatLabels(t *testing.T) { | func TestFormatLabels(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name   string | 		name   string | ||||||
|  |  | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | # Helmfile Test Command Package | ||||||
|  | 
 | ||||||
|  | ## Overview | ||||||
|  | 
 | ||||||
|  | The `testcommand` package provides utilities for testing Helmfile commands in a controlled environment. This package simplifies the creation and configuration of command objects for testing purposes, allowing developers to verify command behavior without executing the full application. | ||||||
|  | 
 | ||||||
|  | ## Components | ||||||
|  | 
 | ||||||
|  | ### CommandTestHelper | ||||||
|  | 
 | ||||||
|  | The core structure that encapsulates the components needed for testing commands: | ||||||
|  | 
 | ||||||
|  | - `Cmd`: The Cobra command instance | ||||||
|  | - `Registry`: Flag registry for managing command flags | ||||||
|  | - `Options`: Command-specific options | ||||||
|  | 
 | ||||||
|  | ### Available Test Commands | ||||||
|  | 
 | ||||||
|  | The package provides helper functions to create test instances of the following Helmfile commands: | ||||||
|  | 
 | ||||||
|  | - **TestDiffCmd()**: Creates a test instance of the `diff` command | ||||||
|  | - **TestApplyCmd()**: Creates a test instance of the `apply` command | ||||||
|  | - **TestTemplateCmd()**: Creates a test instance of the `template` command | ||||||
|  | - **TestSyncCmd()**: Creates a test instance of the `sync` command | ||||||
|  | 
 | ||||||
|  | ## Usage | ||||||
|  | 
 | ||||||
|  | ```go | ||||||
|  | import ( | ||||||
|  |     "testing" | ||||||
|  |     "github.com/helmfile/helmfile/pkg/testcommand" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestMyDiffCommand(t *testing.T) { | ||||||
|  |     // Create a test diff command | ||||||
|  |     helper := testcommand.TestDiffCmd() | ||||||
|  | 
 | ||||||
|  |     // Access the command components | ||||||
|  |     cmd := helper.Cmd | ||||||
|  |     options := helper.Options.(*config.DiffOptions) | ||||||
|  | 
 | ||||||
|  |     // Set up test flags | ||||||
|  |     cmd.Flags().Set("concurrency", "5") | ||||||
|  | 
 | ||||||
|  |     // Test command behavior | ||||||
|  |     // ... | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Implementation Details | ||||||
|  | 
 | ||||||
|  | Each test command function: | ||||||
|  | 
 | ||||||
|  | 1. Creates an options factory for the specific command | ||||||
|  | 2. Instantiates the command options | ||||||
|  | 3. Gets the flag registry | ||||||
|  | 4. Creates a Cobra command instance | ||||||
|  | 5. Registers the appropriate flags | ||||||
|  | 6. Returns a helper with all components | ||||||
|  | 
 | ||||||
|  | For the `diff` command, flag values are automatically transferred to the options object. | ||||||
|  | 
 | ||||||
|  | ## Notes | ||||||
|  | 
 | ||||||
|  | This package is intended for testing purposes only and should not be used in production code. | ||||||
|  | @ -0,0 +1,111 @@ | ||||||
|  | package testcmd | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/spf13/cobra" | ||||||
|  | 
 | ||||||
|  | 	"github.com/helmfile/helmfile/pkg/config" | ||||||
|  | 	"github.com/helmfile/helmfile/pkg/factory" | ||||||
|  | 	"github.com/helmfile/helmfile/pkg/flags" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CommandTestHelper provides utilities for testing commands
 | ||||||
|  | type CommandTestHelper struct { | ||||||
|  | 	Cmd      *cobra.Command | ||||||
|  | 	Registry flags.FlagRegistry | ||||||
|  | 	Options  config.Options | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestDiffCmd creates a diff command for testing and returns a helper with its components
 | ||||||
|  | func TestDiffCmd() *CommandTestHelper { | ||||||
|  | 	// Create command components
 | ||||||
|  | 	optionsFactory := factory.NewDiffOptionsFactory() | ||||||
|  | 	options := optionsFactory.CreateOptions().(*config.DiffOptions) | ||||||
|  | 	registry := optionsFactory.GetFlagRegistry() | ||||||
|  | 
 | ||||||
|  | 	// Create command manually
 | ||||||
|  | 	cmd := &cobra.Command{ | ||||||
|  | 		Use:   "diff", | ||||||
|  | 		Short: "Diff releases defined in state file", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Register flags
 | ||||||
|  | 	registry.RegisterFlags(cmd) | ||||||
|  | 
 | ||||||
|  | 	// Transfer flags to options
 | ||||||
|  | 	registry.TransferFlags(cmd, options) | ||||||
|  | 
 | ||||||
|  | 	return &CommandTestHelper{ | ||||||
|  | 		Cmd:      cmd, | ||||||
|  | 		Registry: registry, | ||||||
|  | 		Options:  options, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestApplyCmd creates an apply command for testing and returns a helper with its components
 | ||||||
|  | func TestApplyCmd() *CommandTestHelper { | ||||||
|  | 	// Create command components
 | ||||||
|  | 	optionsFactory := factory.NewApplyOptionsFactory() | ||||||
|  | 	options := optionsFactory.CreateOptions().(*config.ApplyOptions) | ||||||
|  | 	registry := optionsFactory.GetFlagRegistry() | ||||||
|  | 
 | ||||||
|  | 	// Create command manually
 | ||||||
|  | 	cmd := &cobra.Command{ | ||||||
|  | 		Use:   "apply", | ||||||
|  | 		Short: "Apply all resources from state file only when there are changes", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Register flags
 | ||||||
|  | 	registry.RegisterFlags(cmd) | ||||||
|  | 
 | ||||||
|  | 	return &CommandTestHelper{ | ||||||
|  | 		Cmd:      cmd, | ||||||
|  | 		Registry: registry, | ||||||
|  | 		Options:  options, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestTemplateCmd creates a template command for testing and returns a helper with its components
 | ||||||
|  | func TestTemplateCmd() *CommandTestHelper { | ||||||
|  | 	// Create command components
 | ||||||
|  | 	optionsFactory := factory.NewTemplateOptionsFactory() | ||||||
|  | 	options := optionsFactory.CreateOptions().(*config.TemplateOptions) | ||||||
|  | 	registry := optionsFactory.GetFlagRegistry() | ||||||
|  | 
 | ||||||
|  | 	// Create command manually
 | ||||||
|  | 	cmd := &cobra.Command{ | ||||||
|  | 		Use:   "template", | ||||||
|  | 		Short: "Template releases defined in state file", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Register flags
 | ||||||
|  | 	registry.RegisterFlags(cmd) | ||||||
|  | 
 | ||||||
|  | 	return &CommandTestHelper{ | ||||||
|  | 		Cmd:      cmd, | ||||||
|  | 		Registry: registry, | ||||||
|  | 		Options:  options, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestSyncCmd creates a sync command for testing and returns a helper with its components
 | ||||||
|  | func TestSyncCmd() *CommandTestHelper { | ||||||
|  | 	// Create command components
 | ||||||
|  | 	optionsFactory := factory.NewSyncOptionsFactory() | ||||||
|  | 	options := optionsFactory.CreateOptions().(*config.SyncOptions) | ||||||
|  | 	registry := optionsFactory.GetFlagRegistry() | ||||||
|  | 
 | ||||||
|  | 	// Create command manually
 | ||||||
|  | 	cmd := &cobra.Command{ | ||||||
|  | 		Use:   "sync", | ||||||
|  | 		Short: "Sync all resources from state file", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Register flags
 | ||||||
|  | 	registry.RegisterFlags(cmd) | ||||||
|  | 
 | ||||||
|  | 	return &CommandTestHelper{ | ||||||
|  | 		Cmd:      cmd, | ||||||
|  | 		Registry: registry, | ||||||
|  | 		Options:  options, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | # Helmfile Test Utilities | ||||||
|  | 
 | ||||||
|  | This package provides testing utilities for the Helmfile project, making it easier to write unit tests for Helm-related functionality. | ||||||
|  | 
 | ||||||
|  | ## Overview | ||||||
|  | 
 | ||||||
|  | The `testutil` package contains: | ||||||
|  | 
 | ||||||
|  | 1. Mock implementations for Helm execution | ||||||
|  | 2. Utility functions for testing | ||||||
|  | 
 | ||||||
|  | ## Components | ||||||
|  | 
 | ||||||
|  | ### Mock Helm Executors | ||||||
|  | 
 | ||||||
|  | The package provides mock implementations of Helm executors that can be used in tests: | ||||||
|  | 
 | ||||||
|  | - `V3HelmExec`: A mock that can be configured to simulate Helm 3 behavior | ||||||
|  | - `VersionHelmExec`: A mock that can be configured with a specific Helm version | ||||||
|  | 
 | ||||||
|  | ```go | ||||||
|  | // Create a mock for Helm 3 | ||||||
|  | helmExec := testutil.NewV3HelmExec(true) | ||||||
|  | 
 | ||||||
|  | // Create a mock for a specific Helm version | ||||||
|  | versionExec := testutil.NewVersionHelmExec("3.8.0") | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | These mocks implement the Helm executor interface but will panic if any unexpected methods are called, making them useful for strict testing scenarios. | ||||||
|  | 
 | ||||||
|  | ### Utility Functions | ||||||
|  | 
 | ||||||
|  | #### CaptureStdout | ||||||
|  | 
 | ||||||
|  | Captures stdout output during the execution of a function: | ||||||
|  | 
 | ||||||
|  | ```go | ||||||
|  | output, err := testutil.CaptureStdout(func() { | ||||||
|  |     fmt.Println("Hello, world!") | ||||||
|  | }) | ||||||
|  | // output will contain "Hello, world!\n" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This is useful for testing functions that write to stdout. | ||||||
|  | 
 | ||||||
|  | ## Usage Examples | ||||||
|  | 
 | ||||||
|  | ### Testing with V3HelmExec | ||||||
|  | 
 | ||||||
|  | ```go | ||||||
|  | func TestMyFunction(t *testing.T) { | ||||||
|  |     // Create a mock Helm executor configured as Helm 3 | ||||||
|  |     helmExec := testutil.NewV3HelmExec(true) | ||||||
|  | 
 | ||||||
|  |     // Use in your test | ||||||
|  |     result := myFunctionThatChecksHelmVersion(helmExec) | ||||||
|  | 
 | ||||||
|  |     // Assert that the result is as expected for Helm 3 | ||||||
|  |     assert.True(t, result) | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Testing with VersionHelmExec | ||||||
|  | 
 | ||||||
|  | ```go | ||||||
|  | func TestVersionCompatibility(t *testing.T) { | ||||||
|  |     // Create a mock with specific version | ||||||
|  |     helmExec := testutil.NewVersionHelmExec("3.7.1") | ||||||
|  | 
 | ||||||
|  |     // Test version comparison | ||||||
|  |     assert.True(t, helmExec.IsVersionAtLeast("3.7.0")) | ||||||
|  |     assert.False(t, helmExec.IsVersionAtLeast("3.8.0")) | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Capturing Output | ||||||
|  | 
 | ||||||
|  | ```go | ||||||
|  | func TestOutputFunction(t *testing.T) { | ||||||
|  |     output, err := testutil.CaptureStdout(func() { | ||||||
|  |         MyFunctionThatPrintsOutput() | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     assert.NoError(t, err) | ||||||
|  |     assert.Contains(t, output, "Expected output") | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Contributing | ||||||
|  | 
 | ||||||
|  | When adding new test utilities, please ensure they are well-documented and include appropriate tests. | ||||||
		Loading…
	
		Reference in New Issue