승인된 execution preset을 Edge 조정 경계와 Node workspace/tool 실행 경계로 연결해 단일 요청 수명주기와 관측 계약을 일관되게 처리한다.
155 lines
5.9 KiB
Go
155 lines
5.9 KiB
Go
package configrefresh
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"iop/packages/go/config"
|
|
)
|
|
|
|
// makeTestConfig builds a minimal EdgeConfig with the given workspaces for
|
|
// classification testing. It is not a valid full config for LoadEdge; it is
|
|
// used only as in-memory current/candidate pairs for Classify.
|
|
func makeTestConfig(workspaces []config.WorkspaceDefinition) *config.EdgeConfig {
|
|
return &config.EdgeConfig{
|
|
Edge: config.EdgeInfo{ID: "edge-test", Name: "edge-test"},
|
|
Server: config.EdgeServerConf{Listen: "0.0.0.0:9090"},
|
|
Bootstrap: config.EdgeBootstrapConf{
|
|
Listen: "0.0.0.0:18080",
|
|
ArtifactDir: "artifacts",
|
|
},
|
|
OpenAI: config.EdgeOpenAIConf{Listen: "0.0.0.0:18081", Adapter: "ollama"},
|
|
Logging: config.LoggingConf{Level: "info"},
|
|
Metrics: config.MetricsConf{Port: 19092},
|
|
Refresh: config.EdgeRefreshConf{Enabled: false, Listen: "127.0.0.1:19093"},
|
|
LongContextThresholdTokens: 100000,
|
|
Nodes: []config.NodeDefinition{
|
|
{
|
|
ID: "node-ws-test",
|
|
Alias: "ws-test-node",
|
|
Token: "token-ws-test",
|
|
Providers: []config.NodeProviderConf{
|
|
{
|
|
ID: "prov-a",
|
|
Type: "ollama",
|
|
Category: config.CategoryLocalInference,
|
|
Models: []string{"model-a"},
|
|
Capacity: 2,
|
|
},
|
|
},
|
|
Workspaces: workspaces,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceRootChangeRequiresRestart(t *testing.T) {
|
|
current := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-root-change", Platform: "darwin", Root: "/Users/operator/projects/old", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}},
|
|
})
|
|
candidate := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-root-change", Platform: "darwin", Root: "/Users/operator/projects/new", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}},
|
|
})
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusRestartRequired {
|
|
t.Fatalf("expected restart_required for root change, got %s", result.Status)
|
|
}
|
|
found := false
|
|
for _, c := range result.Changes {
|
|
if c.Class == StatusRestartRequired && containsStr(c.Path, "workspaces") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("expected restart_required change on nodes[].workspaces, got %v", result.Changes)
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceCapabilityChangeRequiresRestart(t *testing.T) {
|
|
current := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-cap-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}},
|
|
})
|
|
candidate := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-cap-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead, config.WorkspaceOpWrite}},
|
|
})
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusRestartRequired {
|
|
t.Fatalf("expected restart_required for capability change, got %s", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceAdditionRequiresRestart(t *testing.T) {
|
|
current := makeTestConfig(nil)
|
|
candidate := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-new", Platform: "darwin", Root: "/Users/operator/projects/new", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}},
|
|
})
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusRestartRequired {
|
|
t.Fatalf("expected restart_required for workspace addition, got %s", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceRemovalRequiresRestart(t *testing.T) {
|
|
current := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-to-remove", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}},
|
|
})
|
|
candidate := makeTestConfig(nil)
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusRestartRequired {
|
|
t.Fatalf("expected restart_required for workspace removal, got %s", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceNoChangeIsApplied(t *testing.T) {
|
|
ws := []config.WorkspaceDefinition{
|
|
{Ref: "ws-no-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}},
|
|
}
|
|
current := makeTestConfig(ws)
|
|
candidate := makeTestConfig(ws)
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusApplied {
|
|
t.Fatalf("expected applied for no workspace change, got %s", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceEnvironmentAllowlistChangeRequiresRestart(t *testing.T) {
|
|
current := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-env-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}, EnvironmentAllowlist: []string{"PATH"}},
|
|
})
|
|
candidate := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-env-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}, EnvironmentAllowlist: []string{"PATH", "HOME"}},
|
|
})
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusRestartRequired {
|
|
t.Fatalf("expected restart_required for environment allowlist change, got %s", result.Status)
|
|
}
|
|
}
|
|
|
|
func TestClassifyWorkspaceLimitsChangeRequiresRestart(t *testing.T) {
|
|
current := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-limits-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}, MaxReadBytes: 1024},
|
|
})
|
|
candidate := makeTestConfig([]config.WorkspaceDefinition{
|
|
{Ref: "ws-limits-change", Platform: "darwin", Root: "/Users/operator/projects/test", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead}, MaxReadBytes: 2048},
|
|
})
|
|
|
|
result := Classify(current, candidate)
|
|
if result.Status != StatusRestartRequired {
|
|
t.Fatalf("expected restart_required for limits change, got %s", result.Status)
|
|
}
|
|
}
|
|
|
|
// containsStr reports whether s contains substr.
|
|
func containsStr(s, substr string) bool {
|
|
for i := 0; i+len(substr) <= len(s); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|