Skip to content

Commit 0f12797

Browse files
committed
test(pods): pods_list_in_namespace test suite
1 parent 30951a3 commit 0f12797

File tree

3 files changed

+116
-44
lines changed

3 files changed

+116
-44
lines changed

pkg/mcp/common_test.go

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,55 @@ import (
2323
"testing"
2424
)
2525

26+
// envTest has an expensive setup, so we only want to do it once per entire test run.
27+
var envTest *envtest.Environment
28+
var envTestRestConfig *rest.Config
29+
30+
func TestMain(m *testing.M) {
31+
// Set up
32+
envTestDir, err := store.DefaultStoreDir()
33+
if err != nil {
34+
panic(err)
35+
}
36+
envTestEnv := &env.Env{
37+
FS: afero.Afero{Fs: afero.NewOsFs()},
38+
Out: os.Stdout,
39+
Client: &remote.HTTPClient{
40+
IndexURL: remote.DefaultIndexURL,
41+
},
42+
Platform: versions.PlatformItem{
43+
Platform: versions.Platform{
44+
OS: runtime.GOOS,
45+
Arch: runtime.GOARCH,
46+
},
47+
},
48+
Version: versions.AnyVersion,
49+
Store: store.NewAt(envTestDir),
50+
}
51+
envTestEnv.CheckCoherence()
52+
workflows.Use{}.Do(envTestEnv)
53+
versionDir := envTestEnv.Platform.Platform.BaseName(*envTestEnv.Version.AsConcrete())
54+
envTest = &envtest.Environment{
55+
BinaryAssetsDirectory: filepath.Join(envTestDir, "k8s", versionDir),
56+
}
57+
envTestRestConfig, _ = envTest.Start()
58+
59+
// Test!
60+
code := m.Run()
61+
62+
// Tear down
63+
if envTest != nil {
64+
_ = envTest.Stop()
65+
}
66+
os.Exit(code)
67+
}
68+
2669
type mcpContext struct {
2770
ctx context.Context
2871
tempDir string
2972
testServer *httptest.Server
3073
cancel context.CancelFunc
3174
mcpClient *client.SSEMCPClient
32-
envTest *envtest.Environment
3375
}
3476

3577
func (c *mcpContext) beforeEach(t *testing.T) {
@@ -57,9 +99,6 @@ func (c *mcpContext) beforeEach(t *testing.T) {
5799
}
58100

59101
func (c *mcpContext) afterEach() {
60-
if c.envTest != nil {
61-
_ = c.envTest.Stop()
62-
}
63102
c.cancel()
64103
_ = c.mcpClient.Close()
65104
c.testServer.Close()
@@ -94,36 +133,7 @@ func (c *mcpContext) withKubeConfig(rc *rest.Config) *api.Config {
94133
}
95134

96135
func (c *mcpContext) withEnvTest() {
97-
if c.envTest != nil {
98-
return
99-
}
100-
envTestDir, err := store.DefaultStoreDir()
101-
if err != nil {
102-
panic(err)
103-
}
104-
envTest := &env.Env{
105-
FS: afero.Afero{Fs: afero.NewOsFs()},
106-
Out: os.Stdout,
107-
Client: &remote.HTTPClient{
108-
IndexURL: remote.DefaultIndexURL,
109-
},
110-
Platform: versions.PlatformItem{
111-
Platform: versions.Platform{
112-
OS: runtime.GOOS,
113-
Arch: runtime.GOARCH,
114-
},
115-
},
116-
Version: versions.AnyVersion,
117-
Store: store.NewAt(envTestDir),
118-
}
119-
envTest.CheckCoherence()
120-
workflows.Use{}.Do(envTest)
121-
versionDir := envTest.Platform.Platform.BaseName(*envTest.Version.AsConcrete())
122-
c.envTest = &envtest.Environment{
123-
BinaryAssetsDirectory: filepath.Join(envTestDir, "k8s", versionDir),
124-
}
125-
restConfig, _ := c.envTest.Start()
126-
c.withKubeConfig(restConfig)
136+
c.withKubeConfig(envTestRestConfig)
127137
}
128138

129139
func (c *mcpContext) newKubernetesClient() *kubernetes.Clientset {
@@ -136,3 +146,10 @@ func (c *mcpContext) newKubernetesClient() *kubernetes.Clientset {
136146
}
137147
return kubernetesClient
138148
}
149+
150+
func (c *mcpContext) callTool(name string, args map[string]interface{}) (*mcp.CallToolResult, error) {
151+
callToolRequest := mcp.CallToolRequest{}
152+
callToolRequest.Params.Name = name
153+
callToolRequest.Params.Arguments = args
154+
return c.mcpClient.CallTool(c.ctx, callToolRequest)
155+
}

pkg/mcp/configuration_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
package mcp
22

33
import (
4-
"github.com/mark3labs/mcp-go/mcp"
54
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
65
"sigs.k8s.io/yaml"
76
"testing"
87
)
98

109
func TestConfigurationView(t *testing.T) {
1110
testCase(t, func(c *mcpContext) {
12-
configurationGet := mcp.CallToolRequest{}
13-
configurationGet.Params.Name = "configuration_view"
14-
configurationGet.Params.Arguments = map[string]interface{}{}
15-
toolResult, err := c.mcpClient.CallTool(c.ctx, configurationGet)
11+
toolResult, err := c.callTool("configuration_view", map[string]interface{}{})
1612
t.Run("configuration_view returns configuration", func(t *testing.T) {
1713
if err != nil {
1814
t.Fatalf("call tool failed %v", err)

pkg/mcp/pods_test.go

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package mcp
22

33
import (
44
"context"
5-
"github.com/mark3labs/mcp-go/mcp"
65
corev1 "k8s.io/api/core/v1"
76
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
87
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -13,11 +12,9 @@ import (
1312

1413
func TestPodsListInAllNamespaces(t *testing.T) {
1514
testCase(t, func(c *mcpContext) {
15+
c.withEnvTest()
1616
createTestData(c.ctx, c.newKubernetesClient())
17-
configurationGet := mcp.CallToolRequest{}
18-
configurationGet.Params.Name = "pods_list"
19-
configurationGet.Params.Arguments = map[string]interface{}{}
20-
toolResult, err := c.mcpClient.CallTool(c.ctx, configurationGet)
17+
toolResult, err := c.callTool("pods_list", map[string]interface{}{})
2118
t.Run("pods_list returns pods list", func(t *testing.T) {
2219
if err != nil {
2320
t.Fatalf("call tool failed %v", err)
@@ -67,6 +64,68 @@ func TestPodsListInAllNamespaces(t *testing.T) {
6764
})
6865
}
6966

67+
func TestPodsListInNamespace(t *testing.T) {
68+
testCase(t, func(c *mcpContext) {
69+
c.withEnvTest()
70+
t.Run("pods_list_in_namespace with nil namespace returns pods list", func(t *testing.T) {
71+
toolResult, _ := c.callTool("pods_list_in_namespace", map[string]interface{}{})
72+
if toolResult.IsError != true {
73+
t.Fatalf("call tool should fail")
74+
return
75+
}
76+
if toolResult.Content[0].(map[string]interface{})["text"].(string) != "failed to list pods in namespace, missing argument namespace" {
77+
t.Fatalf("invalid error message, got %v", toolResult.Content[0].(map[string]interface{})["text"].(string))
78+
return
79+
}
80+
})
81+
createTestData(c.ctx, c.newKubernetesClient())
82+
toolResult, err := c.callTool("pods_list_in_namespace", map[string]interface{}{
83+
"namespace": "ns-1",
84+
})
85+
t.Run("pods_list_in_namespace returns pods list", func(t *testing.T) {
86+
if err != nil {
87+
t.Fatalf("call tool failed %v", err)
88+
return
89+
}
90+
if toolResult.IsError {
91+
t.Fatalf("call tool failed")
92+
return
93+
}
94+
})
95+
var decoded []unstructured.Unstructured
96+
err = yaml.Unmarshal([]byte(toolResult.Content[0].(map[string]interface{})["text"].(string)), &decoded)
97+
t.Run("pods_list_in_namespace has yaml content", func(t *testing.T) {
98+
if err != nil {
99+
t.Fatalf("invalid tool result content %v", err)
100+
return
101+
}
102+
})
103+
t.Run("pods_list_in_namespace returns 1 items", func(t *testing.T) {
104+
if len(decoded) != 1 {
105+
t.Fatalf("invalid pods count, expected 1, got %v", len(decoded))
106+
return
107+
}
108+
})
109+
t.Run("pods_list_in_namespace returns pod in ns-1", func(t *testing.T) {
110+
if decoded[0].GetName() != "a-pod-in-ns-1" {
111+
t.Fatalf("invalid pod name, expected a-pod-in-ns-1, got %v", decoded[0].GetName())
112+
return
113+
}
114+
if decoded[0].GetNamespace() != "ns-1" {
115+
t.Fatalf("invalid pod namespace, expected ns-1, got %v", decoded[0].GetNamespace())
116+
return
117+
}
118+
})
119+
t.Run("pods_list_in_namespace omits managed fields", func(t *testing.T) {
120+
if decoded[0].GetManagedFields() != nil {
121+
t.Fatalf("managed fields should be omitted, got %v", decoded[0].GetManagedFields())
122+
return
123+
}
124+
})
125+
})
126+
127+
}
128+
70129
func createTestData(ctx context.Context, kc *kubernetes.Clientset) {
71130
_, _ = kc.CoreV1().Namespaces().
72131
Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns-1"}}, metav1.CreateOptions{})

0 commit comments

Comments
 (0)