package app import ( "encoding/json" "strings" "testing" ) func TestMergeTOMLMCPFreshFile(t *testing.T) { out, err := mergeTOMLMCP(nil, "mcp_servers", "/usr/local/bin/patterm", []string{"mcp-stdio", "--socket", "/run/patterm/1.sock", "--identity", "abc123"}) if err != nil { t.Fatal(err) } s := string(out) if !strings.Contains(s, "[mcp_servers.patterm]") { t.Fatalf("missing patterm table:\n%s", s) } if !strings.Contains(s, `command = "/usr/local/bin/patterm"`) { t.Fatalf("missing command line:\n%s", s) } if !strings.Contains(s, `args = ["mcp-stdio", "--socket", "/run/patterm/1.sock", "--identity", "abc123"]`) { t.Fatalf("missing args line:\n%s", s) } } func TestMergeTOMLMCPPreservesOtherSections(t *testing.T) { existing := `model = "gpt-5" [mcp_servers.something_else] command = "x" args = ["y"] ` out, err := mergeTOMLMCP([]byte(existing), "mcp_servers", "/bin/patterm", []string{"mcp-stdio", "--socket", "/s", "--identity", "id"}) if err != nil { t.Fatal(err) } s := string(out) if !strings.Contains(s, `model = "gpt-5"`) { t.Fatalf("lost top-level model setting:\n%s", s) } if !strings.Contains(s, "[mcp_servers.something_else]") { t.Fatalf("lost neighbouring mcp_servers entry:\n%s", s) } if !strings.Contains(s, "[mcp_servers.patterm]") { t.Fatalf("missing patterm entry:\n%s", s) } } func TestMergeTOMLMCPReplacesStalePatternEntry(t *testing.T) { existing := `[mcp_servers.patterm] command = "/old/path" args = ["stale"] [mcp_servers.keep] command = "k" ` out, err := mergeTOMLMCP([]byte(existing), "mcp_servers", "/new/bin", []string{"mcp-stdio", "--socket", "/s2", "--identity", "id2"}) if err != nil { t.Fatal(err) } s := string(out) if strings.Contains(s, "/old/path") { t.Fatalf("stale command remained:\n%s", s) } if strings.Contains(s, "stale") { t.Fatalf("stale args remained:\n%s", s) } if !strings.Contains(s, "[mcp_servers.keep]") { t.Fatalf("dropped sibling section:\n%s", s) } // New patterm block appears exactly once. if c := strings.Count(s, "[mcp_servers.patterm]"); c != 1 { t.Fatalf("expected single patterm block, got %d:\n%s", c, s) } } func TestMergeJSONMCPFreshFile(t *testing.T) { out, err := mergeJSONMCP(nil, "mcp", "/bin/patterm", []string{"mcp-stdio", "--socket", "/s", "--identity", "id"}) if err != nil { t.Fatal(err) } var root map[string]any if err := json.Unmarshal(out, &root); err != nil { t.Fatalf("output not valid json: %v\n%s", err, out) } mcp, ok := root["mcp"].(map[string]any) if !ok { t.Fatalf("mcp key missing or wrong type: %v", root) } entry, ok := mcp["patterm"].(map[string]any) if !ok { t.Fatalf("patterm entry missing: %v", mcp) } if entry["type"] != "local" { t.Fatalf("expected type=local, got %v", entry["type"]) } cmd, ok := entry["command"].([]any) if !ok || len(cmd) != 6 || cmd[0] != "/bin/patterm" { t.Fatalf("unexpected command: %#v", entry["command"]) } } func TestMergeJSONMCPPreservesExistingKeysAndReplacesPatterm(t *testing.T) { existing := `{ "$schema": "https://opencode.ai/config.json", "model": "claude-sonnet-4", "mcp": { "patterm": {"type": "local", "command": ["old"]}, "other": {"type": "local", "command": ["k"]} } }` out, err := mergeJSONMCP([]byte(existing), "mcp", "/new/bin", []string{"mcp-stdio", "--socket", "/s", "--identity", "id"}) if err != nil { t.Fatal(err) } var root map[string]any if err := json.Unmarshal(out, &root); err != nil { t.Fatalf("output not valid json: %v\n%s", err, out) } if root["$schema"] != "https://opencode.ai/config.json" { t.Fatalf("lost $schema: %v", root["$schema"]) } if root["model"] != "claude-sonnet-4" { t.Fatalf("lost model: %v", root["model"]) } mcp := root["mcp"].(map[string]any) if _, ok := mcp["other"]; !ok { t.Fatalf("dropped sibling mcp entry") } entry := mcp["patterm"].(map[string]any) cmd := entry["command"].([]any) if cmd[0] != "/new/bin" { t.Fatalf("patterm entry not refreshed: %v", cmd) } }