package tui import ( "fmt" "strings" "testing" "proxyctl/internal/app" "proxyctl/internal/target" ) func TestCompleteModelWideOperationsConsoleShowsStableAvailableTargetDashboard(t *testing.T) { module := &recordingModule{snapshot: app.Snapshot{ ActiveProfile: "office", Targets: []app.TargetSnapshot{ {ID: "shell", Description: target.Description{Name: "Shell", Summary: "Shell summary", Details: "Shell details"}, Available: true, State: target.Enabled, Evidence: "matches Active Profile"}, {ID: "apt", Description: target.Description{Name: "APT"}, Available: false, AvailabilityEvidence: "apt-config was not detected"}, {ID: "docker-client", Description: target.Description{Name: "Docker client"}, Available: true, State: target.External, Evidence: "existing settings"}, {ID: "gnome", Description: target.Description{Name: "GNOME"}, Available: true, State: target.Disabled, Evidence: "proxy mode is none"}, {ID: "docker-daemon", Description: target.Description{Name: "Docker daemon"}, Available: true, State: target.Drifted, Evidence: "managed values differ"}, }, }} view := resizedModel(t, module, themeModeNoColor, 120, 36).View() assertUsableFrame(t, view, 120, 36, "proxyctl", "Profile: office", "> ● Enabled", "Shell", "matches Active Profile", ) if strings.Contains(view, "APT") || strings.Contains(view, "apt-config was not detected") { t.Fatalf("default dashboard included an unavailable Target:\n%s", view) } if !sameLine(view, "Targets", "Target details") { t.Fatalf("wide dashboard did not compose list and details side by side:\n%s", view) } if !(strings.Index(view, "Shell") < strings.Index(view, "Docker client") && strings.Index(view, "Docker client") < strings.Index(view, "GNOME") && strings.Index(view, "GNOME") < strings.Index(view, "Docker daemon")) { t.Fatalf("available Target product order changed:\n%s", view) } } func TestCompleteModelFailedProgressSupplementsTruthfulTargetState(t *testing.T) { for _, state := range []target.State{target.Enabled, target.Disabled, target.External, target.Drifted} { t.Run(string(state), func(t *testing.T) { module := &recordingModule{snapshot: app.Snapshot{Targets: []app.TargetSnapshot{{ ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: state, }}}} model := resizedModel(t, module, themeModeNoColor, 120, 36) model.operation.progress["shell"] = app.TargetProgressFailed view := model.View() wantState := targetStateLabel(state) if !strings.Contains(view, "> "+wantState+" ! Failed Shell") { t.Fatalf("failed progress replaced truthful %s state instead of supplementing it:\n%s", state, view) } }) } } func TestCompleteModelDashboardRevealsUnavailableEvidenceAndStructuredDetails(t *testing.T) { module := &recordingModule{snapshot: app.Snapshot{Targets: []app.TargetSnapshot{ {ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: target.Disabled, Evidence: "fragment absent"}, {ID: "apt", Description: target.Description{ Name: "APT", Summary: "Persistent package proxy.", Details: "Longer APT explanation.", Scope: "package operations across privilege boundaries", Permissions: "pre-existing root write authority", Ownership: "isolated overlay; disabling reveals the previous configuration", RestartRisk: "none", }, Available: false, AvailabilityEvidence: "apt-config was not detected"}, {ID: "docker", Description: target.Description{Name: "Docker daemon"}, Available: true, State: target.Enabled, Evidence: "managed values match"}, }}} model := resizedModel(t, module, themeModeNoColor, 120, 36) model = updateKey(model, "u") model = updateKey(model, "down") view := model.View() assertUsableFrame(t, view, 120, 36, "> ○ Unavailable APT", "○ Unavailable", "apt-config was not detected", "Persistent package proxy.", "u: Hide unavailable", ) if !(strings.Index(view, "Shell") < strings.Index(view, "APT") && strings.Index(view, "APT") < strings.Index(view, "Docker daemon")) { t.Fatalf("revealing unavailable Targets changed product order:\n%s", view) } if strings.Contains(view, "Space: toggle") { t.Fatalf("unavailable Target exposed a mutation action:\n%s", view) } } func TestCompleteModelNarrowDashboardFocusAndIndependentScrollingRetainTargetIdentity(t *testing.T) { targets := make([]app.TargetSnapshot, 28) for i := range targets { targets[i] = app.TargetSnapshot{ ID: fmt.Sprintf("target-%02d", i), Description: target.Description{Name: fmt.Sprintf("Product Target %02d", i)}, Available: true, State: target.Disabled, Evidence: fmt.Sprintf("evidence-%02d", i), } } targets[22].Description.Summary = "Selected identity summary" targets[22].Evidence = strings.Repeat("Scrollable evidence line. ", 45) + "END OF TARGET EVIDENCE" module := &recordingModule{snapshot: app.Snapshot{ActiveProfile: "office", Targets: targets}} model := resizedModel(t, module, themeModeNoColor, 80, 24) for range 22 { model = updateKey(model, "down") } view := model.View() assertUsableFrame(t, view, 80, 24, "> ○ Disabled Product Target 22", "▶ Targets [focused]") model = updateKey(model, "tab") for range 70 { model = updateKey(model, "down") } view = model.View() assertUsableFrame(t, view, 80, 24, "> ○ Disabled Product Target 22", "▶ Target details [focused]", "END OF", "TARGET EVIDENCE", "↑/↓: Scroll", "Tab: Targets", ) model = resize(model, 120, 36) view = model.View() assertUsableFrame(t, view, 120, 36, "> ○ Disabled Product Target 22", "END OF TARGET", "EVIDENCE", "▶ Target details [focused]") if !sameLine(view, "Targets", "Target details") { t.Fatalf("resized dashboard did not become side by side:\n%s", view) } model = resize(model, 80, 24) assertUsableFrame(t, model.View(), 80, 24, "> ○ Disabled Product Target 22", "END OF", "TARGET EVIDENCE", "▶ Target details [focused]") }