iop/packages/go/config/model_execution_preset_config_test.go
toki 1ed718cadc fix(single_request): 명시된 null 템플릿 경로를 생략과 구분한다
`plan_file: null`과 `review_file: ~`은 YAML에 실제로 존재하는 키인데
mapstructure가 present nil을 생략과 같은 nil pointer로 접어버려, 승인 경계가
빈 문자열과 공백만 거부하고 null 형태는 조용히 built-in default로 떨어뜨렸다.
Viper가 돌려주는 raw preset 구조에는 두 키가 nil 값으로 남아 있으므로, strict
decode 전에 그 presence를 검사해 필드 경로와 함께 거부하고 실제로 없는 키만
fallback 신호로 남긴다.

두 필드와 두 가지 YAML null 표기를 모두 회귀로 고정하고, 기존 빈/공백, 생략,
상대 경로, clone 독립성, refresh redaction 근거는 그대로 유지한다.

Refs: agent-task/single_request_plan_review_templates/PLAN-cloud-G08.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 10:35:53 +09:00

1066 lines
36 KiB
Go

package config_test
import (
"os"
"path/filepath"
"strings"
"testing"
"iop/packages/go/config"
"iop/packages/go/singlerequesttemplate"
)
// TestLoadEdgeModelExecutionPresetOneOf covers the one-of admission rule for
// ModelCatalogEntry: exactly one of providers or execution_preset must be set,
// preset ids must resolve to an execution_presets[] entry, and existing
// provider-only fixtures must keep working unchanged.
func TestLoadEdgeModelExecutionPresetOneOf(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "edge.yaml")
// ---- Happy path: provider-only (existing behavior) ----
t.Run("provider-only entry loads unchanged", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
vllm-gpu: "nvidia/Qwen3.6-35B"
nodes:
- id: "node-gpu-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B"
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) != 1 {
t.Fatalf("expected 1 model, got %d", len(cfg.Models))
}
if cfg.Models[0].ExecutionPreset != "" {
t.Errorf("provider-only entry should not have execution_preset set, got %q", cfg.Models[0].ExecutionPreset)
}
if len(cfg.Models[0].Providers) != 1 {
t.Errorf("expected 1 provider, got %d", len(cfg.Models[0].Providers))
}
})
// ---- Happy path: preset-only (virtual model) ----
t.Run("preset-only entry loads as virtual model", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "virtual-model"
execution_preset: "fast-path"
execution_presets:
- id: "fast-path"
selector:
model: "virtual-model"
allowed_modes:
- "direct"
routes:
direct:
stages: []
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 len(cfg.Models) != 1 {
t.Fatalf("expected 1 model, got %d", len(cfg.Models))
}
m := cfg.Models[0]
if m.ID != "virtual-model" {
t.Errorf("model id = %q, want virtual-model", m.ID)
}
if m.ExecutionPreset != "fast-path" {
t.Errorf("execution_preset = %q, want fast-path", m.ExecutionPreset)
}
if len(m.Providers) != 0 {
t.Errorf("virtual model should have empty providers, got %v", m.Providers)
}
})
// ---- Error: both providers and execution_preset set ----
t.Run("both providers and execution_preset rejected", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "confused-model"
execution_preset: "fast-path"
providers:
prov-a: "model-a"
execution_presets:
- id: "fast-path"
selector:
model: "model-a"
allowed_modes:
- "direct"
routes:
direct:
stages: []
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 both providers and execution_preset set")
}
if !strings.Contains(err.Error(), "exactly one of providers or execution_preset must be set") {
t.Fatalf("expected one-of error, got %v", err)
}
})
// ---- Error: neither providers nor execution_preset ----
t.Run("neither providers nor execution_preset rejected", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "empty-model"
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 neither providers nor execution_preset")
}
if !strings.Contains(err.Error(), "providers must not be empty") {
t.Fatalf("expected providers must not be empty error, got %v", err)
}
})
// ---- Error: dangling execution_preset id ----
t.Run("dangling execution_preset id rejected", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "dangling-model"
execution_preset: "non-existent-preset"
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 dangling execution_preset id")
}
if !strings.Contains(err.Error(), "execution_preset") && !strings.Contains(err.Error(), "does not match any execution_presets") {
t.Fatalf("expected dangling preset error, got %v", err)
}
})
// ---- Compatibility: mixed catalog with both provider-only and preset-only ----
t.Run("mixed catalog with provider-only and preset-only entries", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
vllm-gpu: "nvidia/Qwen3.6-35B"
- id: "virtual-light"
execution_preset: "review-path"
execution_presets:
- id: "review-path"
selector:
model: "qwen3.6:35b"
allowed_modes:
- "light"
routes:
light:
stages:
- role: "local"
model: "qwen3.6:35b"
- role: "review"
model: "qwen3.6:35b"
workspace_tools:
- name: "ws1"
operations:
read:
tool_name: "cat"
schema_matcher:
type: "object"
argument_map:
path: "path"
result_matcher:
status: "ok"
write:
tool_name: "tee"
creates_parents: true
schema_matcher:
type: "object"
argument_map:
path: "path"
result_matcher:
status: "ok"
delete:
tool_name: "rm"
schema_matcher:
type: "object"
argument_map:
path: "path"
result_matcher:
status: "ok"
nodes:
- id: "node-gpu-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B"
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) != 2 {
t.Fatalf("expected 2 models, got %d", len(cfg.Models))
}
byID := map[string]config.ModelCatalogEntry{}
for _, m := range cfg.Models {
byID[m.ID] = m
}
// Provider-only entry should be unchanged.
provModel := byID["qwen3.6:35b"]
if len(provModel.Providers) != 1 {
t.Errorf("provider-only model should have 1 provider, got %d", len(provModel.Providers))
}
if provModel.ExecutionPreset != "" {
t.Errorf("provider-only model should not have execution_preset, got %q", provModel.ExecutionPreset)
}
// Virtual entry should reference the preset.
virtualModel := byID["virtual-light"]
if virtualModel.ExecutionPreset != "review-path" {
t.Errorf("virtual model execution_preset = %q, want review-path", virtualModel.ExecutionPreset)
}
if len(virtualModel.Providers) != 0 {
t.Errorf("virtual model should have empty providers, got %v", virtualModel.Providers)
}
})
// ---- Compatibility: provider-only fixture with budget must still validate ----
t.Run("provider-only entry with insufficient budget still rejected", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
context_window_tokens: 262144
providers:
vllm-gpu: "nvidia/Qwen3.6-35B"
nodes:
- id: "node-gpu-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B"
capacity: 4
total_context_tokens: 262144
long_context_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 insufficient long-context budget on provider-only entry")
}
if !strings.Contains(err.Error(), "total_context_tokens") {
t.Fatalf("expected budget error, got %v", err)
}
})
// ---- Edge: whitespace-only execution_preset treated as unset ----
t.Run("whitespace-only execution_preset treated as unset", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "whitespace-model"
execution_preset: " "
providers:
prov-a: "model-a"
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 len(cfg.Models) != 1 {
t.Fatalf("expected 1 model, got %d", len(cfg.Models))
}
// Whitespace-only preset should be treated as unset, so provider-only
// path should apply.
if cfg.Models[0].ExecutionPreset != "" {
t.Errorf("whitespace preset should be treated as unset, got %q", cfg.Models[0].ExecutionPreset)
}
})
// ---- Normalization: non-empty execution_preset is stored canonically ----
t.Run("non-empty execution_preset is normalized", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
vllm-gpu: "nvidia/Qwen3.6-35B"
- id: "virtual-fast"
execution_preset: " fast-path "
execution_presets:
- id: "fast-path"
selector:
model: "qwen3.6:35b"
allowed_modes:
- "direct"
routes:
direct:
stages: []
nodes:
- id: "node-gpu-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B"
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)
}
byID := map[string]config.ModelCatalogEntry{}
for _, m := range cfg.Models {
byID[m.ID] = m
}
// The padded valid preset id must be persisted in canonical (trimmed)
// form so exact downstream lookups match the admitted value.
virtual := byID["virtual-fast"]
if virtual.ExecutionPreset != "fast-path" {
t.Errorf("execution_preset = %q, want canonical %q", virtual.ExecutionPreset, "fast-path")
}
if len(virtual.Providers) != 0 {
t.Errorf("virtual model should have empty providers, got %v", virtual.Providers)
}
// Provider-only entry stays unchanged.
prov := byID["qwen3.6:35b"]
if prov.ExecutionPreset != "" {
t.Errorf("provider-only entry should not have execution_preset set, got %q", prov.ExecutionPreset)
}
if len(prov.Providers) != 1 {
t.Errorf("provider-only entry should have 1 provider, got %d", len(prov.Providers))
}
})
// ---- Error: empty execution_preset string with no providers ----
t.Run("explicit empty execution_preset with no providers rejected", func(t *testing.T) {
yaml := `
server:
listen: "0.0.0.0:9090"
models:
- id: "empty-preset-model"
execution_preset: ""
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 empty execution_preset with no providers")
}
if !strings.Contains(err.Error(), "providers must not be empty") {
t.Fatalf("expected providers must not be empty error, got %v", err)
}
})
}
// TestModelCatalogEntry_ValidateVirtualEntryUnit covers unit-level Validate
// behavior for the one-of rule without going through LoadEdge.
func TestModelCatalogEntry_ValidateVirtualEntryUnit(t *testing.T) {
providerIDs := map[string]struct{}{
"vllm-gpu": {},
}
serveModels := map[string]map[string]struct{}{
"vllm-gpu": {"model-a": {}},
}
t.Run("provider-only validates", func(t *testing.T) {
e := config.ModelCatalogEntry{
ID: "model-a",
Providers: map[string]string{"vllm-gpu": "model-a"},
}
if err := e.Validate(providerIDs, serveModels); err != nil {
t.Fatalf("expected no error, got: %v", err)
}
})
t.Run("preset-only validates (returns nil, preset resolved later)", func(t *testing.T) {
e := config.ModelCatalogEntry{
ID: "virtual-model",
ExecutionPreset: "fast-path",
}
if err := e.Validate(providerIDs, serveModels); err != nil {
t.Fatalf("expected no error for preset-only, got: %v", err)
}
})
t.Run("both providers and execution_preset rejected", func(t *testing.T) {
e := config.ModelCatalogEntry{
ID: "bad-model",
Providers: map[string]string{"vllm-gpu": "model-a"},
ExecutionPreset: "fast-path",
}
if err := e.Validate(providerIDs, serveModels); err == nil {
t.Fatal("expected error for both set")
}
})
t.Run("neither providers nor execution_preset rejected", func(t *testing.T) {
e := config.ModelCatalogEntry{
ID: "empty-model",
}
if err := e.Validate(providerIDs, serveModels); err == nil {
t.Fatal("expected error for neither set")
}
})
t.Run("whitespace execution_preset treated as unset", func(t *testing.T) {
e := config.ModelCatalogEntry{
ID: "ws-model",
Providers: map[string]string{"vllm-gpu": "model-a"},
ExecutionPreset: " ",
}
if err := e.Validate(providerIDs, serveModels); err != nil {
t.Fatalf("expected no error (whitespace preset treated as unset), got: %v", err)
}
})
}
// customPlanTemplate and customReviewTemplate are valid operator-authored
// templates used to prove config-relative loading, per-file fallback, and
// filesystem-kind boundaries without depending on the built-in defaults.
const (
customPlanTemplate = "# Plan\n\n## Goal\n{{goal}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"
customReviewTemplate = "# Review\n\n## Result\nPASS\n\n## Checks\n{{checks}}\n\n## Verification\n{{verification}}\n\n## Summary\n{{summary}}\n"
)
func TestLoadEdgeSingleRequestTemplates(t *testing.T) {
validPresetYAML := func(planFile, reviewFile string) string {
tmplSection := ""
if planFile != "" || reviewFile != "" {
tmplSection = " templates:\n"
if planFile != "" {
tmplSection += " plan_file: " + planFile + "\n"
}
if reviewFile != "" {
tmplSection += " review_file: " + reviewFile + "\n"
}
}
return `
server:
listen: "0.0.0.0:9090"
models:
- id: "qwen3.6:35b"
providers:
vllm-gpu: "nvidia/Qwen3.6-35B"
- id: "virtual-sr"
execution_preset: "sr-preset"
execution_presets:
- id: "sr-preset"
selector:
model: "qwen3.6:35b"
options:
reasoning_effort: "high"
allowed_modes:
- "light"
routes:
light:
stages:
- role: "plan"
model: "qwen3.6:35b"
options:
reasoning_effort: "high"
- role: "work"
model: "qwen3.6:35b"
- role: "review"
model: "qwen3.6:35b"
options:
reasoning_effort: "high"
single_request:
workspace_ref: "ws-1"
limits:
wall_clock_ms: 60000
timeout_ms: 30000
max_tool_iterations: 10
max_output_bytes: 1024
stages:
plan:
model: "qwen3.6:35b"
options:
reasoning_effort: "high"
work:
model: "qwen3.6:35b"
review:
model: "qwen3.6:35b"
options:
reasoning_effort: "high"
` + tmplSection + `
nodes:
- id: "node-gpu-01"
providers:
- id: "vllm-gpu"
type: "vllm"
category: "api"
models:
- "nvidia/Qwen3.6-35B"
capacity: 4
`
}
t.Run("built-in template fallback when templates omitted", func(t *testing.T) {
rootDir := t.TempDir()
cfgSubdir := filepath.Join(rootDir, "sub", "config")
if err := os.MkdirAll(cfgSubdir, 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
cfgPath := filepath.Join(cfgSubdir, "edge.yaml")
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(cfgPath)
if err != nil {
t.Fatalf("LoadEdge failed: %v", err)
}
sr := cfg.ExecutionPresets[0].SingleRequest
if sr.Templates.EffectivePlan == "" || sr.Templates.EffectiveReview == "" {
t.Errorf("expected effective templates to be set to built-ins")
}
if sr.Templates.PlanFile != nil {
t.Errorf("PlanFile = %q, want nil for an omitted field", *sr.Templates.PlanFile)
}
if sr.Templates.ReviewFile != nil {
t.Errorf("ReviewFile = %q, want nil for an omitted field", *sr.Templates.ReviewFile)
}
})
t.Run("relative path template resolution config-relative", func(t *testing.T) {
rootDir := t.TempDir()
cfgSubdir := filepath.Join(rootDir, "sub", "config")
tmplSubdir := filepath.Join(cfgSubdir, "tmpl")
if err := os.MkdirAll(tmplSubdir, 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
cfgPath := filepath.Join(cfgSubdir, "edge.yaml")
customPlan := "# Plan\n\n## Goal\n{{goal}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"
customReview := "# Review\n\n## Result\nPASS\n\n## Checks\n{{checks}}\n\n## Verification\n{{verification}}\n\n## Summary\n{{summary}}\n"
if err := os.WriteFile(filepath.Join(tmplSubdir, "custom_plan.md"), []byte(customPlan), 0o600); err != nil {
t.Fatalf("write custom plan: %v", err)
}
if err := os.WriteFile(filepath.Join(tmplSubdir, "custom_review.md"), []byte(customReview), 0o600); err != nil {
t.Fatalf("write custom review: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("tmpl/custom_plan.md", "tmpl/custom_review.md")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(cfgPath)
if err != nil {
t.Fatalf("LoadEdge failed: %v", err)
}
sr := cfg.ExecutionPresets[0].SingleRequest
if sr.Templates.EffectivePlan != customPlan {
t.Errorf("EffectivePlan = %q, want %q", sr.Templates.EffectivePlan, customPlan)
}
if sr.Templates.EffectiveReview != customReview {
t.Errorf("EffectiveReview = %q, want %q", sr.Templates.EffectiveReview, customReview)
}
if sr.Templates.PlanFile == nil || *sr.Templates.PlanFile != "tmpl/custom_plan.md" {
t.Errorf("PlanFile = %v, want the configured relative path", sr.Templates.PlanFile)
}
if sr.Templates.ReviewFile == nil || *sr.Templates.ReviewFile != "tmpl/custom_review.md" {
t.Errorf("ReviewFile = %v, want the configured relative path", sr.Templates.ReviewFile)
}
})
t.Run("missing configured template file fails closed", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("nonexistent_plan.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for missing plan template file")
}
})
t.Run("oversized template file 8193 bytes fails closed", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
oversizedPlan := "# Plan\n\n## Goal\n{{goal}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n" + strings.Repeat(" ", 8193)
if err := os.WriteFile(filepath.Join(rootDir, "oversized.md"), []byte(oversizedPlan), 0o600); err != nil {
t.Fatalf("write oversized plan: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("oversized.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for oversized template file")
}
})
t.Run("invalid template content missing tokens fails closed", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
badPlan := "# Plan\n\n## Goal\nNo placeholders here\n"
if err := os.WriteFile(filepath.Join(rootDir, "bad_plan.md"), []byte(badPlan), 0o600); err != nil {
t.Fatalf("write bad plan: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("bad_plan.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for invalid template content")
}
})
// The config contract is relative-only: an absolute plan_file/review_file is
// rejected before any filesystem access, so a readable, otherwise valid
// template outside the edge.yaml directory must still fail closed.
t.Run("absolute plan_file rejected before filesystem access", func(t *testing.T) {
rootDir := t.TempDir()
outsideDir := t.TempDir()
absolutePlan := filepath.Join(outsideDir, "plan.md")
if err := os.WriteFile(absolutePlan, []byte(customPlanTemplate), 0o600); err != nil {
t.Fatalf("write absolute plan: %v", err)
}
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(cfgPath, []byte(validPresetYAML(absolutePlan, "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for absolute plan_file %q", absolutePlan)
}
if !strings.Contains(err.Error(), "must be relative") {
t.Fatalf("expected relative-path rejection, got: %v", err)
}
})
t.Run("absolute review_file rejected before filesystem access", func(t *testing.T) {
rootDir := t.TempDir()
outsideDir := t.TempDir()
absoluteReview := filepath.Join(outsideDir, "review.md")
if err := os.WriteFile(absoluteReview, []byte(customReviewTemplate), 0o600); err != nil {
t.Fatalf("write absolute review: %v", err)
}
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("", absoluteReview)), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for absolute review_file %q", absoluteReview)
}
if !strings.Contains(err.Error(), "must be relative") {
t.Fatalf("expected relative-path rejection, got: %v", err)
}
})
// plan_file and review_file fall back independently: configuring one file
// must not replace the other with a loaded template or drop its default.
t.Run("plan_file configured and review_file falls back", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(filepath.Join(rootDir, "plan.md"), []byte(customPlanTemplate), 0o600); err != nil {
t.Fatalf("write plan: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("plan.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(cfgPath)
if err != nil {
t.Fatalf("LoadEdge failed: %v", err)
}
sr := cfg.ExecutionPresets[0].SingleRequest
if sr.Templates.EffectivePlan != customPlanTemplate {
t.Errorf("EffectivePlan = %q, want the configured custom template", sr.Templates.EffectivePlan)
}
if sr.Templates.EffectiveReview != singlerequesttemplate.DefaultReviewTemplate {
t.Errorf("EffectiveReview = %q, want the built-in default", sr.Templates.EffectiveReview)
}
if sr.Templates.PlanFile == nil || *sr.Templates.PlanFile != "plan.md" {
t.Errorf("PlanFile = %v, want the configured relative path", sr.Templates.PlanFile)
}
if sr.Templates.ReviewFile != nil {
t.Errorf("ReviewFile = %q, want nil for an omitted field", *sr.Templates.ReviewFile)
}
})
t.Run("review_file configured and plan_file falls back", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(filepath.Join(rootDir, "review.md"), []byte(customReviewTemplate), 0o600); err != nil {
t.Fatalf("write review: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("", "review.md")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(cfgPath)
if err != nil {
t.Fatalf("LoadEdge failed: %v", err)
}
sr := cfg.ExecutionPresets[0].SingleRequest
if sr.Templates.EffectiveReview != customReviewTemplate {
t.Errorf("EffectiveReview = %q, want the configured custom template", sr.Templates.EffectiveReview)
}
if sr.Templates.EffectivePlan != singlerequesttemplate.DefaultPlanTemplate {
t.Errorf("EffectivePlan = %q, want the built-in default", sr.Templates.EffectivePlan)
}
if sr.Templates.ReviewFile == nil || *sr.Templates.ReviewFile != "review.md" {
t.Errorf("ReviewFile = %v, want the configured relative path", sr.Templates.ReviewFile)
}
if sr.Templates.PlanFile != nil {
t.Errorf("PlanFile = %q, want nil for an omitted field", *sr.Templates.PlanFile)
}
})
t.Run("exact 8192 byte template file accepted", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
exactPlan := customPlanTemplate + strings.Repeat(" ", singlerequesttemplate.MaxTemplateBytes-len(customPlanTemplate))
if len(exactPlan) != singlerequesttemplate.MaxTemplateBytes {
t.Fatalf("fixture size = %d, want %d", len(exactPlan), singlerequesttemplate.MaxTemplateBytes)
}
if err := os.WriteFile(filepath.Join(rootDir, "exact.md"), []byte(exactPlan), 0o600); err != nil {
t.Fatalf("write exact plan: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("exact.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
cfg, err := config.LoadEdge(cfgPath)
if err != nil {
t.Fatalf("LoadEdge failed at the exact size boundary: %v", err)
}
if cfg.ExecutionPresets[0].SingleRequest.Templates.EffectivePlan != exactPlan {
t.Errorf("EffectivePlan did not match the exact-size fixture")
}
})
t.Run("symlinked template file fails closed", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
realPlan := filepath.Join(rootDir, "real_plan.md")
if err := os.WriteFile(realPlan, []byte(customPlanTemplate), 0o600); err != nil {
t.Fatalf("write real plan: %v", err)
}
if err := os.Symlink(realPlan, filepath.Join(rootDir, "link_plan.md")); err != nil {
t.Skipf("symlink unsupported in this environment: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("link_plan.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for symlinked template file")
}
if !strings.Contains(err.Error(), "regular file") {
t.Fatalf("expected non-regular-file rejection, got: %v", err)
}
})
t.Run("directory template path fails closed", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.MkdirAll(filepath.Join(rootDir, "plan_dir"), 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("plan_dir", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for directory template path")
}
if !strings.Contains(err.Error(), "regular file") {
t.Fatalf("expected non-regular-file rejection, got: %v", err)
}
})
t.Run("unreadable template file fails closed", func(t *testing.T) {
if os.Geteuid() == 0 {
t.Skip("running as root: mode bits do not deny read access")
}
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
unreadable := filepath.Join(rootDir, "unreadable.md")
if err := os.WriteFile(unreadable, []byte(customPlanTemplate), 0o600); err != nil {
t.Fatalf("write unreadable plan: %v", err)
}
if err := os.Chmod(unreadable, 0o000); err != nil {
t.Fatalf("chmod: %v", err)
}
t.Cleanup(func() { _ = os.Chmod(unreadable, 0o600) })
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("unreadable.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
if _, err := config.LoadEdge(cfgPath); err == nil {
t.Fatalf("expected error for unreadable template file")
}
})
t.Run("non-UTF-8 template file fails closed", func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
invalid := append([]byte(customPlanTemplate), 0xff, 0xfe)
if err := os.WriteFile(filepath.Join(rootDir, "invalid_utf8.md"), invalid, 0o600); err != nil {
t.Fatalf("write invalid utf8 plan: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML("invalid_utf8.md", "")), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for non-UTF-8 template file")
}
if !strings.Contains(err.Error(), "UTF-8") {
t.Fatalf("expected UTF-8 rejection, got: %v", err)
}
})
// Invalid grammar must fail closed for both files, not only plan_file.
t.Run("invalid grammar matrix fails closed", func(t *testing.T) {
cases := []struct {
name string
planFile string
reviewFile string
content string
}{
{"plan missing heading", "bad.md", "", "## Goal\n{{goal}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"},
{"plan decorated heading", "bad.md", "", "### Plan\n\n## Goal\n{{goal}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"},
{"plan unknown placeholder", "bad.md", "", "# Plan\n\n## Goal\n{{goal}} {{owner}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"},
{"plan delimiter residue", "bad.md", "", "# Plan\n\n## Goal\n{{goal}}}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"},
{"plan duplicate placeholder", "bad.md", "", "# Plan\n\n## Goal\n{{goal}} {{goal}}\n\n## Steps\n{{steps}}\n\n## Verification\n{{verification}}\n"},
{"review missing PASS", "", "bad.md", "# Review\n\n## Result\nFAIL\n\n## Checks\n{{checks}}\n\n## Verification\n{{verification}}\n\n## Summary\n{{summary}}\n"},
{"review NOTPASS", "", "bad.md", "# Review\n\n## Result\nNOTPASS\n\n## Checks\n{{checks}}\n\n## Verification\n{{verification}}\n\n## Summary\n{{summary}}\n"},
{"review decorated heading", "", "bad.md", "### Review\n\n## Result\nPASS\n\n## Checks\n{{checks}}\n\n## Verification\n{{verification}}\n\n## Summary\n{{summary}}\n"},
{"review missing placeholder", "", "bad.md", "# Review\n\n## Result\nPASS\n\n## Checks\n{{checks}}\n\n## Verification\n{{verification}}\n\n## Summary\n"},
{"review unknown placeholder", "", "bad.md", "# Review\n\n## Result\nPASS\n\n## Checks\n{{checks}} {{severity}}\n\n## Verification\n{{verification}}\n\n## Summary\n{{summary}}\n"},
{"review wrong order", "", "bad.md", "# Review\n\n## Result\nPASS\n\n## Verification\n{{verification}}\n\n## Checks\n{{checks}}\n\n## Summary\n{{summary}}\n"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(filepath.Join(rootDir, "bad.md"), []byte(tc.content), 0o600); err != nil {
t.Fatalf("write template: %v", err)
}
if err := os.WriteFile(cfgPath, []byte(validPresetYAML(tc.planFile, tc.reviewFile)), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
if _, err := config.LoadEdge(cfgPath); err == nil {
t.Fatalf("expected error for %s", tc.name)
}
})
}
})
// Only an omitted field selects the built-in default. An explicitly
// configured empty, whitespace-only, or null path is a configuration error
// and must be rejected with its own field context before the loader touches
// the filesystem. Both YAML null spellings are covered because a present
// null decodes into the same nil pointer as an absent key, so the rejection
// has to happen on the raw preset structure before that presence is erased.
t.Run("explicitly configured empty template path fails closed", func(t *testing.T) {
cases := []struct {
name string
planFile string
reviewFile string
field string
}{
{"empty plan_file", `""`, "", "single_request.templates.plan_file"},
{"whitespace plan_file", `" "`, "", "single_request.templates.plan_file"},
{"null plan_file", "null", "", "single_request.templates.plan_file"},
{"tilde plan_file", "~", "", "single_request.templates.plan_file"},
{"empty review_file", "", `""`, "single_request.templates.review_file"},
{"whitespace review_file", "", `" "`, "single_request.templates.review_file"},
{"null review_file", "", "null", "single_request.templates.review_file"},
{"tilde review_file", "", "~", "single_request.templates.review_file"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rootDir := t.TempDir()
cfgPath := filepath.Join(rootDir, "edge.yaml")
if err := os.WriteFile(cfgPath, []byte(validPresetYAML(tc.planFile, tc.reviewFile)), 0o600); err != nil {
t.Fatalf("write yaml: %v", err)
}
_, err := config.LoadEdge(cfgPath)
if err == nil {
t.Fatalf("expected error for %s", tc.name)
}
if !strings.Contains(err.Error(), tc.field) {
t.Errorf("expected %s context, got: %v", tc.field, err)
}
if !strings.Contains(err.Error(), "template path must not be empty") {
t.Errorf("expected empty-path rejection, got: %v", err)
}
if strings.Contains(err.Error(), rootDir) {
t.Errorf("rejection reached the filesystem: %v", err)
}
})
}
})
}
// TestExecutionSingleRequestTemplatePathPresenceClone proves the optional path
// cells survive Clone as independent values: mutating a clone must not reach
// the source policy, and the frozen effective templates must be unaffected.
func TestExecutionSingleRequestTemplatePathPresenceClone(t *testing.T) {
planPath := "tmpl/plan.md"
reviewPath := "tmpl/review.md"
original := &config.ExecutionSingleRequestPolicy{
WorkspaceRef: "ws-1",
Templates: config.ExecutionSingleRequestTemplates{
PlanFile: &planPath,
ReviewFile: &reviewPath,
EffectivePlan: customPlanTemplate,
EffectiveReview: customReviewTemplate,
},
}
clone := original.Clone()
if clone.Templates.PlanFile == nil || clone.Templates.ReviewFile == nil {
t.Fatalf("clone dropped configured template paths: %+v", clone.Templates)
}
if clone.Templates.PlanFile == original.Templates.PlanFile {
t.Errorf("clone aliases the plan_file pointer cell")
}
if clone.Templates.ReviewFile == original.Templates.ReviewFile {
t.Errorf("clone aliases the review_file pointer cell")
}
*clone.Templates.PlanFile = "tmpl/other_plan.md"
*clone.Templates.ReviewFile = "tmpl/other_review.md"
clone.Templates.EffectivePlan = "mutated plan"
if *original.Templates.PlanFile != planPath {
t.Errorf("original PlanFile = %q, want %q", *original.Templates.PlanFile, planPath)
}
if *original.Templates.ReviewFile != reviewPath {
t.Errorf("original ReviewFile = %q, want %q", *original.Templates.ReviewFile, reviewPath)
}
if original.Templates.EffectivePlan != customPlanTemplate {
t.Errorf("original EffectivePlan changed: %q", original.Templates.EffectivePlan)
}
if original.Templates.EffectiveReview != customReviewTemplate {
t.Errorf("original EffectiveReview changed: %q", original.Templates.EffectiveReview)
}
// A nil path must clone as omission, never as a configured empty value.
omitted := &config.ExecutionSingleRequestPolicy{}
omittedClone := omitted.Clone()
if omittedClone.Templates.PlanFile != nil || omittedClone.Templates.ReviewFile != nil {
t.Errorf("clone materialized omitted paths: %+v", omittedClone.Templates)
}
}