pi-agent: Implement contextual Target actions
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
"proxyctl/internal/app"
|
||||
"proxyctl/internal/target"
|
||||
)
|
||||
|
||||
func completeTargetSnapshot(state target.State, primary app.TargetAction) app.Snapshot {
|
||||
return app.Snapshot{ActiveProfile: "office", Targets: []app.TargetSnapshot{{
|
||||
ID: "shell", Description: target.Description{Name: "Shell"}, Available: true, State: state, PrimaryAction: primary,
|
||||
}}}
|
||||
}
|
||||
|
||||
func TestCompleteModelRendersAndDispatchesContextualPrimaryActions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
state target.State
|
||||
action app.TargetActionKind
|
||||
intent app.IntentKind
|
||||
label string
|
||||
}{
|
||||
{name: "Disabled", state: target.Disabled, action: app.EnableTargetAction, intent: app.EnableTarget, label: "Enable"},
|
||||
{name: "Enabled", state: target.Enabled, action: app.DisableTargetAction, intent: app.DisableTarget, label: "Disable"},
|
||||
{name: "External", state: target.External, action: app.ApplyProfileTargetAction, intent: app.ApplyProfileToTarget, label: "Apply Profile"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
initial := completeTargetSnapshot(test.state, app.TargetAction{Kind: test.action, Available: true})
|
||||
module := &targetModule{snapshot: initial, result: app.Result{Snapshot: initial, Notice: "done"}}
|
||||
model := resizedModel(t, module, themeModeNoColor, 120, 36)
|
||||
view := model.View()
|
||||
if !strings.Contains(view, "Action: "+test.label) || !strings.Contains(view, "Space: "+test.label) {
|
||||
t.Fatalf("contextual action not rendered:\n%s", view)
|
||||
}
|
||||
if test.state == target.External && (!strings.Contains(view, "proxyctl will begin managing this configuration") || !strings.Contains(view, "while preserving restorable state")) {
|
||||
t.Fatalf("Apply Profile ownership explanation missing:\n%s", view)
|
||||
}
|
||||
|
||||
updated, command := model.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||
model = updated.(Model)
|
||||
if command == nil || len(module.intents) != 1 || module.intents[0].Kind != test.intent {
|
||||
t.Fatalf("intent = %#v, command=%v", module.intents, command != nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteModelOffersBothExplicitDriftResolutionIntents(t *testing.T) {
|
||||
initial := completeTargetSnapshot(target.Drifted, app.TargetAction{Kind: app.ResolveDriftTargetAction, Available: true})
|
||||
initial.Targets[0].DriftResolutionActions = []app.TargetAction{
|
||||
{Kind: app.ReapplyActiveProfileAction, Available: true},
|
||||
{Kind: app.RestoreOriginalAction, Available: true},
|
||||
}
|
||||
for _, test := range []struct {
|
||||
key string
|
||||
intent app.IntentKind
|
||||
}{
|
||||
{key: "r", intent: app.ReapplyActiveProfile},
|
||||
{key: "o", intent: app.RestoreOriginal},
|
||||
} {
|
||||
t.Run(test.key, func(t *testing.T) {
|
||||
module := &targetModule{snapshot: initial, result: app.Result{Snapshot: initial, Notice: "done"}}
|
||||
model := resizedModel(t, module, themeModeNoColor, 120, 36)
|
||||
model = updateKey(model, " ")
|
||||
view := model.View()
|
||||
for _, want := range []string{"Drift Resolution", "Reapply Active Profile", "Restore Original"} {
|
||||
if !strings.Contains(view, want) {
|
||||
t.Fatalf("missing %q:\n%s", want, view)
|
||||
}
|
||||
}
|
||||
updated, command := model.Update(keyMessage(test.key))
|
||||
model = updated.(Model)
|
||||
if command == nil || len(module.intents) != 1 || module.intents[0].Kind != test.intent {
|
||||
t.Fatalf("resolution intent = %#v, command=%v", module.intents, command != nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteModelUsesStructuredDisabledReasonWithoutInferringFromState(t *testing.T) {
|
||||
initial := completeTargetSnapshot(target.Disabled, app.TargetAction{
|
||||
Kind: app.EnableTargetAction, DisabledReason: "No Active Profile is selected; create or select one before enabling",
|
||||
})
|
||||
module := &targetModule{snapshot: initial}
|
||||
model := resizedModel(t, module, themeModeNoColor, 120, 36)
|
||||
view := model.View()
|
||||
if !strings.Contains(view, "No Active Profile is selected") || !strings.Contains(view, "Space: Enable disabled") {
|
||||
t.Fatalf("structured disabled reason missing:\n%s", view)
|
||||
}
|
||||
updated, command := model.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||
model = updated.(Model)
|
||||
if command != nil || len(module.intents) != 0 || !strings.Contains(model.View(), "No Active Profile is selected") {
|
||||
t.Fatalf("disabled action mutated: intents=%#v\n%s", module.intents, model.View())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteModelRestoreOwnershipLossFailsTruthfully(t *testing.T) {
|
||||
initial := completeTargetSnapshot(target.Drifted, app.TargetAction{Kind: app.ResolveDriftTargetAction, Available: true})
|
||||
initial.Targets[0].DriftResolutionActions = []app.TargetAction{
|
||||
{Kind: app.ReapplyActiveProfileAction, Available: true},
|
||||
{Kind: app.RestoreOriginalAction, Available: true},
|
||||
}
|
||||
module := &targetModule{snapshot: initial, result: app.Result{Snapshot: initial, Err: errors.New("restoration evidence is insufficient; external changes were not overwritten")}}
|
||||
model := resizedModel(t, module, themeModeNoColor, 120, 36)
|
||||
model = updateKey(model, " ")
|
||||
updated, command := model.Update(keyMessage("o"))
|
||||
model = updated.(Model)
|
||||
if command == nil {
|
||||
t.Fatal("Restore Original did not start")
|
||||
}
|
||||
updated, _ = model.Update(command())
|
||||
model = updated.(Model)
|
||||
if len(module.intents) != 1 || module.intents[0].Kind != app.RestoreOriginal || !strings.Contains(model.View(), "Current error") || !strings.Contains(model.View(), "Drifted") {
|
||||
t.Fatalf("ownership-loss result: intents=%#v\n%s", module.intents, model.View())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteModelCancelsDriftResolutionWithoutMutation(t *testing.T) {
|
||||
initial := completeTargetSnapshot(target.Drifted, app.TargetAction{Kind: app.ResolveDriftTargetAction, Available: true})
|
||||
initial.Targets[0].DriftResolutionActions = []app.TargetAction{{Kind: app.ReapplyActiveProfileAction, Available: true}, {Kind: app.RestoreOriginalAction, Available: true}}
|
||||
module := &targetModule{snapshot: initial}
|
||||
model := resizedModel(t, module, themeModeNoColor, 120, 36)
|
||||
model = updateKey(model, " ")
|
||||
updated, command := model.Update(tea.KeyMsg{Type: tea.KeyEnter})
|
||||
model = updated.(Model)
|
||||
if command != nil || len(module.intents) != 0 || strings.Contains(model.View(), "r: Reapply Active Profile") {
|
||||
t.Fatalf("safe-default cancellation mutated: intents=%#v\n%s", module.intents, model.View())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user