package config_test import ( "os" "path/filepath" "strings" "testing" "iop/packages/go/config" ) func TestLoadEdge_ConsoleTimeoutDefault(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") if err := os.WriteFile(f, []byte("server:\n listen: \"0.0.0.0:9090\"\n"), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } if cfg.Console.TimeoutSec != 120 { t.Fatalf("expected default timeout_sec=120, got %d", cfg.Console.TimeoutSec) } if cfg.Console.Adapter != "cli" { t.Fatalf("expected default console.adapter=%q, got %q", "cli", cfg.Console.Adapter) } if cfg.Console.ResolveTarget() != "claude" { t.Fatalf("expected default console.target=%q, got %q", "claude", cfg.Console.ResolveTarget()) } } func TestLoadEdge_ConsoleSessionDefaults(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") if err := os.WriteFile(f, []byte("server:\n listen: \"0.0.0.0:9090\"\n"), 0o600); err != nil { t.Fatalf("write yaml: %v", err) } cfg, err := config.LoadEdge(f) if err != nil { t.Fatalf("load: %v", err) } if cfg.Console.SessionID != "default" { t.Fatalf("expected default session_id=%q, got %q", "default", cfg.Console.SessionID) } if cfg.Console.Background { t.Fatal("expected default background=false") } } func TestLoadEdge_ConsoleSessionOverride(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := "server:\n listen: \"0.0.0.0:9090\"\nconsole:\n session_id: \"worker-1\"\n background: true\n" 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.Console.SessionID != "worker-1" { t.Fatalf("expected session_id=%q, got %q", "worker-1", cfg.Console.SessionID) } if !cfg.Console.Background { t.Fatal("expected background=true") } } func TestLoadEdge_ConsoleTimeoutOverride(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := "server:\n listen: \"0.0.0.0:9090\"\nconsole:\n timeout_sec: 45\n" 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.Console.TimeoutSec != 45 { t.Fatalf("expected timeout_sec=45, got %d", cfg.Console.TimeoutSec) } } func TestLoadEdge_ConsoleTargetOverride(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := "server:\n listen: \"0.0.0.0:9090\"\nconsole:\n target: \"codex\"\n" 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.Console.ResolveTarget() != "codex" { t.Fatalf("expected console.target to resolve to %q, got %q", "codex", cfg.Console.ResolveTarget()) } } func TestLoadEdge_ConsoleTargetFallbackFromLegacyAgent(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := "server:\n listen: \"0.0.0.0:9090\"\nconsole:\n agent: \"codex\"\n" 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.Console.ResolveTarget() != "codex" { t.Fatalf("expected legacy console.agent fallback to resolve to %q, got %q", "codex", cfg.Console.ResolveTarget()) } } func TestLoadEdge_ConsoleTargetFallbackFromLegacyModel(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := "server:\n listen: \"0.0.0.0:9090\"\nconsole:\n model: \"codex\"\n" 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.Console.ResolveTarget() != "codex" { t.Fatalf("expected legacy console.model fallback to resolve to %q, got %q", "codex", cfg.Console.ResolveTarget()) } } func TestLoadEdge_CodexAppServerDefaultProfile(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: codex: command: "codex" args: - "app-server" mode: "codex-app-server" codex-exec: command: "codex" args: - "exec" - "--json" resume_args: - "exec" - "resume" - "--json" output_format: "codex-json" mode: "codex-exec" ` 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") } profiles := cfg.Nodes[0].Adapters.CLI.Profiles codex, ok := profiles["codex"] if !ok { t.Fatal("expected codex profile") } if codex.Mode != "codex-app-server" { t.Errorf("codex.Mode = %q, want %q", codex.Mode, "codex-app-server") } if len(codex.Args) != 1 || codex.Args[0] != "app-server" { t.Errorf("codex.Args = %v, want [app-server]", codex.Args) } exec, ok := profiles["codex-exec"] if !ok { t.Fatal("expected codex-exec profile") } if exec.Mode != "codex-exec" { t.Errorf("codex-exec.Mode = %q, want %q", exec.Mode, "codex-exec") } if exec.OutputFormat != "codex-json" { t.Errorf("codex-exec.OutputFormat = %q, want %q", exec.OutputFormat, "codex-json") } expectedResume := []string{"exec", "resume", "--json"} if len(exec.ResumeArgs) != len(expectedResume) { t.Fatalf("codex-exec.ResumeArgs = %v, want %v", exec.ResumeArgs, expectedResume) } for i, v := range expectedResume { if exec.ResumeArgs[i] != v { t.Errorf("codex-exec.ResumeArgs[%d] = %q, want %q", i, exec.ResumeArgs[i], v) } } } func TestLoadEdge_CodexProfile(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: codex: command: "codex" output_format: "codex-json" ` 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") } codex, ok := cfg.Nodes[0].Adapters.CLI.Profiles["codex"] if !ok { t.Fatal("expected codex profile") } if codex.Command != "codex" || codex.OutputFormat != "codex-json" { t.Errorf("unexpected codex profile: %+v", codex) } } func TestLoadEdge_AntigravityProfile(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: antigravity: command: "agy" args: - "--dangerously-skip-permissions" - "--print-timeout" - "10m" - "--print" resume_args: - "--dangerously-skip-permissions" - "--print-timeout" - "10m" - "--conversation" mode: "antigravity-print" ` 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") } agy, ok := cfg.Nodes[0].Adapters.CLI.Profiles["antigravity"] if !ok { t.Fatal("expected antigravity profile") } if agy.Command != "agy" { t.Errorf("expected command agy, got %q", agy.Command) } expectedArgs := []string{"--dangerously-skip-permissions", "--print-timeout", "10m", "--print"} if len(agy.Args) != len(expectedArgs) { t.Fatalf("expected %d args, got %d: %v", len(expectedArgs), len(agy.Args), agy.Args) } for i, v := range expectedArgs { if agy.Args[i] != v { t.Errorf("args[%d]: expected %q, got %q", i, v, agy.Args[i]) } } if agy.OutputFormat != "" { t.Errorf("expected empty output format, got %q", agy.OutputFormat) } if agy.Mode != "antigravity-print" { t.Errorf("expected mode antigravity-print, got %q", agy.Mode) } expectedResume := []string{"--dangerously-skip-permissions", "--print-timeout", "10m", "--conversation"} if len(agy.ResumeArgs) != len(expectedResume) { t.Fatalf("expected %d resume_args, got %d: %v", len(expectedResume), len(agy.ResumeArgs), agy.ResumeArgs) } for i, v := range expectedResume { if agy.ResumeArgs[i] != v { t.Errorf("resume_args[%d]: expected %q, got %q", i, v, agy.ResumeArgs[i]) } } } func TestCLIProfileConf_CompletionMarkerUnmarshal(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: marker-test: command: "mycli" completion_marker: line: "<>" regex: "^DONE \\d+$" ` 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") } profile, ok := cfg.Nodes[0].Adapters.CLI.Profiles["marker-test"] if !ok { t.Fatal("expected marker-test profile") } if profile.CompletionMarker.Line != "<>" { t.Errorf("expected Line=%q, got %q", "<>", profile.CompletionMarker.Line) } if profile.CompletionMarker.Regex != "^DONE \\d+$" { t.Errorf("expected Regex=%q, got %q", "^DONE \\d+$", profile.CompletionMarker.Regex) } } func TestCompletionMarkerConf_Empty(t *testing.T) { var empty config.CompletionMarkerConf if !empty.Empty() { t.Fatal("expected Empty() == true for zero value") } lineOnly := config.CompletionMarkerConf{Line: "<>"} if lineOnly.Empty() { t.Fatal("expected Empty() == false when Line is set") } regexOnly := config.CompletionMarkerConf{Regex: "^END$"} if regexOnly.Empty() { t.Fatal("expected Empty() == false when Regex is set") } bothSet := config.CompletionMarkerConf{Line: "<>", Regex: "^END$"} if bothSet.Empty() { t.Fatal("expected Empty() == false when both are set") } } func TestCompletionMarkerConf_LineOnlyUnmarshal(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: line-only: command: "mycli" completion_marker: line: "<>" ` 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) } profile, ok := cfg.Nodes[0].Adapters.CLI.Profiles["line-only"] if !ok { t.Fatal("expected line-only profile") } if profile.CompletionMarker.Line != "<>" { t.Errorf("expected Line=%q, got %q", "<>", profile.CompletionMarker.Line) } if profile.CompletionMarker.Regex != "" { t.Errorf("expected empty Regex, got %q", profile.CompletionMarker.Regex) } if profile.CompletionMarker.Empty() { t.Fatal("expected Empty() == false when only Line is set") } } func TestCompletionMarkerConf_RegexOnlyUnmarshal(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: regex-only: command: "mycli" completion_marker: regex: "^DONE \\d+$" ` 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) } profile, ok := cfg.Nodes[0].Adapters.CLI.Profiles["regex-only"] if !ok { t.Fatal("expected regex-only profile") } if profile.CompletionMarker.Regex != "^DONE \\d+$" { t.Errorf("expected Regex=%q, got %q", "^DONE \\d+$", profile.CompletionMarker.Regex) } if profile.CompletionMarker.Line != "" { t.Errorf("expected empty Line, got %q", profile.CompletionMarker.Line) } if profile.CompletionMarker.Empty() { t.Fatal("expected Empty() == false when only Regex is set") } } func TestLoadEdge_CLIProfileMode(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: codex: command: "codex" args: - "exec" - "--json" resume_args: - "exec" - "resume" - "--json" mode: "codex-exec" ` 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") } profile, ok := cfg.Nodes[0].Adapters.CLI.Profiles["codex"] if !ok { t.Fatal("expected codex profile") } if profile.Mode != "codex-exec" { t.Errorf("expected mode=%q, got %q", "codex-exec", profile.Mode) } expectedResume := []string{"exec", "resume", "--json"} if len(profile.ResumeArgs) != len(expectedResume) { t.Fatalf("expected %d resume_args, got %d: %v", len(expectedResume), len(profile.ResumeArgs), profile.ResumeArgs) } for i, v := range expectedResume { if profile.ResumeArgs[i] != v { t.Errorf("resume_args[%d]: expected %q, got %q", i, v, profile.ResumeArgs[i]) } } } func TestLoadEdge_OpencodeSSEProfile(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: opencode: command: "/usr/local/bin/opencode" args: - "--title" - "untitle" - "--model" - "ollama-dgx/qwen3.6:35b-a3b-bf16" - "--dangerously-skip-permissions" mode: "opencode-sse" ` 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") } prof, ok := cfg.Nodes[0].Adapters.CLI.Profiles["opencode"] if !ok { t.Fatal("expected opencode profile") } if prof.Mode != "opencode-sse" { t.Errorf("mode: got %q", prof.Mode) } expectedArgs := []string{"--title", "untitle", "--model", "ollama-dgx/qwen3.6:35b-a3b-bf16", "--dangerously-skip-permissions"} if len(prof.Args) != len(expectedArgs) { t.Fatalf("args len: got %d, want %d", len(prof.Args), len(expectedArgs)) } for i, want := range expectedArgs { if prof.Args[i] != want { t.Errorf("args[%d]: got %q, want %q", i, prof.Args[i], want) } } } func TestCLIProfileConf_CompletionMarkerEmpty(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "edge.yaml") yaml := ` server: listen: "0.0.0.0:9090" nodes: - alias: "local" adapters: cli: enabled: true profiles: no-marker: command: "mycli" ` 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) } profile, ok := cfg.Nodes[0].Adapters.CLI.Profiles["no-marker"] if !ok { t.Fatal("expected no-marker profile") } if !profile.CompletionMarker.Empty() { t.Fatal("expected Empty() == true when completion_marker is not specified") } } func TestCompletionMarkerConf_RegexAndLineUsage(t *testing.T) { lineOnly := config.CompletionMarkerConf{Line: "<>"} if !strings.Contains(lineOnly.Line, "<>") { t.Errorf("expected Line to contain <>, got %q", lineOnly.Line) } regexOnly := config.CompletionMarkerConf{Regex: "^DONE \\d+$"} if !strings.Contains(regexOnly.Regex, "DONE") { t.Errorf("expected Regex to contain DONE, got %q", regexOnly.Regex) } both := config.CompletionMarkerConf{Line: "<>", Regex: "^DONE \\d+$"} if both.Line != "<>" || both.Regex != "^DONE \\d+$" { t.Errorf("expected both fields set, got %+v", both) } if both.Empty() { t.Fatal("expected Empty() == false when both are set") } }