Skip to content

Commit 9d82959

Browse files
authored
test(pods): update PodsRun tests to use testify and improve readability (#433)
Signed-off-by: Marc Nuri <[email protected]>
1 parent ce404f7 commit 9d82959

File tree

1 file changed

+73
-142
lines changed

1 file changed

+73
-142
lines changed

pkg/mcp/pods_run_test.go

Lines changed: 73 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -4,180 +4,107 @@ import (
44
"strings"
55
"testing"
66

7-
"github.com/containers/kubernetes-mcp-server/internal/test"
8-
"github.com/containers/kubernetes-mcp-server/pkg/config"
7+
"github.com/BurntSushi/toml"
98
"github.com/mark3labs/mcp-go/mcp"
9+
"github.com/stretchr/testify/suite"
1010
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1111
"sigs.k8s.io/yaml"
1212
)
1313

14-
func TestPodsRun(t *testing.T) {
15-
testCase(t, func(c *mcpContext) {
16-
c.withEnvTest()
17-
t.Run("pods_run with nil image returns error", func(t *testing.T) {
18-
toolResult, _ := c.callTool("pods_run", map[string]interface{}{})
19-
if toolResult.IsError != true {
20-
t.Errorf("call tool should fail")
21-
return
22-
}
23-
if toolResult.Content[0].(mcp.TextContent).Text != "failed to run pod, missing argument image" {
24-
t.Errorf("invalid error message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
25-
return
26-
}
27-
})
28-
podsRunNilNamespace, err := c.callTool("pods_run", map[string]interface{}{"image": "nginx"})
29-
t.Run("pods_run with image and nil namespace runs pod", func(t *testing.T) {
30-
if err != nil {
31-
t.Errorf("call tool failed %v", err)
32-
return
33-
}
34-
if podsRunNilNamespace.IsError {
35-
t.Errorf("call tool failed")
36-
return
37-
}
14+
type PodsRunSuite struct {
15+
BaseMcpSuite
16+
}
17+
18+
func (s *PodsRunSuite) TestPodsRun() {
19+
s.InitMcpClient()
20+
s.Run("pods_run with nil image returns error", func() {
21+
toolResult, _ := s.CallTool("pods_run", map[string]interface{}{})
22+
s.Truef(toolResult.IsError, "call tool should fail")
23+
s.Equalf("failed to run pod, missing argument image", toolResult.Content[0].(mcp.TextContent).Text,
24+
"invalid error message, got %v", toolResult.Content[0].(mcp.TextContent).Text)
25+
})
26+
s.Run("pods_run(image=nginx, namespace=nil), uses configured namespace", func() {
27+
podsRunNilNamespace, err := s.CallTool("pods_run", map[string]interface{}{"image": "nginx"})
28+
s.Run("no error", func() {
29+
s.Nilf(err, "call tool failed %v", err)
30+
s.Falsef(podsRunNilNamespace.IsError, "call tool failed")
3831
})
3932
var decodedNilNamespace []unstructured.Unstructured
4033
err = yaml.Unmarshal([]byte(podsRunNilNamespace.Content[0].(mcp.TextContent).Text), &decodedNilNamespace)
41-
t.Run("pods_run with image and nil namespace has yaml content", func(t *testing.T) {
42-
if err != nil {
43-
t.Errorf("invalid tool result content %v", err)
44-
return
45-
}
34+
s.Run("has yaml content", func() {
35+
s.Nilf(err, "invalid tool result content %v", err)
4636
})
47-
t.Run("pods_run with image and nil namespace returns 1 item (Pod)", func(t *testing.T) {
48-
if len(decodedNilNamespace) != 1 {
49-
t.Errorf("invalid pods count, expected 1, got %v", len(decodedNilNamespace))
50-
return
51-
}
52-
if decodedNilNamespace[0].GetKind() != "Pod" {
53-
t.Errorf("invalid pod kind, expected Pod, got %v", decodedNilNamespace[0].GetKind())
54-
return
55-
}
37+
s.Run("returns 1 item (Pod)", func() {
38+
s.Lenf(decodedNilNamespace, 1, "invalid pods count, expected 1, got %v", len(decodedNilNamespace))
39+
s.Equalf("Pod", decodedNilNamespace[0].GetKind(), "invalid pod kind, expected Pod, got %v", decodedNilNamespace[0].GetKind())
5640
})
57-
t.Run("pods_run with image and nil namespace returns pod in default", func(t *testing.T) {
58-
if decodedNilNamespace[0].GetNamespace() != "default" {
59-
t.Errorf("invalid pod namespace, expected default, got %v", decodedNilNamespace[0].GetNamespace())
60-
return
61-
}
41+
s.Run("returns pod in default", func() {
42+
s.Equalf("default", decodedNilNamespace[0].GetNamespace(), "invalid pod namespace, expected default, got %v", decodedNilNamespace[0].GetNamespace())
6243
})
63-
t.Run("pods_run with image and nil namespace returns pod with random name", func(t *testing.T) {
64-
if !strings.HasPrefix(decodedNilNamespace[0].GetName(), "kubernetes-mcp-server-run-") {
65-
t.Errorf("invalid pod name, expected random, got %v", decodedNilNamespace[0].GetName())
66-
return
67-
}
44+
s.Run("returns pod with random name", func() {
45+
s.Truef(strings.HasPrefix(decodedNilNamespace[0].GetName(), "kubernetes-mcp-server-run-"),
46+
"invalid pod name, expected random, got %v", decodedNilNamespace[0].GetName())
6847
})
69-
t.Run("pods_run with image and nil namespace returns pod with labels", func(t *testing.T) {
48+
s.Run("returns pod with labels", func() {
7049
labels := decodedNilNamespace[0].Object["metadata"].(map[string]interface{})["labels"].(map[string]interface{})
71-
if labels["app.kubernetes.io/name"] == "" {
72-
t.Errorf("invalid labels, expected app.kubernetes.io/name, got %v", labels)
73-
return
74-
}
75-
if labels["app.kubernetes.io/component"] == "" {
76-
t.Errorf("invalid labels, expected app.kubernetes.io/component, got %v", labels)
77-
return
78-
}
79-
if labels["app.kubernetes.io/managed-by"] != "kubernetes-mcp-server" {
80-
t.Errorf("invalid labels, expected app.kubernetes.io/managed-by, got %v", labels)
81-
return
82-
}
83-
if labels["app.kubernetes.io/part-of"] != "kubernetes-mcp-server-run-sandbox" {
84-
t.Errorf("invalid labels, expected app.kubernetes.io/part-of, got %v", labels)
85-
return
86-
}
50+
s.NotEqualf("", labels["app.kubernetes.io/name"], "invalid labels, expected app.kubernetes.io/name, got %v", labels)
51+
s.NotEqualf("", labels["app.kubernetes.io/component"], "invalid labels, expected app.kubernetes.io/component, got %v", labels)
52+
s.Equalf("kubernetes-mcp-server", labels["app.kubernetes.io/managed-by"], "invalid labels, expected app.kubernetes.io/managed-by, got %v", labels)
53+
s.Equalf("kubernetes-mcp-server-run-sandbox", labels["app.kubernetes.io/part-of"], "invalid labels, expected app.kubernetes.io/part-of, got %v", labels)
8754
})
88-
t.Run("pods_run with image and nil namespace returns pod with nginx container", func(t *testing.T) {
55+
s.Run("returns pod with nginx container", func() {
8956
containers := decodedNilNamespace[0].Object["spec"].(map[string]interface{})["containers"].([]interface{})
90-
if containers[0].(map[string]interface{})["image"] != "nginx" {
91-
t.Errorf("invalid container name, expected nginx, got %v", containers[0].(map[string]interface{})["image"])
92-
return
93-
}
57+
s.Equalf("nginx", containers[0].(map[string]interface{})["image"], "invalid container name, expected nginx, got %v", containers[0].(map[string]interface{})["image"])
9458
})
95-
96-
podsRunNamespaceAndPort, err := c.callTool("pods_run", map[string]interface{}{"image": "nginx", "port": 80})
97-
t.Run("pods_run with image, namespace, and port runs pod", func(t *testing.T) {
98-
if err != nil {
99-
t.Errorf("call tool failed %v", err)
100-
return
101-
}
102-
if podsRunNamespaceAndPort.IsError {
103-
t.Errorf("call tool failed")
104-
return
105-
}
59+
})
60+
s.Run("pods_run(image=nginx, namespace=nil, port=80)", func() {
61+
podsRunNamespaceAndPort, err := s.CallTool("pods_run", map[string]interface{}{"image": "nginx", "port": 80})
62+
s.Run("no error", func() {
63+
s.Nilf(err, "call tool failed %v", err)
64+
s.Falsef(podsRunNamespaceAndPort.IsError, "call tool failed")
10665
})
10766
var decodedNamespaceAndPort []unstructured.Unstructured
10867
err = yaml.Unmarshal([]byte(podsRunNamespaceAndPort.Content[0].(mcp.TextContent).Text), &decodedNamespaceAndPort)
109-
t.Run("pods_run with image, namespace, and port has yaml content", func(t *testing.T) {
110-
if err != nil {
111-
t.Errorf("invalid tool result content %v", err)
112-
return
113-
}
68+
s.Run("has yaml content", func() {
69+
s.Nilf(err, "invalid tool result content %v", err)
11470
})
115-
t.Run("pods_run with image, namespace, and port returns 2 items (Pod + Service)", func(t *testing.T) {
116-
if len(decodedNamespaceAndPort) != 2 {
117-
t.Errorf("invalid pods count, expected 2, got %v", len(decodedNamespaceAndPort))
118-
return
119-
}
120-
if decodedNamespaceAndPort[0].GetKind() != "Pod" {
121-
t.Errorf("invalid pod kind, expected Pod, got %v", decodedNamespaceAndPort[0].GetKind())
122-
return
123-
}
124-
if decodedNamespaceAndPort[1].GetKind() != "Service" {
125-
t.Errorf("invalid service kind, expected Service, got %v", decodedNamespaceAndPort[1].GetKind())
126-
return
127-
}
71+
s.Run("returns 2 items (Pod + Service)", func() {
72+
s.Lenf(decodedNamespaceAndPort, 2, "invalid pods count, expected 2, got %v", len(decodedNamespaceAndPort))
73+
s.Equalf("Pod", decodedNamespaceAndPort[0].GetKind(), "invalid pod kind, expected Pod, got %v", decodedNamespaceAndPort[0].GetKind())
74+
s.Equalf("Service", decodedNamespaceAndPort[1].GetKind(), "invalid service kind, expected Service, got %v", decodedNamespaceAndPort[1].GetKind())
12875
})
129-
t.Run("pods_run with image, namespace, and port returns pod with port", func(t *testing.T) {
76+
s.Run("returns pod with port", func() {
13077
containers := decodedNamespaceAndPort[0].Object["spec"].(map[string]interface{})["containers"].([]interface{})
13178
ports := containers[0].(map[string]interface{})["ports"].([]interface{})
132-
if ports[0].(map[string]interface{})["containerPort"] != int64(80) {
133-
t.Errorf("invalid container port, expected 80, got %v", ports[0].(map[string]interface{})["containerPort"])
134-
return
135-
}
79+
s.Equalf(int64(80), ports[0].(map[string]interface{})["containerPort"], "invalid container port, expected 80, got %v", ports[0].(map[string]interface{})["containerPort"])
13680
})
137-
t.Run("pods_run with image, namespace, and port returns service with port and selector", func(t *testing.T) {
81+
s.Run("returns service with port and selector", func() {
13882
ports := decodedNamespaceAndPort[1].Object["spec"].(map[string]interface{})["ports"].([]interface{})
139-
if ports[0].(map[string]interface{})["port"] != int64(80) {
140-
t.Errorf("invalid service port, expected 80, got %v", ports[0].(map[string]interface{})["port"])
141-
return
142-
}
143-
if ports[0].(map[string]interface{})["targetPort"] != int64(80) {
144-
t.Errorf("invalid service target port, expected 80, got %v", ports[0].(map[string]interface{})["targetPort"])
145-
return
146-
}
83+
s.Equalf(int64(80), ports[0].(map[string]interface{})["port"], "invalid service port, expected 80, got %v", ports[0].(map[string]interface{})["port"])
84+
s.Equalf(int64(80), ports[0].(map[string]interface{})["targetPort"], "invalid service target port, expected 80, got %v", ports[0].(map[string]interface{})["targetPort"])
14785
selector := decodedNamespaceAndPort[1].Object["spec"].(map[string]interface{})["selector"].(map[string]interface{})
148-
if selector["app.kubernetes.io/name"] == "" {
149-
t.Errorf("invalid service selector, expected app.kubernetes.io/name, got %v", selector)
150-
return
151-
}
152-
if selector["app.kubernetes.io/managed-by"] != "kubernetes-mcp-server" {
153-
t.Errorf("invalid service selector, expected app.kubernetes.io/managed-by, got %v", selector)
154-
return
155-
}
156-
if selector["app.kubernetes.io/part-of"] != "kubernetes-mcp-server-run-sandbox" {
157-
t.Errorf("invalid service selector, expected app.kubernetes.io/part-of, got %v", selector)
158-
return
159-
}
86+
s.NotEqualf("", selector["app.kubernetes.io/name"], "invalid service selector, expected app.kubernetes.io/name, got %v", selector)
87+
s.Equalf("kubernetes-mcp-server", selector["app.kubernetes.io/managed-by"], "invalid service selector, expected app.kubernetes.io/managed-by, got %v", selector)
88+
s.Equalf("kubernetes-mcp-server-run-sandbox", selector["app.kubernetes.io/part-of"], "invalid service selector, expected app.kubernetes.io/part-of, got %v", selector)
16089
})
16190
})
16291
}
16392

164-
func TestPodsRunDenied(t *testing.T) {
165-
deniedResourcesServer := test.Must(config.ReadToml([]byte(`
93+
func (s *PodsRunSuite) TestPodsRunDenied() {
94+
s.Require().NoError(toml.Unmarshal([]byte(`
16695
denied_resources = [ { version = "v1", kind = "Pod" } ]
167-
`)))
168-
testCaseWithContext(t, &mcpContext{staticConfig: deniedResourcesServer}, func(c *mcpContext) {
169-
c.withEnvTest()
170-
podsRun, _ := c.callTool("pods_run", map[string]interface{}{"image": "nginx"})
171-
t.Run("pods_run has error", func(t *testing.T) {
172-
if !podsRun.IsError {
173-
t.Fatalf("call tool should fail")
174-
}
175-
})
176-
t.Run("pods_run describes denial", func(t *testing.T) {
96+
`), s.Cfg), "Expected to parse denied resources config")
97+
s.InitMcpClient()
98+
s.Run("pods_run (denied)", func() {
99+
podsRun, err := s.CallTool("pods_run", map[string]interface{}{"image": "nginx"})
100+
s.Run("has error", func() {
101+
s.Truef(podsRun.IsError, "call tool should fail")
102+
s.Nilf(err, "call tool should not return error object")
103+
})
104+
s.Run("describes denial", func() {
177105
expectedMessage := "failed to run pod in namespace : resource not allowed: /v1, Kind=Pod"
178-
if podsRun.Content[0].(mcp.TextContent).Text != expectedMessage {
179-
t.Fatalf("expected descriptive error '%s', got %v", expectedMessage, podsRun.Content[0].(mcp.TextContent).Text)
180-
}
106+
s.Equalf(expectedMessage, podsRun.Content[0].(mcp.TextContent).Text,
107+
"expected descriptive error '%s', got %v", expectedMessage, podsRun.Content[0].(mcp.TextContent).Text)
181108
})
182109
})
183110
}
@@ -216,3 +143,7 @@ func TestPodsRunInOpenShift(t *testing.T) {
216143
})
217144
})
218145
}
146+
147+
func TestPodsRun(t *testing.T) {
148+
suite.Run(t, new(PodsRunSuite))
149+
}

0 commit comments

Comments
 (0)