fix: finish progressive Target detection

This commit is contained in:
proxyctl coordinator
2026-07-17 21:25:43 +08:00
parent ad651402fd
commit 8348fa3ac8
17 changed files with 672 additions and 221 deletions
+31 -17
View File
@@ -8,6 +8,7 @@ import (
tea "github.com/charmbracelet/bubbletea"
"proxyctl/internal/app"
"proxyctl/internal/profile"
"proxyctl/internal/target"
)
@@ -17,7 +18,9 @@ type progressiveDetectionModule struct {
begins int
}
func (m *progressiveDetectionModule) Snapshot() app.Snapshot { return m.snapshot }
func (m *progressiveDetectionModule) Snapshot() app.Snapshot {
return cloneDetectionSnapshot(m.snapshot)
}
func (m *progressiveDetectionModule) Dispatch(app.Intent) app.Result {
return app.Result{Snapshot: m.snapshot}
}
@@ -27,7 +30,7 @@ func (m *progressiveDetectionModule) BeginTargetDetection() (app.Snapshot, <-cha
m.snapshot.Targets[i].DetectionPhase = app.TargetChecking
m.snapshot.Targets[i].DetectionErr = nil
}
return m.snapshot, m.streams[m.begins-1]
return cloneDetectionSnapshot(m.snapshot), m.streams[m.begins-1]
}
func TestCompleteModelStreamsStartupAndRefreshByTargetWithoutMovingSelection(t *testing.T) {
@@ -58,7 +61,7 @@ func TestCompleteModelStreamsStartupAndRefreshByTargetWithoutMovingSelection(t *
t.Fatal("startup did not wait for progressive detection")
}
startup <- detectionUpdate(module.snapshot, "docker", app.TargetReady, true, target.Enabled, "docker enabled", nil)
startup <- module.detectionUpdate("docker", app.TargetReady, true, target.Enabled, "docker enabled", nil)
model, command = applyDetectionCommand(t, model, command)
view := model.View()
assertOrder(t, view, "Shell", "APT", "Docker")
@@ -68,21 +71,18 @@ func TestCompleteModelStreamsStartupAndRefreshByTargetWithoutMovingSelection(t *
}
failure := errors.New("apt-config inspection failed")
startup <- detectionUpdate(module.snapshot, "shell", app.TargetDetectionFailed, false, "", "", failure)
startup <- module.detectionUpdate("shell", app.TargetDetectionFailed, false, "", "", failure)
model, command = applyDetectionCommand(t, model, command)
assertUsableFrame(t, model.View(), 120, 36, "! Check failed Shell", "> … Checking APT", "● Enabled Docker")
startup <- detectionUpdate(module.snapshot, "apt", app.TargetReady, true, target.External, "external APT proxy", nil)
startup <- module.detectionUpdate("apt", app.TargetReady, false, target.Disabled, "apt-config is unavailable", nil)
model, command = applyDetectionCommand(t, model, command)
startup <- app.TargetDetectionUpdate{Complete: true}
startup <- app.TargetDetectionUpdate{Snapshot: cloneDetectionSnapshot(module.snapshot), Complete: true}
model, command = applyDetectionCommand(t, model, command)
if command != nil || model.selectedTargetID != "apt" {
t.Fatalf("startup completion command=%v selection=%q", command != nil, model.selectedTargetID)
}
assertUsableFrame(t, model.View(), 120, 36, "! Check failed Shell", "> ◇ External APT", "● Enabled Docker")
model = updateKey(model, "up")
assertUsableFrame(t, model.View(), 120, 36, "> ! Check failed Shell", "Check error: apt-config inspection failed")
model = updateKey(model, "down")
assertUsableFrame(t, model.View(), 120, 36, "! Check failed Shell", "> ○ Unavailable APT", "● Enabled Docker", "Evidence: apt-config is unavailable")
updated, command := model.Update(keyMessage("r"))
model = updated.(Model)
@@ -91,7 +91,7 @@ func TestCompleteModelStreamsStartupAndRefreshByTargetWithoutMovingSelection(t *
}
assertUsableFrame(t, model.View(), 120, 36, "… Checking Shell", "> … Checking APT", "… Checking Docker")
refresh <- detectionUpdate(module.snapshot, "apt", app.TargetReady, true, target.Enabled, "apt enabled", nil)
refresh <- module.detectionUpdate("apt", app.TargetReady, true, target.Enabled, "apt enabled", nil)
model, command = applyDetectionCommand(t, model, command)
assertUsableFrame(t, model.View(), 120, 36, "… Checking Shell", "> ● Enabled APT", "… Checking Docker")
if model.selectedTargetID != "apt" {
@@ -99,12 +99,26 @@ func TestCompleteModelStreamsStartupAndRefreshByTargetWithoutMovingSelection(t *
}
}
func detectionUpdate(snapshot app.Snapshot, id string, phase app.TargetDetectionPhase, available bool, state target.State, evidence string, err error) app.TargetDetectionUpdate {
return app.TargetDetectionUpdate{Result: app.TargetDetectionResult{
TargetID: id, Phase: phase,
Availability: target.Availability{Available: available, Evidence: evidence},
Inspection: target.Inspection{State: state, Evidence: evidence}, Err: err,
}}
func (m *progressiveDetectionModule) detectionUpdate(id string, phase app.TargetDetectionPhase, available bool, state target.State, evidence string, err error) app.TargetDetectionUpdate {
for i := range m.snapshot.Targets {
if m.snapshot.Targets[i].ID != id {
continue
}
m.snapshot.Targets[i].DetectionPhase = phase
m.snapshot.Targets[i].DetectionErr = err
m.snapshot.Targets[i].Available = available
m.snapshot.Targets[i].AvailabilityEvidence = evidence
m.snapshot.Targets[i].State = state
m.snapshot.Targets[i].Evidence = evidence
break
}
return app.TargetDetectionUpdate{Snapshot: cloneDetectionSnapshot(m.snapshot), TargetID: id}
}
func cloneDetectionSnapshot(snapshot app.Snapshot) app.Snapshot {
snapshot.Targets = append([]app.TargetSnapshot(nil), snapshot.Targets...)
snapshot.Profiles = append([]profile.Profile(nil), snapshot.Profiles...)
return snapshot
}
func applyDetectionCommand(t *testing.T, model Model, command tea.Cmd) (Model, tea.Cmd) {