Skip to content

Commit f496c64

Browse files
authored
test(mcp): refactor configuration toolset tests (#327)
Signed-off-by: Marc Nuri <[email protected]>
1 parent d9d35b9 commit f496c64

File tree

1 file changed

+121
-147
lines changed

1 file changed

+121
-147
lines changed

pkg/mcp/configuration_test.go

Lines changed: 121 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -3,177 +3,151 @@ package mcp
33
import (
44
"testing"
55

6-
"github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
76
"github.com/mark3labs/mcp-go/mcp"
7+
"github.com/stretchr/testify/suite"
88
"k8s.io/client-go/rest"
99
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
1010
"sigs.k8s.io/yaml"
11+
12+
"github.com/containers/kubernetes-mcp-server/internal/test"
13+
"github.com/containers/kubernetes-mcp-server/pkg/config"
14+
"github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
1115
)
1216

13-
func TestConfigurationView(t *testing.T) {
14-
testCase(t, func(c *mcpContext) {
15-
toolResult, err := c.callTool("configuration_view", map[string]interface{}{})
16-
t.Run("configuration_view returns configuration", func(t *testing.T) {
17-
if err != nil {
18-
t.Fatalf("call tool failed %v", err)
19-
}
20-
})
17+
type ConfigurationSuite struct {
18+
suite.Suite
19+
*test.McpClient
20+
mcpServer *Server
21+
Cfg *config.StaticConfig
22+
}
23+
24+
func (s *ConfigurationSuite) SetupTest() {
25+
s.Cfg = config.Default()
26+
}
27+
28+
func (s *ConfigurationSuite) TearDownTest() {
29+
if s.McpClient != nil {
30+
s.McpClient.Close()
31+
}
32+
if s.mcpServer != nil {
33+
s.mcpServer.Close()
34+
}
35+
}
36+
37+
func (s *ConfigurationSuite) InitMcpClient() {
38+
var err error
39+
s.mcpServer, err = NewServer(Configuration{StaticConfig: s.Cfg})
40+
s.Require().NoError(err, "Expected no error creating MCP server")
41+
s.McpClient = test.NewMcpClient(s.T(), s.mcpServer.ServeHTTP(nil))
42+
}
43+
44+
func (s *ConfigurationSuite) TestConfigurationView() {
45+
// Out of cluster requires kubeconfig
46+
mockServer := test.NewMockServer()
47+
s.T().Cleanup(mockServer.Close)
48+
s.Cfg.KubeConfig = mockServer.KubeconfigFile(s.T())
49+
s.InitMcpClient()
50+
s.Run("configuration_view", func() {
51+
toolResult, err := s.CallTool("configuration_view", map[string]interface{}{})
52+
s.Run("returns configuration", func() {
53+
s.Nilf(err, "call tool failed %v", err)
54+
})
55+
s.Require().NotNil(toolResult, "Expected tool result from call")
2156
var decoded *v1.Config
2257
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
23-
t.Run("configuration_view has yaml content", func(t *testing.T) {
24-
if err != nil {
25-
t.Fatalf("invalid tool result content %v", err)
26-
}
27-
})
28-
t.Run("configuration_view returns current-context", func(t *testing.T) {
29-
if decoded.CurrentContext != "fake-context" {
30-
t.Errorf("fake-context not found: %v", decoded.CurrentContext)
31-
}
32-
})
33-
t.Run("configuration_view returns context info", func(t *testing.T) {
34-
if len(decoded.Contexts) != 1 {
35-
t.Errorf("invalid context count, expected 1, got %v", len(decoded.Contexts))
36-
}
37-
if decoded.Contexts[0].Name != "fake-context" {
38-
t.Errorf("fake-context not found: %v", decoded.Contexts)
39-
}
40-
if decoded.Contexts[0].Context.Cluster != "fake" {
41-
t.Errorf("fake-cluster not found: %v", decoded.Contexts)
42-
}
43-
if decoded.Contexts[0].Context.AuthInfo != "fake" {
44-
t.Errorf("fake-auth not found: %v", decoded.Contexts)
45-
}
46-
})
47-
t.Run("configuration_view returns cluster info", func(t *testing.T) {
48-
if len(decoded.Clusters) != 1 {
49-
t.Errorf("invalid cluster count, expected 1, got %v", len(decoded.Clusters))
50-
}
51-
if decoded.Clusters[0].Name != "fake" {
52-
t.Errorf("fake-cluster not found: %v", decoded.Clusters)
53-
}
54-
if decoded.Clusters[0].Cluster.Server != "https://127.0.0.1:6443" {
55-
t.Errorf("fake-server not found: %v", decoded.Clusters)
56-
}
57-
})
58-
t.Run("configuration_view returns auth info", func(t *testing.T) {
59-
if len(decoded.AuthInfos) != 1 {
60-
t.Errorf("invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
61-
}
62-
if decoded.AuthInfos[0].Name != "fake" {
63-
t.Errorf("fake-auth not found: %v", decoded.AuthInfos)
64-
}
65-
})
66-
toolResult, err = c.callTool("configuration_view", map[string]interface{}{
58+
s.Run("has yaml content", func() {
59+
s.Nilf(err, "invalid tool result content %v", err)
60+
})
61+
s.Run("returns current-context", func() {
62+
s.Equalf("fake-context", decoded.CurrentContext, "fake-context not found: %v", decoded.CurrentContext)
63+
})
64+
s.Run("returns context info", func() {
65+
s.Lenf(decoded.Contexts, 1, "invalid context count, expected 1, got %v", len(decoded.Contexts))
66+
s.Equalf("fake-context", decoded.Contexts[0].Name, "fake-context not found: %v", decoded.Contexts)
67+
s.Equalf("fake", decoded.Contexts[0].Context.Cluster, "fake-cluster not found: %v", decoded.Contexts)
68+
s.Equalf("fake", decoded.Contexts[0].Context.AuthInfo, "fake-auth not found: %v", decoded.Contexts)
69+
})
70+
s.Run("returns cluster info", func() {
71+
s.Lenf(decoded.Clusters, 1, "invalid cluster count, expected 1, got %v", len(decoded.Clusters))
72+
s.Equalf("fake", decoded.Clusters[0].Name, "fake-cluster not found: %v", decoded.Clusters)
73+
s.Regexpf(`^https?://(127\.0\.0\.1|localhost):\d{1,5}$`, decoded.Clusters[0].Cluster.Server, "fake-server not found: %v", decoded.Clusters)
74+
})
75+
s.Run("returns auth info", func() {
76+
s.Lenf(decoded.AuthInfos, 1, "invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
77+
s.Equalf("fake", decoded.AuthInfos[0].Name, "fake-auth not found: %v", decoded.AuthInfos)
78+
})
79+
})
80+
s.Run("configuration_view(minified=false)", func() {
81+
toolResult, err := s.CallTool("configuration_view", map[string]interface{}{
6782
"minified": false,
6883
})
69-
t.Run("configuration_view with minified=false returns configuration", func(t *testing.T) {
70-
if err != nil {
71-
t.Fatalf("call tool failed %v", err)
72-
}
84+
s.Run("returns configuration", func() {
85+
s.Nilf(err, "call tool failed %v", err)
7386
})
87+
var decoded *v1.Config
7488
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
75-
t.Run("configuration_view with minified=false has yaml content", func(t *testing.T) {
76-
if err != nil {
77-
t.Fatalf("invalid tool result content %v", err)
78-
}
79-
})
80-
t.Run("configuration_view with minified=false returns additional context info", func(t *testing.T) {
81-
if len(decoded.Contexts) != 2 {
82-
t.Fatalf("invalid context count, expected2, got %v", len(decoded.Contexts))
83-
}
84-
if decoded.Contexts[0].Name != "additional-context" {
85-
t.Errorf("additional-context not found: %v", decoded.Contexts)
86-
}
87-
if decoded.Contexts[0].Context.Cluster != "additional-cluster" {
88-
t.Errorf("additional-cluster not found: %v", decoded.Contexts)
89-
}
90-
if decoded.Contexts[0].Context.AuthInfo != "additional-auth" {
91-
t.Errorf("additional-auth not found: %v", decoded.Contexts)
92-
}
93-
if decoded.Contexts[1].Name != "fake-context" {
94-
t.Errorf("fake-context not found: %v", decoded.Contexts)
95-
}
96-
})
97-
t.Run("configuration_view with minified=false returns cluster info", func(t *testing.T) {
98-
if len(decoded.Clusters) != 2 {
99-
t.Errorf("invalid cluster count, expected 2, got %v", len(decoded.Clusters))
100-
}
101-
if decoded.Clusters[0].Name != "additional-cluster" {
102-
t.Errorf("additional-cluster not found: %v", decoded.Clusters)
103-
}
104-
})
105-
t.Run("configuration_view with minified=false returns auth info", func(t *testing.T) {
106-
if len(decoded.AuthInfos) != 2 {
107-
t.Errorf("invalid auth info count, expected 2, got %v", len(decoded.AuthInfos))
108-
}
109-
if decoded.AuthInfos[0].Name != "additional-auth" {
110-
t.Errorf("additional-auth not found: %v", decoded.AuthInfos)
111-
}
89+
s.Run("has yaml content", func() {
90+
s.Nilf(err, "invalid tool result content %v", err)
91+
})
92+
s.Run("returns additional context info", func() {
93+
s.Lenf(decoded.Contexts, 2, "invalid context count, expected 2, got %v", len(decoded.Contexts))
94+
s.Equalf("additional-context", decoded.Contexts[0].Name, "additional-context not found: %v", decoded.Contexts)
95+
s.Equalf("additional-cluster", decoded.Contexts[0].Context.Cluster, "additional-cluster not found: %v", decoded.Contexts)
96+
s.Equalf("additional-auth", decoded.Contexts[0].Context.AuthInfo, "additional-auth not found: %v", decoded.Contexts)
97+
s.Equalf("fake-context", decoded.Contexts[1].Name, "fake-context not found: %v", decoded.Contexts)
98+
})
99+
s.Run("returns cluster info", func() {
100+
s.Lenf(decoded.Clusters, 2, "invalid cluster count, expected 2, got %v", len(decoded.Clusters))
101+
s.Equalf("additional-cluster", decoded.Clusters[0].Name, "additional-cluster not found: %v", decoded.Clusters)
102+
})
103+
s.Run("configuration_view with minified=false returns auth info", func() {
104+
s.Lenf(decoded.AuthInfos, 2, "invalid auth info count, expected 2, got %v", len(decoded.AuthInfos))
105+
s.Equalf("additional-auth", decoded.AuthInfos[0].Name, "additional-auth not found: %v", decoded.AuthInfos)
112106
})
113107
})
114108
}
115109

116-
func TestConfigurationViewInCluster(t *testing.T) {
110+
func (s *ConfigurationSuite) TestConfigurationViewInCluster() {
117111
kubernetes.InClusterConfig = func() (*rest.Config, error) {
118112
return &rest.Config{
119113
Host: "https://kubernetes.default.svc",
120114
BearerToken: "fake-token",
121115
}, nil
122116
}
123-
defer func() {
124-
kubernetes.InClusterConfig = rest.InClusterConfig
125-
}()
126-
testCase(t, func(c *mcpContext) {
127-
toolResult, err := c.callTool("configuration_view", map[string]interface{}{})
128-
t.Run("configuration_view returns configuration", func(t *testing.T) {
129-
if err != nil {
130-
t.Fatalf("call tool failed %v", err)
131-
}
132-
})
117+
s.T().Cleanup(func() { kubernetes.InClusterConfig = rest.InClusterConfig })
118+
s.InitMcpClient()
119+
s.Run("configuration_view", func() {
120+
toolResult, err := s.CallTool("configuration_view", map[string]interface{}{})
121+
s.Run("returns configuration", func() {
122+
s.Nilf(err, "call tool failed %v", err)
123+
})
124+
s.Require().NotNil(toolResult, "Expected tool result from call")
133125
var decoded *v1.Config
134126
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
135-
t.Run("configuration_view has yaml content", func(t *testing.T) {
136-
if err != nil {
137-
t.Fatalf("invalid tool result content %v", err)
138-
}
139-
})
140-
t.Run("configuration_view returns current-context", func(t *testing.T) {
141-
if decoded.CurrentContext != "context" {
142-
t.Fatalf("context not found: %v", decoded.CurrentContext)
143-
}
144-
})
145-
t.Run("configuration_view returns context info", func(t *testing.T) {
146-
if len(decoded.Contexts) != 1 {
147-
t.Fatalf("invalid context count, expected 1, got %v", len(decoded.Contexts))
148-
}
149-
if decoded.Contexts[0].Name != "context" {
150-
t.Fatalf("context not found: %v", decoded.Contexts)
151-
}
152-
if decoded.Contexts[0].Context.Cluster != "cluster" {
153-
t.Fatalf("cluster not found: %v", decoded.Contexts)
154-
}
155-
if decoded.Contexts[0].Context.AuthInfo != "user" {
156-
t.Fatalf("user not found: %v", decoded.Contexts)
157-
}
158-
})
159-
t.Run("configuration_view returns cluster info", func(t *testing.T) {
160-
if len(decoded.Clusters) != 1 {
161-
t.Fatalf("invalid cluster count, expected 1, got %v", len(decoded.Clusters))
162-
}
163-
if decoded.Clusters[0].Name != "cluster" {
164-
t.Fatalf("cluster not found: %v", decoded.Clusters)
165-
}
166-
if decoded.Clusters[0].Cluster.Server != "https://kubernetes.default.svc" {
167-
t.Fatalf("server not found: %v", decoded.Clusters)
168-
}
169-
})
170-
t.Run("configuration_view returns auth info", func(t *testing.T) {
171-
if len(decoded.AuthInfos) != 1 {
172-
t.Fatalf("invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
173-
}
174-
if decoded.AuthInfos[0].Name != "user" {
175-
t.Fatalf("user not found: %v", decoded.AuthInfos)
176-
}
127+
s.Run("has yaml content", func() {
128+
s.Nilf(err, "invalid tool result content %v", err)
129+
})
130+
s.Run("returns current-context", func() {
131+
s.Equalf("context", decoded.CurrentContext, "context not found: %v", decoded.CurrentContext)
132+
})
133+
s.Run("returns context info", func() {
134+
s.Lenf(decoded.Contexts, 1, "invalid context count, expected 1, got %v", len(decoded.Contexts))
135+
s.Equalf("context", decoded.Contexts[0].Name, "context not found: %v", decoded.Contexts)
136+
s.Equalf("cluster", decoded.Contexts[0].Context.Cluster, "cluster not found: %v", decoded.Contexts)
137+
s.Equalf("user", decoded.Contexts[0].Context.AuthInfo, "user not found: %v", decoded.Contexts)
138+
})
139+
s.Run("returns cluster info", func() {
140+
s.Lenf(decoded.Clusters, 1, "invalid cluster count, expected 1, got %v", len(decoded.Clusters))
141+
s.Equalf("cluster", decoded.Clusters[0].Name, "cluster not found: %v", decoded.Clusters)
142+
s.Equalf("https://kubernetes.default.svc", decoded.Clusters[0].Cluster.Server, "server not found: %v", decoded.Clusters)
143+
})
144+
s.Run("returns auth info", func() {
145+
s.Lenf(decoded.AuthInfos, 1, "invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
146+
s.Equalf("user", decoded.AuthInfos[0].Name, "user not found: %v", decoded.AuthInfos)
177147
})
178148
})
179149
}
150+
151+
func TestConfiguration(t *testing.T) {
152+
suite.Run(t, new(ConfigurationSuite))
153+
}

0 commit comments

Comments
 (0)