- Archive completed subtask plans/code reviews (04, 05+03,04, 07+03) - Add provider_pool_admission_test.go - Update edge config types, load, catalog validation - Update runtime, config refresh, service layers for admission - Update test docs and inventory - Update provider scheduling, resolution, tunnel, status modules
767 lines
22 KiB
Go
767 lines
22 KiB
Go
package configrefresh_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"iop/apps/edge/internal/configrefresh"
|
|
)
|
|
|
|
// TestClassifyProviderCapacityApplied verifies S07: provider capacity change is classified as applied.
|
|
func TestClassifyProviderCapacityApplied(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
currentPath := writeYAML(t, dir, "current.yaml", baseEdgeYAML)
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 8
|
|
max_queue: 4
|
|
queue_timeout_ms: 5000
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
|
|
ctx := context.Background()
|
|
result, _, err := configrefresh.Evaluate(ctx, current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-s07",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Errorf("expected status=%q, got %q (summary: %s)", configrefresh.StatusApplied, result.Status, result.Summary)
|
|
}
|
|
if len(result.Changes) == 0 {
|
|
t.Fatalf("expected at least one change")
|
|
}
|
|
found := false
|
|
for _, c := range result.Changes {
|
|
if c.Class == configrefresh.StatusApplied {
|
|
found = true
|
|
}
|
|
if c.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required change: %s", c.Path)
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("no applied change found in: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
func TestClassifyProviderStructuralChangeRestartRequired(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentPath := writeYAML(t, dir, "current.yaml", baseEdgeYAML)
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "vllm"
|
|
category: "api"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
max_queue: 4
|
|
queue_timeout_ms: 5000
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-provider-structural",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusRestartRequired {
|
|
t.Fatalf("expected status=%q, got %q changes=%+v", configrefresh.StatusRestartRequired, result.Status, result.Changes)
|
|
}
|
|
found := false
|
|
for _, change := range result.Changes {
|
|
if change.Path == `nodes[].providers["prov-a"].type` && change.Class == configrefresh.StatusRestartRequired {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("provider structural type change not found in %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestRefreshInvalidProviderRejected verifies S09: invalid provider config is classified as rejected.
|
|
func TestRefreshInvalidProviderRejected(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentPath := writeYAML(t, dir, "current.yaml", baseEdgeYAML)
|
|
// Invalid: shared validator rejects enabled openai_compat without endpoint.
|
|
invalidProviderYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
openai_compat:
|
|
enabled: true
|
|
endpoint: ""
|
|
`
|
|
invalidPath := writeYAML(t, dir, "invalid_provider.yaml", invalidProviderYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
|
|
ctx := context.Background()
|
|
result, candidate, err := configrefresh.Evaluate(ctx, current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: invalidPath,
|
|
RequestID: "test-s09-provider",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate should not return error for rejected config: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusRejected {
|
|
t.Errorf("expected status=%q, got %q (summary: %s)", configrefresh.StatusRejected, result.Status, result.Summary)
|
|
}
|
|
if candidate != nil {
|
|
t.Errorf("expected nil candidate for rejected result")
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderEnabledLiveApply verifies that toggling providers[].enabled
|
|
// is classified as StatusApplied (live apply, no restart required).
|
|
func TestClassifyProviderEnabledLiveApply(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
// Candidate disables prov-a.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
enabled: false
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-enabled-liveapply",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected status=applied for enabled toggle, got %q", result.Status)
|
|
}
|
|
foundEnabled := false
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes[].providers["prov-a"].enabled` {
|
|
foundEnabled = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("providers[].enabled class: got %q, want applied", c.Class)
|
|
}
|
|
if c.Previous != "true" || c.Next != "false" {
|
|
t.Errorf("providers[].enabled change: prev=%q next=%q", c.Previous, c.Next)
|
|
}
|
|
}
|
|
if c.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required change for enabled-only diff: %s", c.Path)
|
|
}
|
|
}
|
|
if !foundEnabled {
|
|
t.Errorf("providers[].enabled change path not found in: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderFirstEndpointOnlyRestartRequired verifies that changing
|
|
// only the provider endpoint (type/category unchanged) is classified as
|
|
// restart_required and surfaces the correct path.
|
|
func TestClassifyProviderFirstEndpointOnlyRestartRequired(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
providers:
|
|
- id: "prov-oc"
|
|
type: "openai_compat"
|
|
category: "api"
|
|
endpoint: "http://127.0.0.1:8000/v1"
|
|
models: ["model-a"]
|
|
capacity: 2
|
|
`
|
|
// Only endpoint changes — type, category, models, capacity are identical.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
providers:
|
|
- id: "prov-oc"
|
|
type: "openai_compat"
|
|
category: "api"
|
|
endpoint: "http://127.0.0.1:9000/v1"
|
|
models: ["model-a"]
|
|
capacity: 2
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-endpoint-only",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusRestartRequired {
|
|
t.Fatalf("expected status=restart_required for endpoint-only change, got %q; changes=%+v", result.Status, result.Changes)
|
|
}
|
|
found := false
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes[].providers["prov-oc"].endpoint` && c.Class == configrefresh.StatusRestartRequired {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected restart_required change for providers[].endpoint; changes=%+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderFirstHeadersChangeRestartRequired verifies that changing
|
|
// a provider headers map (type/category unchanged) is classified as
|
|
// restart_required — covers map deep-diff path.
|
|
func TestClassifyProviderFirstHeadersChangeRestartRequired(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
providers:
|
|
- id: "prov-oc"
|
|
type: "openai_compat"
|
|
category: "api"
|
|
endpoint: "http://127.0.0.1:8000/v1"
|
|
headers:
|
|
Authorization: "Bearer old-token"
|
|
models: ["model-a"]
|
|
capacity: 2
|
|
`
|
|
// Only headers change.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
providers:
|
|
- id: "prov-oc"
|
|
type: "openai_compat"
|
|
category: "api"
|
|
endpoint: "http://127.0.0.1:8000/v1"
|
|
headers:
|
|
Authorization: "Bearer new-token"
|
|
models: ["model-a"]
|
|
capacity: 2
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-headers-only",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusRestartRequired {
|
|
t.Fatalf("expected status=restart_required for headers change, got %q; changes=%+v", result.Status, result.Changes)
|
|
}
|
|
found := false
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes[].providers["prov-oc"].headers` && c.Class == configrefresh.StatusRestartRequired {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected restart_required change for providers[].headers; changes=%+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
func TestClassifyProviderFirstExecutionFieldRestartRequired(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
vllm:
|
|
enabled: true
|
|
endpoint: "http://127.0.0.1:8000/v1"
|
|
providers:
|
|
- id: "prov-vllm"
|
|
type: "vllm"
|
|
category: "api"
|
|
endpoint: "http://127.0.0.1:8000/v1"
|
|
models: ["model-a"]
|
|
capacity: 2
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
// Candidate changes endpoint (provider-first execution field) → restart required.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
vllm:
|
|
enabled: true
|
|
endpoint: "http://127.0.0.1:8080/v1"
|
|
providers:
|
|
- id: "prov-vllm"
|
|
type: "openai_compat"
|
|
category: "api"
|
|
endpoint: "http://127.0.0.1:8080/v1"
|
|
models: ["model-a"]
|
|
capacity: 2
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-provfirst-restart",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusRestartRequired {
|
|
t.Fatalf("expected status=restart_required for provider type change, got %q", result.Status)
|
|
}
|
|
hasTypeRestart := false
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes[].providers["prov-vllm"].type` && c.Class == configrefresh.StatusRestartRequired {
|
|
hasTypeRestart = true
|
|
}
|
|
}
|
|
if !hasTypeRestart {
|
|
t.Errorf("expected restart_required for providers[].type change; changes: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderPriorityApplied verifies that provider priority change
|
|
// is classified as StatusApplied (live apply, no restart required).
|
|
func TestClassifyProviderPriorityApplied(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
priority: 5
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
// Candidate only changes priority.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
priority: 1
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-priority-applied",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected status=applied for priority-only change, got %q (summary: %s)", result.Status, result.Summary)
|
|
}
|
|
foundPriority := false
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes[].providers["prov-a"].priority` {
|
|
foundPriority = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("providers[].priority class: got %q, want applied", c.Class)
|
|
}
|
|
if c.Previous != "5" || c.Next != "1" {
|
|
t.Errorf("providers[].priority change: prev=%q next=%q", c.Previous, c.Next)
|
|
}
|
|
}
|
|
if c.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required change for priority-only diff: %s", c.Path)
|
|
}
|
|
}
|
|
if !foundPriority {
|
|
t.Errorf("providers[].priority change path not found in: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderLongContextCapacityApplied verifies that provider
|
|
// total_context_tokens and long_context_capacity changes are classified as
|
|
// live-apply (StatusApplied), consistent with capacity/priority.
|
|
func TestClassifyProviderLongContextCapacityApplied(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
total_context_tokens: 262144
|
|
long_context_capacity: 1
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
// Candidate only changes the two long-context fields.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
total_context_tokens: 524288
|
|
long_context_capacity: 2
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-long-capacity-applied",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected status=applied for long-context-only change, got %q (summary: %s)", result.Status, result.Summary)
|
|
}
|
|
foundTotal := false
|
|
foundLong := false
|
|
for _, c := range result.Changes {
|
|
switch c.Path {
|
|
case `nodes[].providers["prov-a"].total_context_tokens`:
|
|
foundTotal = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("total_context_tokens class: got %q, want applied", c.Class)
|
|
}
|
|
if c.Previous != "262144" || c.Next != "524288" {
|
|
t.Errorf("total_context_tokens change: prev=%q next=%q", c.Previous, c.Next)
|
|
}
|
|
case `nodes[].providers["prov-a"].long_context_capacity`:
|
|
foundLong = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("long_context_capacity class: got %q, want applied", c.Class)
|
|
}
|
|
if c.Previous != "1" || c.Next != "2" {
|
|
t.Errorf("long_context_capacity change: prev=%q next=%q", c.Previous, c.Next)
|
|
}
|
|
}
|
|
if c.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required change for long-context-only diff: %s", c.Path)
|
|
}
|
|
}
|
|
if !foundTotal {
|
|
t.Errorf("providers[].total_context_tokens change path not found in: %+v", result.Changes)
|
|
}
|
|
if !foundLong {
|
|
t.Errorf("providers[].long_context_capacity change path not found in: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderPoolPolicyLiveApply verifies that changes to the
|
|
// canonical root provider_pool.max_queue and provider_pool.queue_timeout_ms
|
|
// are classified as live-apply (StatusApplied) at the root path.
|
|
func TestClassifyProviderPoolPolicyLiveApply(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
provider_pool:
|
|
max_queue: 16
|
|
queue_timeout_ms: 30000
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
provider_pool:
|
|
max_queue: 32
|
|
queue_timeout_ms: 60000
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-pool-policy-liveapply",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected status=applied for root provider_pool change, got %q (summary: %s)", result.Status, result.Summary)
|
|
}
|
|
foundMaxQ := false
|
|
foundTimeout := false
|
|
for _, c := range result.Changes {
|
|
switch c.Path {
|
|
case "provider_pool.max_queue":
|
|
foundMaxQ = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("provider_pool.max_queue class: got %q, want applied", c.Class)
|
|
}
|
|
if c.Previous != "16" || c.Next != "32" {
|
|
t.Errorf("provider_pool.max_queue change: prev=%q next=%q", c.Previous, c.Next)
|
|
}
|
|
case "provider_pool.queue_timeout_ms":
|
|
foundTimeout = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("provider_pool.queue_timeout_ms class: got %q, want applied", c.Class)
|
|
}
|
|
if c.Previous != "30000" || c.Next != "60000" {
|
|
t.Errorf("provider_pool.queue_timeout_ms change: prev=%q next=%q", c.Previous, c.Next)
|
|
}
|
|
}
|
|
if c.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required change for policy-only diff: %s", c.Path)
|
|
}
|
|
}
|
|
if !foundMaxQ {
|
|
t.Errorf("provider_pool.max_queue change not found in: %+v", result.Changes)
|
|
}
|
|
if !foundTimeout {
|
|
t.Errorf("provider_pool.queue_timeout_ms change not found in: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
// TestClassifyProviderPoolPolicyIgnoresLegacyWhenCanonical verifies that when
|
|
// canonical provider_pool is set, per-provider max_queue/queue_timeout_ms
|
|
// changes are not reported as effective policy changes.
|
|
func TestClassifyProviderPoolPolicyIgnoresLegacyWhenCanonical(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
provider_pool:
|
|
max_queue: 16
|
|
queue_timeout_ms: 30000
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
max_queue: 4
|
|
queue_timeout_ms: 5000
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
// Candidate changes only the legacy per-provider queue fields while
|
|
// canonical provider_pool remains identical.
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
provider_pool:
|
|
max_queue: 16
|
|
queue_timeout_ms: 30000
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
max_queue: 8
|
|
queue_timeout_ms: 10000
|
|
`
|
|
candidatePath := writeYAML(t, dir, "candidate.yaml", candidateYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
result, _, err := configrefresh.Evaluate(context.Background(), current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: candidatePath,
|
|
RequestID: "test-policy-ignores-legacy",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes[].providers["prov-a"].max_queue` {
|
|
t.Errorf("legacy per-provider max_queue should not be reported as policy change; got change: %+v", c)
|
|
}
|
|
if c.Path == `nodes[].providers["prov-a"].queue_timeout_ms` {
|
|
t.Errorf("legacy per-provider queue_timeout_ms should not be reported as policy change; got change: %+v", c)
|
|
}
|
|
}
|
|
}
|