1
1
package mcp
2
2
3
3
import (
4
+ "github.com/manusa/kubernetes-mcp-server/pkg/output"
5
+ corev1 "k8s.io/api/core/v1"
6
+ "regexp"
4
7
"strings"
5
8
"testing"
6
9
@@ -19,44 +22,36 @@ func TestResourcesList(t *testing.T) {
19
22
toolResult , _ := c .callTool ("resources_list" , map [string ]interface {}{})
20
23
if ! toolResult .IsError {
21
24
t .Fatalf ("call tool should fail" )
22
- return
23
25
}
24
26
if toolResult .Content [0 ].(mcp.TextContent ).Text != "failed to list resources, missing argument apiVersion" {
25
27
t .Fatalf ("invalid error message, got %v" , toolResult .Content [0 ].(mcp.TextContent ).Text )
26
- return
27
28
}
28
29
})
29
30
t .Run ("resources_list with missing kind returns error" , func (t * testing.T ) {
30
31
toolResult , _ := c .callTool ("resources_list" , map [string ]interface {}{"apiVersion" : "v1" })
31
32
if ! toolResult .IsError {
32
33
t .Fatalf ("call tool should fail" )
33
- return
34
34
}
35
35
if toolResult .Content [0 ].(mcp.TextContent ).Text != "failed to list resources, missing argument kind" {
36
36
t .Fatalf ("invalid error message, got %v" , toolResult .Content [0 ].(mcp.TextContent ).Text )
37
- return
38
37
}
39
38
})
40
39
t .Run ("resources_list with invalid apiVersion returns error" , func (t * testing.T ) {
41
40
toolResult , _ := c .callTool ("resources_list" , map [string ]interface {}{"apiVersion" : "invalid/api/version" , "kind" : "Pod" })
42
41
if ! toolResult .IsError {
43
42
t .Fatalf ("call tool should fail" )
44
- return
45
43
}
46
44
if toolResult .Content [0 ].(mcp.TextContent ).Text != "failed to list resources, invalid argument apiVersion" {
47
45
t .Fatalf ("invalid error message, got %v" , toolResult .Content [0 ].(mcp.TextContent ).Text )
48
- return
49
46
}
50
47
})
51
48
t .Run ("resources_list with nonexistent apiVersion returns error" , func (t * testing.T ) {
52
49
toolResult , _ := c .callTool ("resources_list" , map [string ]interface {}{"apiVersion" : "custom.non.existent.example.com/v1" , "kind" : "Custom" })
53
50
if ! toolResult .IsError {
54
51
t .Fatalf ("call tool should fail" )
55
- return
56
52
}
57
53
if toolResult .Content [0 ].(mcp.TextContent ).Text != `failed to list resources: no matches for kind "Custom" in version "custom.non.existent.example.com/v1"` {
58
54
t .Fatalf ("invalid error message, got %v" , toolResult .Content [0 ].(mcp.TextContent ).Text )
59
- return
60
55
}
61
56
})
62
57
namespaces , err := c .callTool ("resources_list" , map [string ]interface {}{"apiVersion" : "v1" , "kind" : "Namespace" })
@@ -75,13 +70,11 @@ func TestResourcesList(t *testing.T) {
75
70
t .Run ("resources_list has yaml content" , func (t * testing.T ) {
76
71
if err != nil {
77
72
t .Fatalf ("invalid tool result content %v" , err )
78
- return
79
73
}
80
74
})
81
75
t .Run ("resources_list returns more than 2 items" , func (t * testing.T ) {
82
76
if len (decodedNamespaces ) < 3 {
83
77
t .Fatalf ("invalid namespace count, expected >2, got %v" , len (decodedNamespaces ))
84
- return
85
78
}
86
79
})
87
80
@@ -155,6 +148,83 @@ func TestResourcesList(t *testing.T) {
155
148
})
156
149
}
157
150
151
+ func TestResourcesListAsTable (t * testing.T ) {
152
+ testCaseWithContext (t , & mcpContext {listOutput : output .Table , before : inOpenShift , after : inOpenShiftClear }, func (c * mcpContext ) {
153
+ c .withEnvTest ()
154
+ kc := c .newKubernetesClient ()
155
+ _ , _ = kc .CoreV1 ().ConfigMaps ("default" ).Create (t .Context (), & corev1.ConfigMap {
156
+ ObjectMeta : metav1.ObjectMeta {Name : "a-configmap-to-list-as-table" , Labels : map [string ]string {"resource" : "config-map" }},
157
+ Data : map [string ]string {"key" : "value" },
158
+ }, metav1.CreateOptions {})
159
+ configMapList , err := c .callTool ("resources_list" , map [string ]interface {}{"apiVersion" : "v1" , "kind" : "ConfigMap" })
160
+ t .Run ("resources_list returns ConfigMap list" , func (t * testing.T ) {
161
+ if err != nil {
162
+ t .Fatalf ("call tool failed %v" , err )
163
+ }
164
+ if configMapList .IsError {
165
+ t .Fatalf ("call tool failed" )
166
+ }
167
+ })
168
+ outConfigMapList := configMapList .Content [0 ].(mcp.TextContent ).Text
169
+ t .Run ("resources_list returns column headers for ConfigMap list" , func (t * testing.T ) {
170
+ expectedHeaders := "NAMESPACE\\ s+APIVERSION\\ s+KIND\\ s+NAME\\ s+DATA\\ s+AGE\\ s+LABELS"
171
+ if m , e := regexp .MatchString (expectedHeaders , outConfigMapList ); ! m || e != nil {
172
+ t .Fatalf ("Expected headers '%s' not found in output:\n %s" , expectedHeaders , outConfigMapList )
173
+ }
174
+ })
175
+ t .Run ("resources_list returns formatted row for a-configmap-to-list-as-table" , func (t * testing.T ) {
176
+ expectedRow := "(?<namespace>default)\\ s+" +
177
+ "(?<apiVersion>v1)\\ s+" +
178
+ "(?<kind>ConfigMap)\\ s+" +
179
+ "(?<name>a-configmap-to-list-as-table)\\ s+" +
180
+ "(?<data>1)\\ s+" +
181
+ "(?<age>\\ d+(s|m))\\ s+" +
182
+ "(?<labels>resource=config-map)"
183
+ if m , e := regexp .MatchString (expectedRow , outConfigMapList ); ! m || e != nil {
184
+ t .Fatalf ("Expected row '%s' not found in output:\n %s" , expectedRow , outConfigMapList )
185
+ }
186
+ })
187
+ // Custom Resource List
188
+ _ , _ = dynamic .NewForConfigOrDie (envTestRestConfig ).
189
+ Resource (schema.GroupVersionResource {Group : "route.openshift.io" , Version : "v1" , Resource : "routes" }).
190
+ Namespace ("default" ).
191
+ Create (c .ctx , & unstructured.Unstructured {Object : map [string ]interface {}{
192
+ "apiVersion" : "route.openshift.io/v1" ,
193
+ "kind" : "Route" ,
194
+ "metadata" : map [string ]interface {}{
195
+ "name" : "an-openshift-route-to-list-as-table" ,
196
+ },
197
+ }}, metav1.CreateOptions {})
198
+ routeList , err := c .callTool ("resources_list" , map [string ]interface {}{"apiVersion" : "route.openshift.io/v1" , "kind" : "Route" })
199
+ t .Run ("resources_list returns Route list" , func (t * testing.T ) {
200
+ if err != nil {
201
+ t .Fatalf ("call tool failed %v" , err )
202
+ }
203
+ if routeList .IsError {
204
+ t .Fatalf ("call tool failed" )
205
+ }
206
+ })
207
+ outRouteList := routeList .Content [0 ].(mcp.TextContent ).Text
208
+ t .Run ("resources_list returns column headers for Route list" , func (t * testing.T ) {
209
+ expectedHeaders := "NAMESPACE\\ s+APIVERSION\\ s+KIND\\ s+NAME\\ s+AGE\\ s+LABELS"
210
+ if m , e := regexp .MatchString (expectedHeaders , outRouteList ); ! m || e != nil {
211
+ t .Fatalf ("Expected headers '%s' not found in output:\n %s" , expectedHeaders , outRouteList )
212
+ }
213
+ })
214
+ t .Run ("resources_list returns formatted row for an-openshift-route-to-list-as-table" , func (t * testing.T ) {
215
+ expectedRow := "(?<namespace>default)\\ s+" +
216
+ "(?<apiVersion>route.openshift.io/v1)\\ s+" +
217
+ "(?<kind>Route)\\ s+" +
218
+ "(?<name>an-openshift-route-to-list-as-table)\\ s+" +
219
+ "(?<age>\\ d+(s|m))\\ s+" +
220
+ "(?<labels><none>)"
221
+ if m , e := regexp .MatchString (expectedRow , outRouteList ); ! m || e != nil {
222
+ t .Fatalf ("Expected row '%s' not found in output:\n %s" , expectedRow , outRouteList )
223
+ }
224
+ })
225
+ })
226
+ }
227
+
158
228
func TestResourcesGet (t * testing.T ) {
159
229
testCase (t , func (c * mcpContext ) {
160
230
c .withEnvTest ()
0 commit comments