fix: harden operations and configuration recovery

This commit is contained in:
proxyctl coordinator
2026-08-30 11:13:45 +08:00
parent e34bf7d989
commit 8928986830
27 changed files with 542 additions and 125 deletions
+53 -1
View File
@@ -83,6 +83,45 @@ func (t *coordinatedOperationTarget) SetDesired(_ context.Context, enabled bool,
return target.OperationResult{Inspection: target.Inspection{State: t.state, Evidence: "completed"}}
}
type cancellableOperationTarget struct{ started chan struct{} }
func (t *cancellableOperationTarget) ID() string { return "cancellable" }
func (t *cancellableOperationTarget) Description() target.Description {
return target.Description{Name: "Cancellable"}
}
func (t *cancellableOperationTarget) Availability(context.Context) target.Availability {
return target.Availability{Available: true}
}
func (t *cancellableOperationTarget) Inspect(context.Context, *profile.Profile) target.Inspection {
return target.Inspection{State: target.Disabled, Evidence: "not changed"}
}
func (t *cancellableOperationTarget) Confirmation(bool) string { return "" }
func (t *cancellableOperationTarget) SetDesired(ctx context.Context, _ bool, _ *profile.Profile) target.OperationResult {
close(t.started)
<-ctx.Done()
return target.OperationResult{Inspection: target.Inspection{State: target.Disabled}, Err: ctx.Err()}
}
func TestInteractiveOperationCancellationReachesTarget(t *testing.T) {
adapter := &cancellableOperationTarget{started: make(chan struct{})}
application, err := app.NewWithTargets(profile.NewFileStore(filepath.Join(t.TempDir(), "profiles.json")), []target.Target{adapter})
if err != nil {
t.Fatal(err)
}
createAndActivate(t, application)
refreshApplicationTargets(t, application)
operation := application.StartOperation(app.Intent{Kind: app.EnableTarget, TargetID: adapter.ID()})
<-adapter.started
operation.Cancel()
var final app.OperationUpdate
for update := range operation.Updates {
final = update
}
if !final.IsFailed() || !errors.Is(final.Err(), context.Canceled) {
t.Fatalf("cancelled operation final update = %#v", final)
}
}
func TestInteractiveOperationStreamSerializesMutationAndPublishesStructuredProgress(t *testing.T) {
adapter := &coordinatedOperationTarget{id: "shell", state: target.Disabled, started: make(chan struct{}), release: make(chan struct{})}
application, err := app.NewWithTargets(profile.NewFileStore(filepath.Join(t.TempDir(), "profiles.json")), []target.Target{adapter})
@@ -224,9 +263,22 @@ func TestInteractiveOperationStreamUsesStructuredConfirmation(t *testing.T) {
operation := application.StartOperation(app.Intent{Kind: app.EnableTarget, TargetID: adapter.ID()})
update := <-operation.Updates
confirmation := update.Confirmation()
if !update.IsConfirmation() || confirmation == nil || confirmation.Title == "" || confirmation.CancelLabel == "" {
if !update.IsConfirmation() || confirmation == nil || confirmation.Title == "" || confirmation.CancelLabel == "" || confirmation.Token == "" {
t.Fatalf("confirmation update = %#v", update)
}
// Retry immediately after the terminal confirmation, before observing the
// first stream close. The Application gate must already be released.
retry := application.StartOperation(app.Intent{Kind: app.EnableTarget, TargetID: adapter.ID(), Confirm: true, ConfirmationToken: confirmation.Token})
var final app.OperationUpdate
for next := range retry.Updates {
if next.Decode().Kind == app.OperationClosed {
t.Fatalf("immediate confirmed retry observed stale operation gate: %#v", next)
}
final = next
}
if !final.IsSucceeded() {
t.Fatalf("confirmed retry final update = %#v", final)
}
if _, open := <-operation.Updates; open {
t.Fatal("confirmation operation stream did not close")
}