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)