- agent-readable-repository-refactor 완료: 기존 테스트 파일 아카이브 이동 - 새 테스트 파일 추가 (edge, node, client, config, readability) - readability_audit 스크립트 및 baseline 추가 - roadmap/SDD 문서 갱신 - agent-client/pi/extensions/openai-sampling-parameters 추가
238 lines
6.1 KiB
Go
238 lines
6.1 KiB
Go
package configrefresh_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"iop/apps/edge/internal/configrefresh"
|
|
)
|
|
|
|
// TestClassifyNodeRuntimeConcurrencyApplied verifies S15: node runtime
|
|
// concurrency is applied live.
|
|
func TestClassifyNodeRuntimeConcurrencyApplied(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
|
|
runtime:
|
|
concurrency: 2
|
|
`
|
|
currentPath := writeYAML(t, dir, "current.yaml", currentYAML)
|
|
|
|
concurrencyOnlyYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
runtime:
|
|
concurrency: 4
|
|
`
|
|
concurrencyOnlyPath := writeYAML(t, dir, "concurrency.yaml", concurrencyOnlyYAML)
|
|
|
|
current := buildNormalizedCurrent(t, currentPath)
|
|
ctx := context.Background()
|
|
|
|
// concurrency-only change → applied.
|
|
result, _, err := configrefresh.Evaluate(ctx, current, configrefresh.Request{
|
|
Mode: configrefresh.ModeDryRun,
|
|
ConfigPath: concurrencyOnlyPath,
|
|
RequestID: "test-s15-conc",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate concurrency: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected concurrency change status=applied, got %q (summary: %s)", result.Status, result.Summary)
|
|
}
|
|
foundConc := false
|
|
for _, c := range result.Changes {
|
|
if c.Path == `nodes["node-1"].runtime.concurrency` {
|
|
foundConc = true
|
|
if c.Class != configrefresh.StatusApplied {
|
|
t.Errorf("expected concurrency class=applied, got %q", c.Class)
|
|
}
|
|
}
|
|
if c.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required change for concurrency-only diff: %s", c.Path)
|
|
}
|
|
}
|
|
if !foundConc {
|
|
t.Errorf("concurrency change path not found in: %+v", result.Changes)
|
|
}
|
|
}
|
|
|
|
func TestClassifyModelCatalogProviderMappingApplied(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
|
|
- id: "prov-b"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
models:
|
|
- id: "qwen3.6:35b"
|
|
display_name: "Qwen"
|
|
context_window_tokens: 131072
|
|
default_max_tokens: 8192
|
|
min_max_tokens: 8192
|
|
providers:
|
|
prov-a: "llama3.1"
|
|
`
|
|
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
|
|
- id: "prov-b"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
adapter: "cli"
|
|
models: ["llama3.1"]
|
|
capacity: 2
|
|
models:
|
|
- id: "qwen3.6:35b"
|
|
display_name: "Qwen"
|
|
context_window_tokens: 262144
|
|
default_max_tokens: 32768
|
|
min_max_tokens: 32768
|
|
default_thinking_token_budget: 8192
|
|
providers:
|
|
prov-b: "llama3.1"
|
|
`
|
|
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-model-catalog",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected status=%q, got %q changes=%+v", configrefresh.StatusApplied, result.Status, result.Changes)
|
|
}
|
|
found := map[string]bool{}
|
|
for _, change := range result.Changes {
|
|
if change.Class == configrefresh.StatusApplied {
|
|
found[change.Path] = true
|
|
}
|
|
}
|
|
for _, path := range []string{
|
|
`models["qwen3.6:35b"].context_window_tokens`,
|
|
`models["qwen3.6:35b"].default_max_tokens`,
|
|
`models["qwen3.6:35b"].min_max_tokens`,
|
|
`models["qwen3.6:35b"].default_thinking_token_budget`,
|
|
`models["qwen3.6:35b"].providers`,
|
|
} {
|
|
if !found[path] {
|
|
t.Fatalf("model catalog change %s not found in %+v", path, result.Changes)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClassifyLongContextThresholdApplied(t *testing.T) {
|
|
dir := t.TempDir()
|
|
currentYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
long_context_threshold_tokens: 100000
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
`
|
|
candidateYAML := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
long_context_threshold_tokens: 120000
|
|
nodes:
|
|
- id: "node-1"
|
|
alias: "n1"
|
|
token: "tok-1"
|
|
adapters:
|
|
cli:
|
|
enabled: true
|
|
`
|
|
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-long-threshold",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Evaluate: %v", err)
|
|
}
|
|
if result.Status != configrefresh.StatusApplied {
|
|
t.Fatalf("expected status=%q, got %q changes=%+v", configrefresh.StatusApplied, result.Status, result.Changes)
|
|
}
|
|
found := false
|
|
for _, change := range result.Changes {
|
|
if change.Path == "long_context_threshold_tokens" {
|
|
found = true
|
|
if change.Class != configrefresh.StatusApplied {
|
|
t.Errorf("threshold class: got %q, want applied", change.Class)
|
|
}
|
|
if change.Previous != "100000" || change.Next != "120000" {
|
|
t.Errorf("threshold diff: got %s→%s", change.Previous, change.Next)
|
|
}
|
|
}
|
|
if change.Class == configrefresh.StatusRestartRequired {
|
|
t.Errorf("unexpected restart_required threshold change: %s", change.Path)
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("long_context_threshold_tokens change not found in %+v", result.Changes)
|
|
}
|
|
}
|