docker_mcp_test.go

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