feat: coordinate Active Profile target panel
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
"proxyctl/internal/app"
|
||||
"proxyctl/internal/profile"
|
||||
"proxyctl/internal/target"
|
||||
)
|
||||
|
||||
@@ -32,7 +33,7 @@ func TestTargetPanelShowsOnlyAvailableTargetsAndAppliesAsynchronously(t *testing
|
||||
module.result = app.Result{Snapshot: app.Snapshot{ActiveProfile: "office", Targets: []app.TargetSnapshot{{ID: "shell", Description: target.Description{Name: "Shell", Summary: "Exports for Bash/Zsh"}, Available: true, State: target.Enabled, Evidence: "matches"}}}, Notice: "Shell Target is now Enabled"}
|
||||
model := NewModel(module)
|
||||
view := model.View()
|
||||
if !strings.Contains(view, "Shell") || strings.Contains(view, "] APT") || !strings.Contains(view, "Unavailable APT") || !strings.Contains(view, "Exports for Bash/Zsh") {
|
||||
if !strings.Contains(view, "Shell") || strings.Contains(view, "APT") || !strings.Contains(view, "Exports for Bash/Zsh") {
|
||||
t.Fatalf("Target panel:\n%s", view)
|
||||
}
|
||||
|
||||
@@ -85,13 +86,102 @@ func TestTargetRestartConfirmationCancelsWithoutConfirmedMutation(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnavailableShellIsOutsideMainListWithDetectionExplanation(t *testing.T) {
|
||||
func TestHelpListsEveryTargetAndMainListsOnlyAvailableTargets(t *testing.T) {
|
||||
module := &targetModule{snapshot: app.Snapshot{Targets: []app.TargetSnapshot{
|
||||
{ID: "shell", Description: target.Description{Name: "Shell"}, Available: false, AvailabilityEvidence: "Shell Target requires Bash or Zsh; neither was detected"},
|
||||
{ID: "shell", Description: target.Description{Name: "Shell", Summary: "summary", Details: "affects future Shell commands; does not affect daemons; owns a generated fragment; user permission; reload side effect"}, Available: true, State: target.Disabled, Evidence: "integration installed"},
|
||||
{ID: "apt", Description: target.Description{Name: "APT", Summary: "persistent package proxy", Details: "owns an overlay and requires system write permission"}, Available: false, AvailabilityEvidence: "Debian APT tool apt-config was not detected"},
|
||||
}}}
|
||||
view := NewModel(module).View()
|
||||
if strings.Contains(view, "[Disabled ] Shell") || !strings.Contains(view, "Unavailable Shell") || !strings.Contains(view, "requires Bash or Zsh") {
|
||||
t.Fatalf("unavailable Shell explanation:\n%s", view)
|
||||
model := NewModel(module)
|
||||
view := model.View()
|
||||
if strings.Contains(view, "APT") || !strings.Contains(view, "affects future Shell commands") || !strings.Contains(view, "integration installed") {
|
||||
t.Fatalf("main Target panel:\n%s", view)
|
||||
}
|
||||
model = updateKey(model, "?")
|
||||
view = model.View()
|
||||
if !strings.Contains(view, "Shell") || !strings.Contains(view, "APT") || !strings.Contains(view, "Not detected: Debian APT tool apt-config was not detected") || !strings.Contains(view, "owns an overlay") {
|
||||
t.Fatalf("Target help:\n%s", view)
|
||||
}
|
||||
model = updateKey(model, "esc")
|
||||
if !strings.Contains(model.View(), "Target panel") {
|
||||
t.Fatalf("Escape did not return from help:\n%s", model.View())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshKeyDispatchesDetectionRefresh(t *testing.T) {
|
||||
module := &targetModule{snapshot: app.Snapshot{}, result: app.Result{Snapshot: app.Snapshot{Targets: []app.TargetSnapshot{{ID: "apt", Description: target.Description{Name: "APT"}, Available: true, State: target.External}}}}}
|
||||
model := NewModel(module)
|
||||
model = updateKey(model, "r")
|
||||
if len(module.intents) != 1 || module.intents[0].Kind != app.RefreshTargets || !strings.Contains(model.View(), "External") {
|
||||
t.Fatalf("refresh = intents %#v\n%s", module.intents, model.View())
|
||||
}
|
||||
}
|
||||
|
||||
type progressiveTargetModule struct {
|
||||
targetModule
|
||||
updates chan app.Result
|
||||
}
|
||||
|
||||
func (m *progressiveTargetModule) DispatchProgress(intent app.Intent) <-chan app.Result {
|
||||
m.intents = append(m.intents, intent)
|
||||
return m.updates
|
||||
}
|
||||
|
||||
func TestActiveProfileTransitionRendersPerTargetProgress(t *testing.T) {
|
||||
initial := app.Snapshot{ActiveProfile: "old", Profiles: []profile.Profile{{Name: "new"}}, Targets: []app.TargetSnapshot{{ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: target.Enabled}}}
|
||||
updates := make(chan app.Result, 3)
|
||||
updates <- app.Result{Snapshot: app.Snapshot{ActiveProfile: "new", Targets: initial.Targets}, Progress: []app.TargetProgress{{TargetID: "shell", Applying: true}}}
|
||||
updates <- app.Result{Snapshot: app.Snapshot{ActiveProfile: "new", Targets: []app.TargetSnapshot{{ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: target.Enabled}}}, Progress: []app.TargetProgress{{TargetID: "shell", State: target.Enabled}}, Notice: "Shell Target update completed"}
|
||||
updates <- app.Result{Snapshot: app.Snapshot{ActiveProfile: "new", Targets: []app.TargetSnapshot{{ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: target.Enabled}}}, Complete: true, Notice: "Activated Profile new"}
|
||||
close(updates)
|
||||
module := &progressiveTargetModule{targetModule: targetModule{snapshot: initial}, updates: updates}
|
||||
model := updateKey(NewModel(module), "p")
|
||||
updated, command := model.Update(tea.KeyMsg{Type: tea.KeyEnter})
|
||||
model = updated.(Model)
|
||||
if command == nil {
|
||||
t.Fatal("activation did not start progressive operation")
|
||||
}
|
||||
updated, command = model.Update(command())
|
||||
model = updated.(Model)
|
||||
if command == nil || !strings.Contains(model.View(), "Applying") || !strings.Contains(model.View(), "Active Profile: new") {
|
||||
t.Fatalf("applying progress:\n%s", model.View())
|
||||
}
|
||||
updated, command = model.Update(command())
|
||||
model = updated.(Model)
|
||||
if command == nil || !strings.Contains(model.View(), "Enabled") || !strings.Contains(model.View(), "update completed") {
|
||||
t.Fatalf("Target completion:\n%s", model.View())
|
||||
}
|
||||
updated, _ = model.Update(command())
|
||||
model = updated.(Model)
|
||||
if !strings.Contains(model.View(), "Activated Profile new") {
|
||||
t.Fatalf("final transition:\n%s", model.View())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetPanelRendersEveryTargetState(t *testing.T) {
|
||||
states := []target.State{target.Enabled, target.Disabled, target.External, target.Drifted}
|
||||
var targets []app.TargetSnapshot
|
||||
for _, state := range states {
|
||||
targets = append(targets, app.TargetSnapshot{ID: string(state), Description: target.Description{Name: string(state)}, Available: true, State: state})
|
||||
}
|
||||
view := NewModel(&targetModule{snapshot: app.Snapshot{Targets: targets}}).View()
|
||||
for _, state := range states {
|
||||
if !strings.Contains(view, string(state)) {
|
||||
t.Fatalf("missing state %s:\n%s", state, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriftedTargetWithoutActiveProfileTogglesTowardDisabled(t *testing.T) {
|
||||
initial := app.Snapshot{Targets: []app.TargetSnapshot{{ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: target.Drifted}}}
|
||||
module := &targetModule{snapshot: initial, result: app.Result{Snapshot: app.Snapshot{Targets: []app.TargetSnapshot{{ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: target.Disabled}}}}}
|
||||
model := NewModel(module)
|
||||
_, command := model.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||
if command == nil {
|
||||
t.Fatal("Drifted Target did not start disable")
|
||||
}
|
||||
_ = command()
|
||||
if len(module.intents) != 1 || module.intents[0].Enabled {
|
||||
t.Fatalf("Drifted Target intent = %#v", module.intents)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user