iop/packages/go/config/provider_catalog_config_test.go
toki 2f560e3f3b feat: provider pool admission, policy config, snapshot source task archive + runtime updates
- 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
2026-07-19 22:41:05 +09:00

1217 lines
36 KiB
Go

package config_test
import (
"os"
"path/filepath"
"strings"
"testing"
"iop/packages/go/config"
)
func TestLoadEdge_MultiOllamaInstances(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- alias: "multi"
adapters:
ollama_instances:
- name: "local"
enabled: true
base_url: "http://127.0.0.1:11434"
context_size: 131072
capacity: 2
max_queue: 4
queue_timeout_ms: 1000
request_timeout_ms: 20000
- name: "dgx"
enabled: true
base_url: "http://192.168.0.91:11434"
context_size: 262144
capacity: 6
max_queue: 12
queue_timeout_ms: 2000
request_timeout_ms: 60000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if len(cfg.Nodes) == 0 {
t.Fatal("expected 1 node")
}
insts := cfg.Nodes[0].Adapters.OllamaInstances
if len(insts) != 2 {
t.Fatalf("expected 2 ollama instances, got %d", len(insts))
}
if insts[0].Name != "local" || insts[0].BaseURL != "http://127.0.0.1:11434" || insts[0].ContextSize != 131072 {
t.Errorf("unexpected instance[0]: %+v", insts[0])
}
if insts[0].Capacity != 2 || insts[0].MaxQueue != 4 || insts[0].QueueTimeoutMS != 1000 || insts[0].RequestTimeoutMS != 20000 {
t.Errorf("unexpected instance[0] queue config: %+v", insts[0])
}
if insts[1].Name != "dgx" || insts[1].BaseURL != "http://192.168.0.91:11434" || insts[1].ContextSize != 262144 {
t.Errorf("unexpected instance[1]: %+v", insts[1])
}
if insts[1].Capacity != 6 || insts[1].MaxQueue != 12 || insts[1].QueueTimeoutMS != 2000 || insts[1].RequestTimeoutMS != 60000 {
t.Errorf("unexpected instance[1] queue config: %+v", insts[1])
}
}
func TestLoadEdge_MultiVllmInstances(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- alias: "multi-vllm"
adapters:
vllm_instances:
- name: "a100"
enabled: true
endpoint: "http://10.0.0.5:8000"
capacity: 4
max_queue: 10
queue_timeout_ms: 1500
request_timeout_ms: 45000
- name: "h100"
enabled: true
endpoint: "http://10.0.0.6:8000"
capacity: 8
max_queue: 16
queue_timeout_ms: 2500
request_timeout_ms: 90000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if len(cfg.Nodes) == 0 {
t.Fatal("expected 1 node")
}
insts := cfg.Nodes[0].Adapters.VllmInstances
if len(insts) != 2 {
t.Fatalf("expected 2 vllm instances, got %d", len(insts))
}
if insts[0].Name != "a100" || insts[0].Endpoint != "http://10.0.0.5:8000" {
t.Errorf("unexpected instance[0]: %+v", insts[0])
}
if insts[0].Capacity != 4 || insts[0].MaxQueue != 10 || insts[0].QueueTimeoutMS != 1500 || insts[0].RequestTimeoutMS != 45000 {
t.Errorf("unexpected instance[0] queue config: %+v", insts[0])
}
if insts[1].Name != "h100" || insts[1].Endpoint != "http://10.0.0.6:8000" {
t.Errorf("unexpected instance[1]: %+v", insts[1])
}
if insts[1].Capacity != 8 || insts[1].MaxQueue != 16 || insts[1].QueueTimeoutMS != 2500 || insts[1].RequestTimeoutMS != 90000 {
t.Errorf("unexpected instance[1] queue config: %+v", insts[1])
}
}
func TestLoadEdge_LegacyOllamaPromotedToInstances(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- alias: "local"
adapters:
ollama:
enabled: true
base_url: "http://192.168.0.91:11434"
context_size: 262144
capacity: 5
max_queue: 9
queue_timeout_ms: 1700
request_timeout_ms: 55000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
insts := cfg.Nodes[0].Adapters.OllamaInstances
if len(insts) != 1 {
t.Fatalf("expected 1 ollama instance from legacy field, got %d", len(insts))
}
if insts[0].Name != "ollama" {
t.Errorf("expected instance name %q, got %q", "ollama", insts[0].Name)
}
if insts[0].BaseURL != "http://192.168.0.91:11434" {
t.Errorf("expected base_url %q, got %q", "http://192.168.0.91:11434", insts[0].BaseURL)
}
if insts[0].Capacity != 5 || insts[0].MaxQueue != 9 || insts[0].QueueTimeoutMS != 1700 || insts[0].RequestTimeoutMS != 55000 {
t.Errorf("unexpected promoted queue config: %+v", insts[0])
}
}
func TestLoadEdge_DuplicateOllamaInstanceNameRejected(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- alias: "dup"
adapters:
ollama_instances:
- name: "same"
enabled: true
base_url: "http://127.0.0.1:11434"
- name: "same"
enabled: true
base_url: "http://127.0.0.2:11434"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for duplicate ollama instance name")
}
}
func TestLoadEdge_ProviderQueueConfigRejectsNegative(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- alias: "bad-queue"
adapters:
ollama_instances:
- name: "local"
enabled: true
base_url: "http://127.0.0.1:11434"
capacity: -1
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected negative provider queue config error")
}
if !strings.Contains(err.Error(), "capacity") || !strings.Contains(err.Error(), "non-negative") {
t.Fatalf("expected error to mention capacity non-negative, got %v", err)
}
}
func TestNormalizeAdapters_OllamaIdempotent(t *testing.T) {
a := config.AdaptersConf{
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434", ContextSize: 4096},
}
if err := config.NormalizeAdapters(&a); err != nil {
t.Fatalf("first normalize: %v", err)
}
if len(a.OllamaInstances) != 1 {
t.Fatalf("expected 1 instance after first normalize, got %d", len(a.OllamaInstances))
}
if err := config.NormalizeAdapters(&a); err != nil {
t.Fatalf("second normalize (idempotent): %v", err)
}
if len(a.OllamaInstances) != 1 {
t.Fatalf("expected still 1 instance after second normalize, got %d", len(a.OllamaInstances))
}
}
func TestNormalizeAdapters_VllmIdempotent(t *testing.T) {
a := config.AdaptersConf{
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
}
if err := config.NormalizeAdapters(&a); err != nil {
t.Fatalf("first normalize: %v", err)
}
if len(a.VllmInstances) != 1 {
t.Fatalf("expected 1 instance after first normalize, got %d", len(a.VllmInstances))
}
if err := config.NormalizeAdapters(&a); err != nil {
t.Fatalf("second normalize (idempotent): %v", err)
}
if len(a.VllmInstances) != 1 {
t.Fatalf("expected still 1 instance after second normalize, got %d", len(a.VllmInstances))
}
}
func TestNormalizeAdapters_OllamaConflictErrors(t *testing.T) {
a := config.AdaptersConf{
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434"},
OllamaInstances: []config.OllamaInstanceConf{
{Name: "ollama", Enabled: true, BaseURL: "http://other-host:11434"},
},
}
if err := config.NormalizeAdapters(&a); err == nil {
t.Fatal("expected error for conflicting legacy ollama and explicit instance with same name")
}
}
func TestNormalizeAdapters_VllmConflictErrors(t *testing.T) {
a := config.AdaptersConf{
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
VllmInstances: []config.VllmInstanceConf{
{Name: "vllm", Enabled: true, Endpoint: "http://other-host:8000"},
},
}
if err := config.NormalizeAdapters(&a); err == nil {
t.Fatal("expected error for conflicting legacy vllm and explicit instance with same name")
}
}
func TestNormalizeAdapters_OllamaDisabledSameNameErrors(t *testing.T) {
a := config.AdaptersConf{
Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434"},
OllamaInstances: []config.OllamaInstanceConf{
{Name: "ollama", Enabled: false, BaseURL: "http://127.0.0.1:11434"},
},
}
if err := config.NormalizeAdapters(&a); err == nil {
t.Fatal("expected error for legacy enabled ollama conflicting with same-name disabled explicit instance")
}
}
func TestNormalizeAdapters_VllmDisabledSameNameErrors(t *testing.T) {
a := config.AdaptersConf{
Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"},
VllmInstances: []config.VllmInstanceConf{
{Name: "vllm", Enabled: false, Endpoint: "http://127.0.0.1:8000"},
},
}
if err := config.NormalizeAdapters(&a); err == nil {
t.Fatal("expected error for legacy enabled vllm conflicting with same-name disabled explicit instance")
}
}
func TestLoadEdge_OllamaContextSize(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- alias: "local"
adapters:
ollama:
enabled: true
base_url: "http://192.168.0.91:11434"
context_size: 262144
capacity: 3
max_queue: 8
queue_timeout_ms: 1500
request_timeout_ms: 30000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
ollama := cfg.Nodes[0].Adapters.Ollama
if !ollama.Enabled {
t.Fatal("expected ollama.enabled=true")
}
if ollama.BaseURL != "http://192.168.0.91:11434" || ollama.ContextSize != 262144 {
t.Fatalf("unexpected ollama config: %+v", ollama)
}
if ollama.Capacity != 3 || ollama.MaxQueue != 8 || ollama.QueueTimeoutMS != 1500 || ollama.RequestTimeoutMS != 30000 {
t.Fatalf("unexpected ollama queue config: %+v", ollama)
}
}
// Provider pool schema validation tests (G06)
func TestLoadEdge_ModelCatalogBasicHappyPath(t *testing.T) {
// Happy path: models[].providers values must match nodes[].providers[].models.
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
display_name: "Qwen 3.6 35B"
context_window_tokens: 262144
default_max_tokens: 32768
min_max_tokens: 32768
default_thinking_token_budget: 8192
providers:
vllm-gpu: "nvidia/Qwen3.6-35B-A3B-NVFP4"
ollama-local: "qwen3.6:35b"
nodes:
- id: "node-gpu-01"
alias: "gpu-node"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B-A3B-NVFP4"
capacity: 4
- id: "ollama-local"
type: "ollama"
category: "local_inference"
models:
- "qwen3.6:35b"
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if len(cfg.Models) != 1 {
t.Fatalf("expected 1 model catalog entry, got %d", len(cfg.Models))
}
m := cfg.Models[0]
if m.ID != "qwen3.6:35b" {
t.Errorf("models[0].ID = %q, want %q", m.ID, "qwen3.6:35b")
}
if m.DisplayName != "Qwen 3.6 35B" {
t.Errorf("models[0].DisplayName = %q, want %q", m.DisplayName, "Qwen 3.6 35B")
}
if m.ContextWindowTokens != 262144 {
t.Errorf("models[0].ContextWindowTokens = %d, want 262144", m.ContextWindowTokens)
}
if m.DefaultMaxTokens != 32768 {
t.Errorf("models[0].DefaultMaxTokens = %d, want 32768", m.DefaultMaxTokens)
}
if m.MinMaxTokens != 32768 {
t.Errorf("models[0].MinMaxTokens = %d, want 32768", m.MinMaxTokens)
}
if m.DefaultThinkingTokenBudget != 8192 {
t.Errorf("models[0].DefaultThinkingTokenBudget = %d, want 8192", m.DefaultThinkingTokenBudget)
}
if len(m.Providers) != 2 {
t.Fatalf("expected 2 providers, got %d", len(m.Providers))
}
if m.Providers["vllm-gpu"] != "nvidia/Qwen3.6-35B-A3B-NVFP4" {
t.Errorf("providers[vllm-gpu] = %q, want %q", m.Providers["vllm-gpu"], "nvidia/Qwen3.6-35B-A3B-NVFP4")
}
if m.Providers["ollama-local"] != "qwen3.6:35b" {
t.Errorf("providers[ollama-local] = %q, want %q", m.Providers["ollama-local"], "qwen3.6:35b")
}
if len(cfg.Nodes) != 1 {
t.Fatalf("expected 1 node, got %d", len(cfg.Nodes))
}
if len(cfg.Nodes[0].Providers) != 2 {
t.Fatalf("expected 2 node providers, got %d", len(cfg.Nodes[0].Providers))
}
// Check category resolution.
for _, p := range cfg.Nodes[0].Providers {
if p.ID == "vllm-gpu" && p.Category != config.CategoryAPI {
t.Errorf("providers[vllm-gpu].Category = %q, want %q", p.Category, config.CategoryAPI)
}
if p.ID == "ollama-local" && p.Category != config.CategoryLocalInference {
t.Errorf("providers[ollama-local].Category = %q, want %q", p.Category, config.CategoryLocalInference)
}
}
}
func TestLoadEdge_ConfigExampleMixedAndOllamaOnlyProviderGroups(t *testing.T) {
f := filepath.Join("..", "..", "..", "configs", "edge.yaml")
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load example config: %v", err)
}
models := map[string]config.ModelCatalogEntry{}
for _, m := range cfg.Models {
models[m.ID] = m
}
mixed, ok := models["qwen3.6:35b"]
if !ok {
t.Fatal("expected qwen3.6:35b mixed model group in example config")
}
if mixed.Providers["mac-mlx-vllm"] != "mlx-community/Qwen3.6-35B-A3B-4bit" {
t.Fatalf("expected mac-mlx-vllm served model in mixed group, got %+v", mixed.Providers)
}
if mixed.Providers["ollama-local"] != "qwen3.6:35b" {
t.Fatalf("expected ollama-local fallback in mixed group, got %+v", mixed.Providers)
}
ollamaOnly, ok := models["local-llama3"]
if !ok {
t.Fatal("expected local-llama3 Ollama-only model group in example config")
}
if len(ollamaOnly.Providers) != 1 || ollamaOnly.Providers["ollama-local"] != "llama3.1:8b" {
t.Fatalf("expected local-llama3 to route only to ollama-local, got %+v", ollamaOnly.Providers)
}
providers := map[string]config.NodeProviderConf{}
for _, n := range cfg.Nodes {
for _, p := range n.Providers {
providers[p.ID] = p
}
}
vllm := providers["mac-mlx-vllm"]
ollama := providers["ollama-local"]
if vllm.Type == "" || ollama.Type == "" {
t.Fatalf("expected both mac-mlx-vllm and ollama-local providers, got %+v", providers)
}
if config.NormalizeProviderType(vllm.Type) != "openai_compat" {
t.Fatalf("mac-mlx-vllm should classify as OpenAI-compatible, got %q", vllm.Type)
}
if config.NormalizeProviderType(ollama.Type) != "ollama" {
t.Fatalf("ollama-local should classify as normalized Ollama, got %q", ollama.Type)
}
if ollama.Capacity != 1 || ollama.Priority <= vllm.Priority {
t.Fatalf("expected Ollama example to use lower capacity and higher priority number than vLLM, got vllm=%+v ollama=%+v", vllm, ollama)
}
}
func TestLoadEdge_SeulgivibeProviderFirstAliases(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
openai:
provider_auth:
enabled: true
models:
- id: "claude-sonnet-4-5"
providers:
seulgivibe-claude: "claude-sonnet-4-5"
- id: "claude-opus-4-8"
providers:
seulgivibe-claude: "claude-opus-4-8"
- id: "claude-fable-5"
providers:
seulgivibe-claude: "claude-fable-5"
- id: "gpt-5.1"
providers:
seulgivibe-openai: "gpt-5.1"
- id: "gpt-5.5"
providers:
seulgivibe-openai: "gpt-5.5"
nodes:
- id: "node-seulgivibe"
alias: "seulgivibe-node"
providers:
- id: "seulgivibe-claude"
type: "seulgivibe_claude"
category: "api"
endpoint: "https://seulgivibe.example.invalid/anthropic/v1"
models:
- "claude-sonnet-4-5"
- "claude-opus-4-8"
- "claude-fable-5"
capacity: 4
- id: "seulgivibe-openai"
type: "seulgivibe_openai"
category: "api"
endpoint: "https://seulgivibe.example.invalid/openai/v1"
models:
- "gpt-5.1"
- "gpt-5.5"
capacity: 4
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if len(cfg.Models) != 5 {
t.Fatalf("expected 5 Seulgivibe model catalog entries, got %d", len(cfg.Models))
}
if got := config.NormalizeProviderType(cfg.Nodes[0].Providers[0].Type); got != "openai_compat" {
t.Fatalf("seulgivibe_claude should normalize to openai_compat, got %q", got)
}
if got := config.NormalizeProviderType(cfg.Nodes[0].Providers[1].Type); got != "openai_compat" {
t.Fatalf("seulgivibe_openai should normalize to openai_compat, got %q", got)
}
auth := cfg.OpenAI.ProviderAuth
if !auth.Enabled || auth.FromHeader != "X-IOP-Provider-Authorization" || auth.TargetHeader != "Authorization" || auth.Scheme != "Bearer" || !auth.Required {
t.Fatalf("unexpected provider_auth defaults: %+v", auth)
}
for _, m := range cfg.Models {
for providerID, servedModel := range m.Providers {
if strings.TrimSpace(servedModel) == "" {
t.Fatalf("model %q provider %q has empty served model", m.ID, providerID)
}
}
}
}
func TestLoadEdge_ModelCatalogEmptyModelIDRejected(t *testing.T) {
// No models[] on node, so serve model check is skipped (legacy compat).
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: ""
providers:
vllm-gpu: "model-a"
nodes:
- id: "node-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for empty model id")
}
if !strings.Contains(err.Error(), "models[0]") || !strings.Contains(err.Error(), "id must not be empty") {
t.Fatalf("expected error mentioning models[0] and id must not be empty, got %v", err)
}
}
func TestLoadEdge_ModelCatalogDuplicateModelIDRejected(t *testing.T) {
// serve model membership fail → 다른 error로 먼저 실패해야 하지만 duplicate 체크가
// 먼저 발생하므로 models validation 전에 seen check가 먼저 통과하는 fixture.
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
vllm-gpu: "model-a"
- id: "qwen3.6:35b"
providers:
ollama: "qwen3.6"
nodes:
- id: "node-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "model-a"
- id: "ollama"
type: "ollama"
category: "local_inference"
models:
- "qwen3.6"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for duplicate model id")
}
if !strings.Contains(err.Error(), "duplicate model id") {
t.Fatalf("expected error mentioning duplicate model id, got %v", err)
}
}
func TestLoadEdge_ModelCatalogEmptyProvidersRejected(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers: {}
nodes:
- id: "node-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for empty providers")
}
if !strings.Contains(err.Error(), "providers must not be empty") {
t.Fatalf("expected error mentioning providers must not be empty, got %v", err)
}
}
func TestLoadEdge_ModelCatalogContextWindowRejectsNegative(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
context_window_tokens: -1
providers:
vllm-gpu: "model-a"
nodes:
- id: "node-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "model-a"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for negative context_window_tokens")
}
if !strings.Contains(err.Error(), "context_window_tokens must be non-negative") {
t.Fatalf("expected error mentioning context_window_tokens, got %v", err)
}
}
func TestLoadEdge_ModelCatalogServedModelMembershipMissing(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
vllm-gpu: "typo-model"
nodes:
- id: "node-gpu-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B-A3B-NVFP4"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for served model not in provider's models list")
}
if !strings.Contains(err.Error(), "typo-model") || !strings.Contains(err.Error(), "vllm-gpu") {
t.Fatalf("expected error mentioning typo-model and vllm-gpu, got %v", err)
}
}
func TestLoadEdge_ModelCatalogUnresolvedProviderRejected(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
non-existent: "model-a"
nodes:
- id: "node-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for unresolved provider id")
}
if !strings.Contains(err.Error(), "does not resolve") || !strings.Contains(err.Error(), "non-existent") {
t.Fatalf("expected error mentioning does not resolve and non-existent, got %v", err)
}
}
// ---------- PROVIDER_POLICY_CONFIG: root provider_pool queue policy tests ----------
// TestLoadEdge_ProviderPoolPolicyCanonical verifies S06: root provider_pool
// is the canonical queue policy owner and its explicit values are honored.
func TestLoadEdge_ProviderPoolPolicyCanonical(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
provider_pool:
max_queue: 32
queue_timeout_ms: 60000
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != 32 {
t.Errorf("expected canonical max_queue=32, got %d", cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 60000 {
t.Errorf("expected canonical queue_timeout_ms=60000, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
}
// TestLoadEdge_ProviderPoolPolicyCanonicalMaxZeroDefaults verifies that
// canonical max_queue=0 normalizes to the default 16, preserving other values.
func TestLoadEdge_ProviderPoolPolicyCanonicalMaxZeroDefaults(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
provider_pool:
max_queue: 0
queue_timeout_ms: 45000
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != config.DefaultProviderPoolMaxQueue {
t.Errorf("expected normalized max_queue=%d, got %d", config.DefaultProviderPoolMaxQueue, cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 45000 {
t.Errorf("expected queue_timeout_ms=45000, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
}
// TestLoadEdge_ProviderPoolPolicyCanonicalExplicitZeroTimeout verifies that
// explicit queue_timeout_ms=0 is preserved as no-timeout.
func TestLoadEdge_ProviderPoolPolicyCanonicalExplicitZeroTimeout(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
provider_pool:
max_queue: 20
queue_timeout_ms: 0
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != 20 {
t.Errorf("expected max_queue=20, got %d", cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 0 {
t.Errorf("expected explicit zero timeout preserved, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
}
// TestLoadEdge_ProviderPoolPolicyLegacyIdenticalPromotion verifies that when
// no canonical is set, identical legacy pairs across providers are promoted.
func TestLoadEdge_ProviderPoolPolicyLegacyIdenticalPromotion(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
max_queue: 8
queue_timeout_ms: 15000
- id: "prov-b"
type: "ollama"
category: "local_inference"
models: ["model-b"]
capacity: 3
max_queue: 8
queue_timeout_ms: 15000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != 8 {
t.Errorf("expected promoted max_queue=8, got %d", cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 15000 {
t.Errorf("expected promoted queue_timeout_ms=15000, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
}
// TestLoadEdge_ProviderPoolPolicyLegacyConflict verifies that differing
// legacy pairs across providers produce a deterministic error naming both
// providers.
func TestLoadEdge_ProviderPoolPolicyLegacyConflict(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
max_queue: 8
queue_timeout_ms: 15000
- id: "prov-b"
type: "ollama"
category: "local_inference"
models: ["model-b"]
capacity: 3
max_queue: 16
queue_timeout_ms: 15000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected conflict error for differing legacy queue policy")
}
if !strings.Contains(err.Error(), "conflicting provider queue policy") {
t.Fatalf("expected conflict error, got: %v", err)
}
if !strings.Contains(err.Error(), "prov-a") || !strings.Contains(err.Error(), "prov-b") {
t.Fatalf("expected error to name both conflicting providers, got: %v", err)
}
}
// TestLoadEdge_ProviderPoolPolicyOrderIndependentConflict verifies that the
// conflict detection is order-independent (swapping prov-a and prov-b order).
func TestLoadEdge_ProviderPoolPolicyOrderIndependentConflict(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-b"
type: "ollama"
category: "local_inference"
models: ["model-b"]
capacity: 3
max_queue: 16
queue_timeout_ms: 15000
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
max_queue: 8
queue_timeout_ms: 15000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected conflict error for reversed legacy queue policy order")
}
if !strings.Contains(err.Error(), "conflicting provider queue policy") {
t.Fatalf("expected conflict error regardless of provider order, got: %v", err)
}
}
// TestLoadEdge_ProviderPoolPolicyDefaults verifies that with no canonical and
// no usable legacy values, defaults are used.
func TestLoadEdge_ProviderPoolPolicyDefaults(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != config.DefaultProviderPoolMaxQueue {
t.Errorf("expected default max_queue=%d, got %d", config.DefaultProviderPoolMaxQueue, cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != config.DefaultProviderPoolQueueTimeoutMS {
t.Errorf("expected default queue_timeout_ms=%d, got %d", config.DefaultProviderPoolQueueTimeoutMS, cfg.ProviderPool.QueueTimeoutMS)
}
}
// TestLoadEdge_ProviderPoolPolicyCanonicalOverridesLegacy verifies that when
// canonical is set, legacy per-provider values do not affect the effective policy.
func TestLoadEdge_ProviderPoolPolicyCanonicalOverridesLegacy(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
provider_pool:
max_queue: 10
queue_timeout_ms: 5000
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
max_queue: 99
queue_timeout_ms: 99000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != 10 {
t.Errorf("expected canonical max_queue=10 to override legacy, got %d", cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 5000 {
t.Errorf("expected canonical queue_timeout_ms=5000 to override legacy, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
}
// ---------- PROVIDER_POLICY_CONFIG: canonical negative rejection (G04 followup) ----------
// TestLoadEdge_ProviderPoolPolicyCanonicalNegativeRejected verifies S06 that
// negative max_queue and queue_timeout_ms are rejected before default
// normalization can mask them.
func TestLoadEdge_ProviderPoolPolicyCanonicalNegativeRejected(t *testing.T) {
t.Run("max_queue negative", func(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
provider_pool:
max_queue: -1
queue_timeout_ms: 30000
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for negative max_queue")
}
if !strings.Contains(err.Error(), "provider_pool.max_queue") || !strings.Contains(err.Error(), "non-negative") {
t.Fatalf("expected error to mention provider_pool.max_queue non-negative, got: %v", err)
}
})
t.Run("queue_timeout_ms negative", func(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
provider_pool:
max_queue: 10
queue_timeout_ms: -500
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(f)
if err == nil {
t.Fatal("expected error for negative queue_timeout_ms")
}
if !strings.Contains(err.Error(), "provider_pool.queue_timeout_ms") || !strings.Contains(err.Error(), "non-negative") {
t.Fatalf("expected error to mention provider_pool.queue_timeout_ms non-negative, got: %v", err)
}
})
}
// ---------- PROVIDER_POLICY_CONFIG: legacy partial fields and effective equality (G04 followup) ----------
// TestLoadEdge_ProviderPoolPolicyLegacyPartialFields verifies that legacy
// timeout-only and max-only pairs are normalized to effective (DefaultProviderPoolMaxQueue,
// timeout) and (max, 0) respectively before promotion.
func TestLoadEdge_ProviderPoolPolicyLegacyPartialFields(t *testing.T) {
t.Run("timeout-only promotes to default-max", func(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
queue_timeout_ms: 20000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != config.DefaultProviderPoolMaxQueue {
t.Errorf("expected normalized max_queue=%d, got %d", config.DefaultProviderPoolMaxQueue, cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 20000 {
t.Errorf("expected queue_timeout_ms=20000, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
})
t.Run("max-only preserves no-timeout", func(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
max_queue: 24
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.ProviderPool.MaxQueue != 24 {
t.Errorf("expected max_queue=24, got %d", cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 0 {
t.Errorf("expected no-timeout preserved (0), got %d", cfg.ProviderPool.QueueTimeoutMS)
}
})
}
// TestLoadEdge_ProviderPoolPolicyLegacyEffectiveEquality verifies that a
// timeout-only provider (max_queue=0) and a provider with max_queue=Default
// are treated as equal effective pairs and promoted without conflict.
func TestLoadEdge_ProviderPoolPolicyLegacyEffectiveEquality(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
yaml := `
server:
listen: "0.0.0.0:9090"
nodes:
- id: "node-01"
providers:
- id: "prov-a"
type: "ollama"
category: "local_inference"
models: ["model-a"]
capacity: 2
queue_timeout_ms: 20000
- id: "prov-b"
type: "ollama"
category: "local_inference"
models: ["model-b"]
capacity: 3
max_queue: 16
queue_timeout_ms: 20000
`
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(f)
if err != nil {
t.Fatalf("load: %v", err)
}
// Both normalize to (DefaultProviderPoolMaxQueue, 20000).
if cfg.ProviderPool.MaxQueue != config.DefaultProviderPoolMaxQueue {
t.Errorf("expected normalized max_queue=%d, got %d", config.DefaultProviderPoolMaxQueue, cfg.ProviderPool.MaxQueue)
}
if cfg.ProviderPool.QueueTimeoutMS != 20000 {
t.Errorf("expected queue_timeout_ms=20000, got %d", cfg.ProviderPool.QueueTimeoutMS)
}
}