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", "Active Profile: office", "● Enabled 1", "○ Disabled 1", "◇ External 1", "! Drifted 1", "> ● Enabled", "Shell", "State: ● Enabled", "Evidence: 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 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", "State: ○ Unavailable", "Evidence: apt-config was not detected", "Affects: package operations across privilege boundaries", "Permissions: pre-existing root write authority", "Ownership & restoration: isolated overlay; disabling reveals the", "previous configuration", "Restart risk: none", "Persistent package proxy.", "Longer APT explanation.", "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].Description.Details = strings.Repeat("Scrollable detail line. ", 45) + "END OF TARGET DETAILS" 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", "Selected identity summary", "▶ 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 DETAILS", "↑/k ↓/j PgUp/PgDn: scroll", "Tab: focus Targets", ) model = resize(model, 120, 36) view = model.View() assertUsableFrame(t, view, 120, 36, "> ○ Disabled Product Target 22", "END OF TARGET DETAILS", "▶ 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 DETAILS", "▶ Target details [focused]") }