From c4c36ce7b935a1b7fa519d84434c3deb6ab32227 Mon Sep 17 00:00:00 2001 From: Joshua Temple Date: Wed, 8 Jul 2026 13:17:28 -0400 Subject: [PATCH] test: make nil-guard branches terminate to satisfy staticcheck SA5011 The release-path golangci-lint (running on the go1.26.5 toolchain) flags staticcheck SA5011 (possible nil pointer dereference) at test sites that guard a pointer with 'if x == nil { t.Fatal(...) }' and then dereference it: staticcheck does not always treat t.Fatal as terminating, so it sees the nil branch as able to fall through. Add an explicit return as the last statement of each such guard so the nil path provably terminates. The return is unreachable at runtime because t.Fatal already exits the goroutine, so no test behavior changes; the edit is purely additive and only removes the lint ambiguity. Fixes the three flagged sites and every sibling with the same idiom so a later cut does not surface a new one. Signed-off-by: Joshua Temple --- e2e/harness/parser_test.go | 2 ++ e2e/harness/scenario_retry_test.go | 1 + internal/config/parse_test.go | 7 +++++++ internal/config/schema_v1_test.go | 1 + internal/config/schema_version_test.go | 2 ++ internal/config/validate_telemetry_test.go | 2 ++ internal/config/validate_versionoverrides_test.go | 3 +++ internal/hotfix/chain_test.go | 2 ++ internal/hotfix/finalize_test.go | 2 ++ internal/hotfix/plan_test.go | 4 ++++ internal/hotfix/pr_checker_test.go | 1 + internal/hotfix/selfheal_test.go | 7 +++++++ internal/orchestrate/nochange_skip_test.go | 2 ++ internal/promote/guards_test.go | 1 + internal/promote/promote_test.go | 2 ++ internal/promote/save_config_preserve_test.go | 1 + internal/release/command_test.go | 5 +++++ internal/rollback/command_subcommands_test.go | 5 +++++ internal/rollback/first_env_guard_test.go | 4 ++++ internal/rollback/ring_test.go | 2 ++ internal/visualize/theme_test.go | 2 ++ internal/visualize/visualize_test.go | 1 + 22 files changed, 59 insertions(+) diff --git a/e2e/harness/parser_test.go b/e2e/harness/parser_test.go index 91275707..61abadab 100644 --- a/e2e/harness/parser_test.go +++ b/e2e/harness/parser_test.go @@ -23,6 +23,7 @@ func TestParseActOutput(t *testing.T) { if result.Jobs["build-app"] == nil { t.Fatal("expected build-app job in result") + return } if result.Jobs["build-app"].Conclusion != "success" { t.Errorf("expected build-app conclusion=success, got %s", result.Jobs["build-app"].Conclusion) @@ -32,6 +33,7 @@ func TestParseActOutput(t *testing.T) { } if result.Jobs["deploy-cdk"] == nil { t.Fatal("expected deploy-cdk job in result") + return } if result.Jobs["deploy-cdk"].Conclusion != "skipped" { t.Errorf("expected deploy-cdk conclusion=skipped, got %s", result.Jobs["deploy-cdk"].Conclusion) diff --git a/e2e/harness/scenario_retry_test.go b/e2e/harness/scenario_retry_test.go index db46f4a2..7bd8b120 100644 --- a/e2e/harness/scenario_retry_test.go +++ b/e2e/harness/scenario_retry_test.go @@ -210,6 +210,7 @@ func TestRunScenarioWithRetry_PersistsEvidenceOnExhaustion(t *testing.T) { }) if err == nil { t.Fatal("expected an error after exhausting attempts") + return } // The error must reference a written artifact path. diff --git a/internal/config/parse_test.go b/internal/config/parse_test.go index 159ea84b..7172cfc2 100644 --- a/internal/config/parse_test.go +++ b/internal/config/parse_test.go @@ -61,6 +61,7 @@ func TestParse(t *testing.T) { // Verify validate config if cfg.Validate == nil { t.Fatal("Validate is nil") + return } if cfg.Validate.Workflow != ".github/workflows/validate.yaml" { t.Errorf("Validate.Workflow = %q, want %q", cfg.Validate.Workflow, ".github/workflows/validate.yaml") @@ -143,6 +144,7 @@ func TestParse_WithInputs(t *testing.T) { // Verify build inputs if cfg.Builds[0].Inputs == nil { t.Fatal("Builds[0].Inputs is nil") + return } if cfg.Builds[0].Inputs["dockerfile_path"] != "./src/Dockerfile" { t.Errorf("Builds[0].Inputs[dockerfile_path] = %v, want ./src/Dockerfile", cfg.Builds[0].Inputs["dockerfile_path"]) @@ -154,6 +156,7 @@ func TestParse_WithInputs(t *testing.T) { // Verify deploy inputs if cfg.Deploys[0].Inputs == nil { t.Fatal("Deploys[0].Inputs is nil") + return } if cfg.Deploys[0].Inputs["cluster_name"] != "my-cluster" { t.Errorf("Deploys[0].Inputs[cluster_name] = %v, want my-cluster", cfg.Deploys[0].Inputs["cluster_name"]) @@ -162,6 +165,7 @@ func TestParse_WithInputs(t *testing.T) { // Verify env_inputs if cfg.Deploys[0].EnvInputs == nil { t.Fatal("Deploys[0].EnvInputs is nil") + return } if cfg.Deploys[0].EnvInputs["dev"]["replicas"] != 1 { t.Errorf("Deploys[0].EnvInputs[dev][replicas] = %v, want 1", cfg.Deploys[0].EnvInputs["dev"]["replicas"]) @@ -499,6 +503,7 @@ func TestParse_ReleaseAndChangelogConfig(t *testing.T) { // Release config if cfg.Release == nil { t.Fatal("Release is nil") + return } if cfg.Release.Disabled { t.Error("Release.Disabled should be false (enabled by default)") @@ -510,6 +515,7 @@ func TestParse_ReleaseAndChangelogConfig(t *testing.T) { // Changelog config if cfg.Changelog == nil { t.Fatal("Changelog is nil") + return } if cfg.Changelog.Workflow != ".github/workflows/custom-changelog.yaml" { t.Errorf("Changelog.Workflow = %q, want .github/workflows/custom-changelog.yaml", cfg.Changelog.Workflow) @@ -544,6 +550,7 @@ func TestParse_ReleaseDisabled(t *testing.T) { if cfg.Release == nil { t.Fatal("Release is nil") + return } if !cfg.Release.Disabled { t.Error("Release.Disabled should be true") diff --git a/internal/config/schema_v1_test.go b/internal/config/schema_v1_test.go index 449a6461..db5e420b 100644 --- a/internal/config/schema_v1_test.go +++ b/internal/config/schema_v1_test.go @@ -973,6 +973,7 @@ components: `) if cfg.Components == nil { t.Fatal("Components map should be parsed") + return } if cfg.Components["api"].Path != "services/api" { t.Fatalf("unexpected path: %q", cfg.Components["api"].Path) diff --git a/internal/config/schema_version_test.go b/internal/config/schema_version_test.go index 938f37ca..ca0cc1df 100644 --- a/internal/config/schema_version_test.go +++ b/internal/config/schema_version_test.go @@ -97,6 +97,7 @@ func TestValidateSchemaVersionHelper(t *testing.T) { if tt.wantErr { if err == nil { t.Fatalf("expected error, got nil (warn=%q)", warn) + return } if tt.errContains != "" && !strings.Contains(err.Error(), tt.errContains) { t.Fatalf("error %q does not contain %q", err.Error(), tt.errContains) @@ -163,6 +164,7 @@ func TestValidateSchemaVersion(t *testing.T) { if tt.wantFatal { if fatalErr == nil { t.Fatalf("expected fatal error, got nil") + return } if tt.errContains != "" && !strings.Contains(fatalErr.Error(), tt.errContains) { t.Fatalf("error %q does not contain %q", fatalErr.Error(), tt.errContains) diff --git a/internal/config/validate_telemetry_test.go b/internal/config/validate_telemetry_test.go index 30d6247b..3ec619fa 100644 --- a/internal/config/validate_telemetry_test.go +++ b/internal/config/validate_telemetry_test.go @@ -130,12 +130,14 @@ telemetry: `) if cfg.Telemetry == nil { t.Fatalf("telemetry block did not parse") + return } if !cfg.Telemetry.Enabled || cfg.Telemetry.Adapter != TelemetryAdapterNone { t.Fatalf("telemetry enabled/adapter: %#v", cfg.Telemetry) } if cfg.Telemetry.Webhook == nil { t.Fatalf("telemetry.webhook did not parse") + return } if cfg.Telemetry.Webhook.URL != "https://metrics.example.com/ingest" { t.Fatalf("telemetry.webhook.url: %q", cfg.Telemetry.Webhook.URL) diff --git a/internal/config/validate_versionoverrides_test.go b/internal/config/validate_versionoverrides_test.go index 3c793051..6640b0a9 100644 --- a/internal/config/validate_versionoverrides_test.go +++ b/internal/config/validate_versionoverrides_test.go @@ -87,9 +87,11 @@ release: `) if cfg.Release == nil { t.Fatalf("release block did not parse") + return } if cfg.Release.VersionOverrides == nil { t.Fatalf("release.version_overrides did not parse") + return } if got := cfg.Release.VersionOverrides.Dir; got != ".cascade/version-overrides" { t.Fatalf("release.version_overrides.dir = %q", got) @@ -121,6 +123,7 @@ release: `) if cfg.Release == nil { t.Fatalf("release block did not parse") + return } if cfg.Release.VersionOverrides != nil { t.Fatalf("version_overrides should be nil when absent, got %#v", cfg.Release.VersionOverrides) diff --git a/internal/hotfix/chain_test.go b/internal/hotfix/chain_test.go index 0dd8c113..12b88e23 100644 --- a/internal/hotfix/chain_test.go +++ b/internal/hotfix/chain_test.go @@ -348,6 +348,7 @@ func TestPlanChain_RejectsNonTrunkCommit(t *testing.T) { _, err := p.PlanChain([]string{fix1, side}, "test") if err == nil { t.Fatal("expected error citing the non-trunk ref") + return } if !strings.Contains(err.Error(), "trunk") { t.Errorf("error %q should mention trunk", err.Error()) @@ -396,6 +397,7 @@ func TestPlanChain_RemoteEnvTipDiverged_FailsWithGuidance(t *testing.T) { _, err := p.PlanChain([]string{fix}, "test") if err == nil { t.Fatal("expected divergence error when remote env tip differs from recorded state SHA") + return } msg := err.Error() if !strings.Contains(msg, "env/test") { diff --git a/internal/hotfix/finalize_test.go b/internal/hotfix/finalize_test.go index 28eb7151..4eb9cb6e 100644 --- a/internal/hotfix/finalize_test.go +++ b/internal/hotfix/finalize_test.go @@ -451,6 +451,7 @@ func TestFinalize_MergeSHATipMismatch_Fails(t *testing.T) { err := f.Finalize("test", other, []string{fix}, base) if err == nil { t.Fatal("expected mismatch error when merge SHA is not env/test tip") + return } if !strings.Contains(strings.ToLower(err.Error()), "tip") { t.Errorf("error %q should mention the branch tip mismatch", err.Error()) @@ -773,6 +774,7 @@ func TestFinalize_TrunkStateAbsent_Errors(t *testing.T) { err := f.Finalize("test", merge, []string{fix}, base) if err == nil { t.Fatal("expected missing-state error when trunk has no recorded SHA for the target env") + return } if !strings.Contains(err.Error(), "no recorded state SHA") { t.Errorf("error %q should report the missing recorded state SHA", err.Error()) diff --git a/internal/hotfix/plan_test.go b/internal/hotfix/plan_test.go index 29e09f4b..4c14b45b 100644 --- a/internal/hotfix/plan_test.go +++ b/internal/hotfix/plan_test.go @@ -129,6 +129,7 @@ func TestPlan_RejectsNonTrunkCommit(t *testing.T) { _, err := p.Plan(side, "test") if err == nil { t.Fatal("expected error for non-trunk commit, got nil") + return } if !strings.Contains(err.Error(), "trunk") { t.Errorf("error %q should mention trunk", err.Error()) @@ -150,6 +151,7 @@ func TestPlan_RejectsFirstEnv(t *testing.T) { _, err := p.Plan(fix, "dev") if err == nil { t.Fatal("expected error for first env, got nil") + return } if !strings.Contains(err.Error(), "first environment") { t.Errorf("error %q should mention first environment", err.Error()) @@ -277,6 +279,7 @@ func TestPlan_ExistingBranchTipMismatch_FailsClosedWithoutRealChecker(t *testing _, err := p.Plan(fix, "test") if err == nil { t.Fatal("expected tip-mismatch error") + return } msg := err.Error() if !strings.Contains(msg, "abandoned hotfix branch") { @@ -325,6 +328,7 @@ func TestPlan_SingleFlight_OpenPRBlocks(t *testing.T) { _, err := p.Plan(fix, "test") if err == nil { t.Fatal("expected single-flight error") + return } if checker.calledWith != "env/test" { t.Errorf("PR checker queried %q, want env/test", checker.calledWith) diff --git a/internal/hotfix/pr_checker_test.go b/internal/hotfix/pr_checker_test.go index 8e23ba42..0856b7bd 100644 --- a/internal/hotfix/pr_checker_test.go +++ b/internal/hotfix/pr_checker_test.go @@ -184,6 +184,7 @@ func TestRestPRChecker_NonOKResponseReturnsError(t *testing.T) { _, err := c.OpenHotfixPRs("env/test") if err == nil { t.Fatal("expected an error for a non-200 response, got nil") + return } if !strings.Contains(err.Error(), "404") { t.Errorf("error %q should reference the HTTP status code", err.Error()) diff --git a/internal/hotfix/selfheal_test.go b/internal/hotfix/selfheal_test.go index aa34415d..1fb53503 100644 --- a/internal/hotfix/selfheal_test.go +++ b/internal/hotfix/selfheal_test.go @@ -118,6 +118,7 @@ func TestReconcileBranch_NotCheckedFailsClosedNoReset(t *testing.T) { created, reset, err := p.reconcileBranch("env/test", "basesha", false /*diverged*/, false /*singleFlightChecked*/) if err == nil { t.Fatal("expected fail-closed divergence error, got nil") + return } if created || reset { t.Errorf("created=%v reset=%v, both must be false when failing closed", created, reset) @@ -142,6 +143,7 @@ func TestReconcileBranch_DivergedFailsClosedNoReset(t *testing.T) { created, reset, err := p.reconcileBranch("env/test", "basesha", true /*diverged*/, true /*singleFlightChecked*/) if err == nil { t.Fatal("expected fail-closed divergence error, got nil") + return } if created || reset { t.Errorf("created=%v reset=%v, both must be false when failing closed", created, reset) @@ -179,6 +181,7 @@ func TestReconcileBranch_SelfHealReportsResetError(t *testing.T) { _, _, err := p.reconcileBranch("env/test", "basesha", false, true) if err == nil { t.Fatal("expected the ResetBranch failure to surface") + return } if !strings.Contains(err.Error(), "self-healing orphan") { t.Errorf("error %q should wrap the self-heal failure", err.Error()) @@ -226,6 +229,7 @@ func TestVerifyRemoteEnvTip_DivergedFailsClosedNoReset(t *testing.T) { reset, err := p.verifyRemoteEnvTip("env/test", "basesha", true /*diverged*/, true) if err == nil { t.Fatal("expected fail-closed divergence error, got nil") + return } if reset { t.Error("reset must be false for a diverged env") @@ -317,6 +321,7 @@ func TestPlanChain_OrphanRemoteTipNoopCheckerFailsClosed(t *testing.T) { _, err := p.PlanChain([]string{fix}, "test") if err == nil { t.Fatal("expected fail-closed divergence error without a real checker") + return } if len(rr.resets) != 0 { t.Fatalf("ResetBranch must NOT be called without a real single-flight check, got %d", len(rr.resets)) @@ -355,6 +360,7 @@ func TestPlanChain_AbortsOnOpenConflictPR(t *testing.T) { _, err := p.PlanChain([]string{fix}, "test") if err == nil { t.Fatal("expected single-flight abort when a conflict-resolution hotfix PR is open") + return } if !strings.Contains(err.Error(), "55") { t.Errorf("error %q should reference the open PR number", err.Error()) @@ -388,6 +394,7 @@ func TestPlanChain_AbortsOnOpenHotfixPR(t *testing.T) { _, err := p.PlanChain([]string{fix}, "test") if err == nil { t.Fatal("expected single-flight abort when a hotfix PR is open") + return } if !strings.Contains(err.Error(), "77") { t.Errorf("error %q should reference the open PR number", err.Error()) diff --git a/internal/orchestrate/nochange_skip_test.go b/internal/orchestrate/nochange_skip_test.go index 2a2c5b85..01589d22 100644 --- a/internal/orchestrate/nochange_skip_test.go +++ b/internal/orchestrate/nochange_skip_test.go @@ -225,10 +225,12 @@ func TestFinalize_RecordsPerBuildSHA(t *testing.T) { envState := orch.cicdFile.State["prerelease"] if envState == nil { t.Fatalf("prerelease state is nil after Finalize") + return } bs := envState.Builds["app"] if bs == nil { t.Fatalf("envState.Builds[app] is nil; expected a recorded build state") + return } if bs.SHA != headSHA { t.Errorf("envState.Builds[app].SHA = %q, want %q", bs.SHA, headSHA) diff --git a/internal/promote/guards_test.go b/internal/promote/guards_test.go index 609bde22..5417454d 100644 --- a/internal/promote/guards_test.go +++ b/internal/promote/guards_test.go @@ -144,6 +144,7 @@ func TestPromote_IntoDivergedEnv_MissingPatch_Blocked(t *testing.T) { _, err := pf.Run() if err == nil { t.Fatalf("expected preflight to block promotion into diverged env missing a patch") + return } if !containsAllOf(err.Error(), "patchsha1") { t.Errorf("error should name the missing patch, got: %q", err.Error()) diff --git a/internal/promote/promote_test.go b/internal/promote/promote_test.go index 87beb169..f8d3ab38 100644 --- a/internal/promote/promote_test.go +++ b/internal/promote/promote_test.go @@ -339,6 +339,7 @@ func TestDefaultPromotion_StripsRCSuffixOnPublishEnv(t *testing.T) { } if releasePromo == nil { t.Fatal("no promotion found for env 'release'") + return } if releasePromo.Version != "v1.0.0" { @@ -404,6 +405,7 @@ func TestCascadePromotion_StripsRCSuffixOnPublishEnv(t *testing.T) { } if releasePromo == nil { t.Fatal("no promotion found for env 'release'") + return } if releasePromo.Version != "v1.0.0" { diff --git a/internal/promote/save_config_preserve_test.go b/internal/promote/save_config_preserve_test.go index b6cef7bc..92ee8b3b 100644 --- a/internal/promote/save_config_preserve_test.go +++ b/internal/promote/save_config_preserve_test.go @@ -86,6 +86,7 @@ func TestSaveConfig_NonDryRunPromote_PreservesUnmodeledManifestKeys(t *testing.T testState := reparsed.State["test"] if testState == nil { t.Fatalf("expected state.test to be written, got nil; manifest:\n%s", out) + return } if testState.SHA != "abc123" { t.Errorf("state.test.sha = %q, want %q", testState.SHA, "abc123") diff --git a/internal/release/command_test.go b/internal/release/command_test.go index 80510348..2dcb04ca 100644 --- a/internal/release/command_test.go +++ b/internal/release/command_test.go @@ -15,6 +15,7 @@ func TestNewCommand_TagOnlyFlag(t *testing.T) { flag := cmd.Flags().Lookup("tag-only") if flag == nil { t.Fatal("expected --tag-only flag to be registered on manage-release") + return } if flag.DefValue != "false" { t.Errorf("--tag-only default = %q, want false", flag.DefValue) @@ -98,6 +99,7 @@ func TestValidateManageReleaseFlags_SHARequiredOnlyForTagCreatingActions(t *test if tt.wantErr { if err == nil { t.Fatalf("expected error for action %q with sha=%q, got nil", tt.action, tt.sha) + return } if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) { t.Fatalf("expected error containing %q, got %q", tt.errSubstr, err.Error()) @@ -132,6 +134,7 @@ func TestValidateManageReleaseFlags_OtherRequiredFields(t *testing.T) { err := validateManageReleaseFlags(ActionLock, tt.repo, tt.env, "", tt.tag) if err == nil { t.Fatalf("expected error, got nil") + return } if !strings.Contains(err.Error(), tt.errSubstr) { t.Fatalf("expected error containing %q, got %q", tt.errSubstr, err.Error()) @@ -166,6 +169,7 @@ func TestComponentReapOptions_ComponentRequiresConfig(t *testing.T) { _, err := componentReapOptions("", "ci", "api") if err == nil { t.Fatal("expected an error when --component is set without --config") + return } if !strings.Contains(err.Error(), "--config is required") { t.Fatalf("expected a --config-required error, got %q", err.Error()) @@ -205,6 +209,7 @@ func TestComponentReapOptions_ThreadsStrictComponentGrammar(t *testing.T) { mgr := NewManager("owner/repo", "tok", opts...) if mgr.grammar == nil { t.Fatal("expected a threaded component grammar, got nil") + return } if got := mgr.grammar.Prefix; got != "api-" { t.Errorf("grammar prefix = %q, want api-", got) diff --git a/internal/rollback/command_subcommands_test.go b/internal/rollback/command_subcommands_test.go index 638746e7..7cdb7075 100644 --- a/internal/rollback/command_subcommands_test.go +++ b/internal/rollback/command_subcommands_test.go @@ -239,6 +239,7 @@ func TestRollbackFinalize_SkippedDeployableNotCounted(t *testing.T) { prod := file.State["prod"] if prod == nil { t.Fatal("prod state missing after finalize") + return } // A deployable-scoped rollback re-applies the per-deployable SHA without // touching the env-level pointer. With "services" succeeding and "web-api" @@ -247,6 +248,7 @@ func TestRollbackFinalize_SkippedDeployableNotCounted(t *testing.T) { ds := prod.Deploys["services"] if ds == nil { t.Fatal("services deploy state missing after finalize") + return } if ds.SHA != "prodold1112223" { t.Errorf("services sha = %q, want prodold1112223", ds.SHA) @@ -281,6 +283,7 @@ func TestRollbackFinalize_AppliesWhenDeploySucceeded(t *testing.T) { prod := file.State["prod"] if prod == nil { t.Fatal("prod state missing after finalize") + return } if prod.SHA != "prodold1112223" { t.Errorf("env sha = %q, want prodold1112223", prod.SHA) @@ -319,6 +322,7 @@ func TestRollbackFinalize_AbortsWhenDeployFailed(t *testing.T) { err = cmd.Execute() if err == nil { t.Fatal("expected error when a deploy failed, got nil") + return } if !strings.Contains(err.Error(), "services") || !strings.Contains(err.Error(), "did not succeed") { t.Errorf("error %q should name the failed deploy and say it did not succeed", err.Error()) @@ -389,6 +393,7 @@ func TestRollbackFinalize_NoDeploysConfigured_StillApplies(t *testing.T) { prod := file.State["prod"] if prod == nil { t.Fatal("prod state missing after finalize") + return } if prod.SHA != "prodold1112223" { t.Errorf("env sha = %q, want prodold1112223", prod.SHA) diff --git a/internal/rollback/first_env_guard_test.go b/internal/rollback/first_env_guard_test.go index 03935727..579d4e43 100644 --- a/internal/rollback/first_env_guard_test.go +++ b/internal/rollback/first_env_guard_test.go @@ -21,6 +21,7 @@ func TestPlan_FirstEnvironment_NoTarget_Guarded(t *testing.T) { _, err := rb.Plan("dev", "", "") if err == nil { t.Fatalf("expected guard error rolling back the first environment, got nil") + return } if !strings.Contains(err.Error(), "first environment") { t.Errorf("error = %q, want it to name the first environment", err.Error()) @@ -43,6 +44,7 @@ func TestPlan_FirstEnvironment_WithTarget_Guarded(t *testing.T) { _, err := rb.Plan("dev", "devsha1234567", "") if err == nil { t.Fatalf("expected guard error rolling back the first environment with --to, got nil") + return } if !strings.Contains(err.Error(), "first environment") { t.Errorf("error = %q, want it to name the first environment", err.Error()) @@ -57,6 +59,7 @@ func TestPlan_FirstEnvironment_Deployable_Guarded(t *testing.T) { _, err := rb.Plan("dev", "", "services") if err == nil { t.Fatalf("expected guard error for a deployable-scoped first-env rollback, got nil") + return } if !strings.Contains(err.Error(), "first environment") { t.Errorf("error = %q, want it to name the first environment", err.Error()) @@ -89,6 +92,7 @@ func TestApply_FirstEnvironment_Guarded(t *testing.T) { err := rb.Apply(plan) if err == nil { t.Fatalf("expected Apply to refuse a first-environment rollback, got nil") + return } if !strings.Contains(err.Error(), "first environment") { t.Errorf("error = %q, want it to name the first environment", err.Error()) diff --git a/internal/rollback/ring_test.go b/internal/rollback/ring_test.go index 21397f77..eed306b0 100644 --- a/internal/rollback/ring_test.go +++ b/internal/rollback/ring_test.go @@ -16,6 +16,7 @@ func seedRing(t *testing.T, rb *Rollbacker, env string, ring []config.EnvStateSn st := rb.cicdFile.State[env] if st == nil { t.Fatalf("no live state for env %q to seed ring", env) + return } st.Previous = ring } @@ -185,6 +186,7 @@ func TestResolveTarget_UnresolvableReturnsError(t *testing.T) { _, err := rb.Plan("prod", "", "") if err == nil { t.Fatal("expected error when no prior version to roll back to, got nil") + return } if !strings.Contains(err.Error(), "no prior version to roll back to") { t.Errorf("error = %q, want it to mention no prior version to roll back to", err.Error()) diff --git a/internal/visualize/theme_test.go b/internal/visualize/theme_test.go index 6f620f58..872859a9 100644 --- a/internal/visualize/theme_test.go +++ b/internal/visualize/theme_test.go @@ -173,6 +173,7 @@ func TestLoadTheme_MissingFile_Errors(t *testing.T) { _, err := LoadTheme(filepath.Join(t.TempDir(), "absent.json")) if err == nil { t.Fatal("expected error for missing theme file, got nil") + return } if !strings.Contains(err.Error(), "absent.json") { t.Errorf("error should name the file, got: %v", err) @@ -199,6 +200,7 @@ func TestLoadTheme_Malformed_Errors(t *testing.T) { _, err := LoadTheme(noName) if err == nil { t.Fatal("expected error for theme missing name, got nil") + return } if !strings.Contains(err.Error(), "name") { t.Errorf("error should mention the missing name field, got: %v", err) diff --git a/internal/visualize/visualize_test.go b/internal/visualize/visualize_test.go index 20242008..8ba8ca20 100644 --- a/internal/visualize/visualize_test.go +++ b/internal/visualize/visualize_test.go @@ -188,6 +188,7 @@ func TestBuildViewModel_CyclicGraphErrors(t *testing.T) { _, err := BuildViewModel(g) if err == nil { t.Fatal("expected error for cyclic graph, got nil") + return } if !strings.Contains(err.Error(), "cycle") { t.Errorf("expected cycle error, got: %v", err)