1package config
2
3import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "github.com/charmbracelet/crush/internal/env"
9 "github.com/stretchr/testify/require"
10)
11
12func TestIsDockerMCPEnabled(t *testing.T) {
13 t.Parallel()
14
15 t.Run("returns false when MCP is nil", func(t *testing.T) {
16 t.Parallel()
17 cfg := &Config{
18 MCP: nil,
19 }
20 require.False(t, cfg.IsDockerMCPEnabled())
21 })
22
23 t.Run("returns false when docker mcp not configured", func(t *testing.T) {
24 t.Parallel()
25 cfg := &Config{
26 MCP: make(map[string]MCPConfig),
27 }
28 require.False(t, cfg.IsDockerMCPEnabled())
29 })
30
31 t.Run("returns true when docker mcp is configured", func(t *testing.T) {
32 t.Parallel()
33 cfg := &Config{
34 MCP: map[string]MCPConfig{
35 DockerMCPName: {
36 Type: MCPStdio,
37 Command: "docker",
38 },
39 },
40 }
41 require.True(t, cfg.IsDockerMCPEnabled())
42 })
43}
44
45func TestEnableDockerMCP(t *testing.T) {
46 t.Parallel()
47
48 t.Run("adds docker mcp to config", func(t *testing.T) {
49 t.Parallel()
50
51 // Create a temporary directory for config.
52 tmpDir := t.TempDir()
53 configPath := filepath.Join(tmpDir, "crush.json")
54
55 cfg := &Config{
56 MCP: make(map[string]MCPConfig),
57 }
58 store := &ConfigStore{
59 config: cfg,
60 globalDataPath: configPath,
61 resolver: NewShellVariableResolver(env.New()),
62 }
63
64 // Only run this test if docker mcp is available.
65 if !IsDockerMCPAvailable() {
66 t.Skip("Docker MCP not available, skipping test")
67 }
68
69 err := store.EnableDockerMCP()
70 require.NoError(t, err)
71
72 // Check in-memory config.
73 require.True(t, cfg.IsDockerMCPEnabled())
74 mcpConfig, exists := cfg.MCP[DockerMCPName]
75 require.True(t, exists)
76 require.Equal(t, MCPStdio, mcpConfig.Type)
77 require.Equal(t, "docker", mcpConfig.Command)
78 require.Equal(t, []string{"mcp", "gateway", "run"}, mcpConfig.Args)
79 require.False(t, mcpConfig.Disabled)
80
81 // Check persisted config.
82 data, err := os.ReadFile(configPath)
83 require.NoError(t, err)
84 require.Contains(t, string(data), "docker")
85 require.Contains(t, string(data), "gateway")
86 })
87
88 t.Run("fails when docker mcp not available", func(t *testing.T) {
89 t.Parallel()
90
91 // Create a temporary directory for config.
92 tmpDir := t.TempDir()
93 configPath := filepath.Join(tmpDir, "crush.json")
94
95 cfg := &Config{
96 MCP: make(map[string]MCPConfig),
97 }
98 store := &ConfigStore{
99 config: cfg,
100 globalDataPath: configPath,
101 resolver: NewShellVariableResolver(env.New()),
102 }
103
104 // Skip if docker mcp is actually available.
105 if IsDockerMCPAvailable() {
106 t.Skip("Docker MCP is available, skipping unavailable test")
107 }
108
109 err := store.EnableDockerMCP()
110 require.Error(t, err)
111 require.Contains(t, err.Error(), "docker mcp is not available")
112 })
113}
114
115func TestDisableDockerMCP(t *testing.T) {
116 t.Parallel()
117
118 t.Run("removes docker mcp from config", func(t *testing.T) {
119 t.Parallel()
120
121 // Create a temporary directory for config.
122 tmpDir := t.TempDir()
123 configPath := filepath.Join(tmpDir, "crush.json")
124
125 cfg := &Config{
126 MCP: map[string]MCPConfig{
127 DockerMCPName: {
128 Type: MCPStdio,
129 Command: "docker",
130 Args: []string{"mcp", "gateway", "run"},
131 Disabled: false,
132 },
133 },
134 }
135 store := &ConfigStore{
136 config: cfg,
137 globalDataPath: configPath,
138 resolver: NewShellVariableResolver(env.New()),
139 }
140
141 // Verify it's enabled first.
142 require.True(t, cfg.IsDockerMCPEnabled())
143
144 err := store.DisableDockerMCP()
145 require.NoError(t, err)
146
147 // Check in-memory config.
148 require.False(t, cfg.IsDockerMCPEnabled())
149 _, exists := cfg.MCP[DockerMCPName]
150 require.False(t, exists)
151 })
152
153 t.Run("does nothing when MCP is nil", func(t *testing.T) {
154 t.Parallel()
155
156 cfg := &Config{
157 MCP: nil,
158 }
159 store := &ConfigStore{
160 config: cfg,
161 globalDataPath: filepath.Join(t.TempDir(), "crush.json"),
162 resolver: NewShellVariableResolver(env.New()),
163 }
164
165 err := store.DisableDockerMCP()
166 require.NoError(t, err)
167 })
168}