@@ -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