55 "slices"
66 "testing"
77
8+ "github.com/BurntSushi/toml"
89 "github.com/mark3labs/mcp-go/mcp"
10+ "github.com/stretchr/testify/suite"
911 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1012 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1113 "k8s.io/apimachinery/pkg/runtime/schema"
@@ -14,108 +16,100 @@ import (
1416
1517 "github.com/containers/kubernetes-mcp-server/internal/test"
1618 "github.com/containers/kubernetes-mcp-server/pkg/config"
17- "github.com/containers/kubernetes-mcp-server/pkg/output"
1819)
1920
20- func TestNamespacesList ( t * testing. T ) {
21- testCase ( t , func ( c * mcpContext ) {
22- c . withEnvTest ()
23- toolResult , err := c . callTool ( "namespaces_list" , map [ string ] interface {}{})
24- t . Run ( "namespaces_list returns namespace list" , func ( t * testing. T ) {
25- if err != nil {
26- t . Fatalf ( "call tool failed %v " , err )
27- }
28- if toolResult . IsError {
29- t . Fatalf ( "call tool failed" )
30- }
21+ type NamespacesSuite struct {
22+ BaseMcpSuite
23+ }
24+
25+ func ( s * NamespacesSuite ) TestNamespacesList ( ) {
26+ s . InitMcpClient ()
27+ s . Run ( "namespaces_list " , func () {
28+ toolResult , err := s . CallTool ( "namespaces_list" , map [ string ] interface {}{})
29+ s . Run ( "no error" , func () {
30+ s . Nilf ( err , "call tool failed %v" , err )
31+ s . Falsef ( toolResult . IsError , "call tool failed" )
3132 })
33+ s .Require ().NotNil (toolResult , "Expected tool result from call" )
3234 var decoded []unstructured.Unstructured
3335 err = yaml .Unmarshal ([]byte (toolResult .Content [0 ].(mcp.TextContent ).Text ), & decoded )
34- t .Run ("namespaces_list has yaml content" , func (t * testing.T ) {
35- if err != nil {
36- t .Fatalf ("invalid tool result content %v" , err )
37- }
36+ s .Run ("has yaml content" , func () {
37+ s .Nilf (err , "invalid tool result content %v" , err )
3838 })
39- t .Run ("namespaces_list returns at least 3 items" , func (t * testing.T ) {
40- if len (decoded ) < 3 {
41- t .Errorf ("invalid namespace count, expected at least 3, got %v" , len (decoded ))
42- }
39+ s .Run ("returns at least 3 items" , func () {
40+ s .Truef (len (decoded ) >= 3 , "expected at least 3 items, got %v" , len (decoded ))
4341 for _ , expectedNamespace := range []string {"default" , "ns-1" , "ns-2" } {
44- idx := slices .IndexFunc (decoded , func (ns unstructured.Unstructured ) bool {
42+ s . Truef ( slices .ContainsFunc (decoded , func (ns unstructured.Unstructured ) bool {
4543 return ns .GetName () == expectedNamespace
46- })
47- if idx == - 1 {
48- t .Errorf ("namespace %s not found in the list" , expectedNamespace )
49- }
44+ }), "namespace %s not found in the list" , expectedNamespace )
5045 }
5146 })
5247 })
5348}
5449
55- func TestNamespacesListDenied ( t * testing. T ) {
56- deniedResourcesServer := test . Must ( config . ReadToml ([]byte (`
50+ func ( s * NamespacesSuite ) TestNamespacesListDenied ( ) {
51+ s . Require (). NoError ( toml . Unmarshal ([]byte (`
5752 denied_resources = [ { version = "v1", kind = "Namespace" } ]
58- ` )))
59- testCaseWithContext (t , & mcpContext {staticConfig : deniedResourcesServer }, func (c * mcpContext ) {
60- c .withEnvTest ()
61- namespacesList , _ := c .callTool ("namespaces_list" , map [string ]interface {}{})
62- t .Run ("namespaces_list has error" , func (t * testing.T ) {
63- if ! namespacesList .IsError {
64- t .Fatalf ("call tool should fail" )
65- }
53+ ` ), s .Cfg ), "Expected to parse denied resources config" )
54+ s .InitMcpClient ()
55+ s .Run ("namespaces_list (denied)" , func () {
56+ toolResult , err := s .CallTool ("namespaces_list" , map [string ]interface {}{})
57+ s .Run ("has error" , func () {
58+ s .Truef (toolResult .IsError , "call tool should fail" )
59+ s .Nilf (err , "call tool should not return error object" )
6660 })
67- t .Run ("namespaces_list describes denial" , func (t * testing. T ) {
61+ s .Run ("describes denial" , func () {
6862 expectedMessage := "failed to list namespaces: resource not allowed: /v1, Kind=Namespace"
69- if namespacesList .Content [0 ].(mcp.TextContent ).Text != expectedMessage {
70- t .Fatalf ("expected descriptive error '%s', got %v" , expectedMessage , namespacesList .Content [0 ].(mcp.TextContent ).Text )
71- }
63+ s .Equalf (expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text ,
64+ "expected descriptive error '%s', got %v" , expectedMessage , toolResult .Content [0 ].(mcp.TextContent ).Text )
7265 })
7366 })
7467}
7568
76- func TestNamespacesListAsTable (t * testing.T ) {
77- testCaseWithContext (t , & mcpContext {listOutput : output .Table }, func (c * mcpContext ) {
78- c .withEnvTest ()
79- toolResult , err := c .callTool ("namespaces_list" , map [string ]interface {}{})
80- t .Run ("namespaces_list returns namespace list" , func (t * testing.T ) {
81- if err != nil {
82- t .Fatalf ("call tool failed %v" , err )
83- }
84- if toolResult .IsError {
85- t .Fatalf ("call tool failed" )
86- }
69+ func (s * NamespacesSuite ) TestNamespacesListAsTable () {
70+ s .Cfg .ListOutput = "table"
71+ s .InitMcpClient ()
72+ s .Run ("namespaces_list (list_output=table)" , func () {
73+ toolResult , err := s .CallTool ("namespaces_list" , map [string ]interface {}{})
74+ s .Run ("no error" , func () {
75+ s .Nilf (err , "call tool failed %v" , err )
76+ s .Falsef (toolResult .IsError , "call tool failed" )
8777 })
78+ s .Require ().NotNil (toolResult , "Expected tool result from call" )
8879 out := toolResult .Content [0 ].(mcp.TextContent ).Text
89- t .Run ("namespaces_list returns column headers" , func (t * testing. T ) {
80+ s .Run ("returns column headers" , func () {
9081 expectedHeaders := "APIVERSION\\ s+KIND\\ s+NAME\\ s+STATUS\\ s+AGE\\ s+LABELS"
91- if m , e := regexp .MatchString (expectedHeaders , out ); ! m || e != nil {
92- t . Fatalf ( "Expected headers '%s' not found in output:\n %s" , expectedHeaders , out )
93- }
82+ m , e := regexp .MatchString (expectedHeaders , out )
83+ s . Truef ( m , "Expected headers '%s' not found in output:\n %s" , expectedHeaders , out )
84+ s . NoErrorf ( e , "Error matching headers regex: %v" , e )
9485 })
95- t .Run ("namespaces_list returns formatted row for ns-1" , func (t * testing. T ) {
86+ s .Run ("returns formatted row for ns-1" , func () {
9687 expectedRow := "(?<apiVersion>v1)\\ s+" +
9788 "(?<kind>Namespace)\\ s+" +
9889 "(?<name>ns-1)\\ s+" +
9990 "(?<status>Active)\\ s+" +
10091 "(?<age>(\\ d+m)?(\\ d+s)?)\\ s+" +
10192 "(?<labels>kubernetes.io/metadata.name=ns-1)"
102- if m , e := regexp .MatchString (expectedRow , out ); ! m || e != nil {
103- t . Fatalf ( "Expected row '%s' not found in output:\n %s" , expectedRow , out )
104- }
93+ m , e := regexp .MatchString (expectedRow , out )
94+ s . Truef ( m , "Expected row '%s' not found in output:\n %s" , expectedRow , out )
95+ s . NoErrorf ( e , "Error matching ns-1 regex: %v" , e )
10596 })
106- t .Run ("namespaces_list returns formatted row for ns-2" , func (t * testing. T ) {
97+ s .Run ("returns formatted row for ns-2" , func () {
10798 expectedRow := "(?<apiVersion>v1)\\ s+" +
10899 "(?<kind>Namespace)\\ s+" +
109100 "(?<name>ns-2)\\ s+" +
110101 "(?<status>Active)\\ s+" +
111102 "(?<age>(\\ d+m)?(\\ d+s)?)\\ s+" +
112103 "(?<labels>kubernetes.io/metadata.name=ns-2)"
113- if m , e := regexp .MatchString (expectedRow , out ); ! m || e != nil {
114- t . Fatalf ( "Expected row '%s' not found in output:\n %s" , expectedRow , out )
115- }
104+ m , e := regexp .MatchString (expectedRow , out )
105+ s . Truef ( m , "Expected row '%s' not found in output:\n %s" , expectedRow , out )
106+ s . NoErrorf ( e , "Error matching ns-2 regex: %v" , e )
116107 })
117108 })
109+ }
118110
111+ func TestNamespaces (t * testing.T ) {
112+ suite .Run (t , new (NamespacesSuite ))
119113}
120114
121115func TestProjectsListInOpenShift (t * testing.T ) {
0 commit comments