docker_mcp_test.go

  1package config
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"testing"
  7
  8	"charm.land/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), "docker")
 84		require.Contains(t, string(data), "gateway")
 85	})
 86
 87	t.Run("fails when docker mcp not available", func(t *testing.T) {
 88		t.Parallel()
 89
 90		// Create a temporary directory for config.
 91		tmpDir := t.TempDir()
 92		configPath := filepath.Join(tmpDir, "crush.json")
 93
 94		cfg := &Config{
 95			MCP:            make(map[string]MCPConfig),
 96			dataConfigDir:  configPath,
 97			resolver:       NewShellVariableResolver(env.New()),
 98			knownProviders: []catwalk.Provider{},
 99		}
100
101		// Skip if docker mcp is actually available.
102		if IsDockerMCPAvailable() {
103			t.Skip("Docker MCP is available, skipping unavailable test")
104		}
105
106		err := cfg.EnableDockerMCP()
107		require.Error(t, err)
108		require.Contains(t, err.Error(), "docker mcp is not available")
109	})
110}
111
112func TestDisableDockerMCP(t *testing.T) {
113	t.Parallel()
114
115	t.Run("removes docker mcp from config", func(t *testing.T) {
116		t.Parallel()
117
118		// Create a temporary directory for config.
119		tmpDir := t.TempDir()
120		configPath := filepath.Join(tmpDir, "crush.json")
121
122		cfg := &Config{
123			MCP: map[string]MCPConfig{
124				DockerMCPName: {
125					Type:     MCPStdio,
126					Command:  "docker",
127					Args:     []string{"mcp", "gateway", "run"},
128					Disabled: false,
129				},
130			},
131			dataConfigDir:  configPath,
132			resolver:       NewShellVariableResolver(env.New()),
133			knownProviders: []catwalk.Provider{},
134		}
135
136		// Verify it's enabled first.
137		require.True(t, cfg.IsDockerMCPEnabled())
138
139		err := cfg.DisableDockerMCP()
140		require.NoError(t, err)
141
142		// Check in-memory config.
143		require.False(t, cfg.IsDockerMCPEnabled())
144		_, exists := cfg.MCP[DockerMCPName]
145		require.False(t, exists)
146	})
147
148	t.Run("does nothing when MCP is nil", func(t *testing.T) {
149		t.Parallel()
150
151		cfg := &Config{
152			MCP:            nil,
153			dataConfigDir:  t.TempDir() + "/crush.json",
154			resolver:       NewShellVariableResolver(env.New()),
155			knownProviders: []catwalk.Provider{},
156		}
157
158		err := cfg.DisableDockerMCP()
159		require.NoError(t, err)
160	})
161}