Skip to content

Commit 1e154d7

Browse files
authored
test(kubernetes): refactor tests for Derived manager functionality to use testify (#369)
Signed-off-by: Marc Nuri <[email protected]>
1 parent 68619b5 commit 1e154d7

File tree

2 files changed

+185
-316
lines changed

2 files changed

+185
-316
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package kubernetes
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"github.com/containers/kubernetes-mcp-server/internal/test"
11+
"github.com/containers/kubernetes-mcp-server/pkg/config"
12+
"github.com/stretchr/testify/suite"
13+
)
14+
15+
type DerivedTestSuite struct {
16+
suite.Suite
17+
}
18+
19+
func (s *DerivedTestSuite) TestKubeConfig() {
20+
// Create a temporary kubeconfig file for testing
21+
tempDir := s.T().TempDir()
22+
kubeconfigPath := filepath.Join(tempDir, "config")
23+
kubeconfigContent := `
24+
apiVersion: v1
25+
kind: Config
26+
clusters:
27+
- cluster:
28+
server: https://test-cluster.example.com
29+
name: test-cluster
30+
contexts:
31+
- context:
32+
cluster: test-cluster
33+
user: test-user
34+
name: test-context
35+
current-context: test-context
36+
users:
37+
- name: test-user
38+
user:
39+
username: test-username
40+
password: test-password
41+
`
42+
err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644)
43+
s.Require().NoError(err, "failed to create kubeconfig file")
44+
45+
s.Run("with no RequireOAuth (default) config", func() {
46+
testStaticConfig := test.Must(config.ReadToml([]byte(`
47+
kubeconfig = "` + strings.ReplaceAll(kubeconfigPath, `\`, `\\`) + `"
48+
`)))
49+
s.Run("without authorization header returns original manager", func() {
50+
testManager, err := NewManager(testStaticConfig)
51+
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
52+
s.T().Cleanup(testManager.Close)
53+
54+
derived, err := testManager.Derived(s.T().Context())
55+
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
56+
57+
s.Equal(derived.manager, testManager, "expected original manager, got different manager")
58+
})
59+
60+
s.Run("with invalid authorization header returns original manager", func() {
61+
testManager, err := NewManager(testStaticConfig)
62+
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
63+
s.T().Cleanup(testManager.Close)
64+
65+
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "invalid-token")
66+
derived, err := testManager.Derived(ctx)
67+
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
68+
69+
s.Equal(derived.manager, testManager, "expected original manager, got different manager")
70+
})
71+
72+
s.Run("with valid bearer token creates derived manager with correct configuration", func() {
73+
testManager, err := NewManager(testStaticConfig)
74+
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
75+
s.T().Cleanup(testManager.Close)
76+
77+
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "Bearer aiTana-julIA")
78+
derived, err := testManager.Derived(ctx)
79+
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
80+
81+
s.NotEqual(derived.manager, testManager, "expected new derived manager, got original manager")
82+
s.Equal(derived.manager.staticConfig, testStaticConfig, "staticConfig not properly wired to derived manager")
83+
84+
s.Run("RestConfig is correctly copied and sensitive fields are omitted", func() {
85+
derivedCfg := derived.manager.cfg
86+
s.Require().NotNil(derivedCfg, "derived config is nil")
87+
88+
originalCfg := testManager.cfg
89+
s.Equalf(originalCfg.Host, derivedCfg.Host, "expected Host %s, got %s", originalCfg.Host, derivedCfg.Host)
90+
s.Equalf(originalCfg.APIPath, derivedCfg.APIPath, "expected APIPath %s, got %s", originalCfg.APIPath, derivedCfg.APIPath)
91+
s.Equalf(originalCfg.QPS, derivedCfg.QPS, "expected QPS %f, got %f", originalCfg.QPS, derivedCfg.QPS)
92+
s.Equalf(originalCfg.Burst, derivedCfg.Burst, "expected Burst %d, got %d", originalCfg.Burst, derivedCfg.Burst)
93+
s.Equalf(originalCfg.Timeout, derivedCfg.Timeout, "expected Timeout %v, got %v", originalCfg.Timeout, derivedCfg.Timeout)
94+
95+
s.Equalf(originalCfg.Insecure, derivedCfg.Insecure, "expected TLS Insecure %v, got %v", originalCfg.Insecure, derivedCfg.Insecure)
96+
s.Equalf(originalCfg.ServerName, derivedCfg.ServerName, "expected TLS ServerName %s, got %s", originalCfg.ServerName, derivedCfg.ServerName)
97+
s.Equalf(originalCfg.CAFile, derivedCfg.CAFile, "expected TLS CAFile %s, got %s", originalCfg.CAFile, derivedCfg.CAFile)
98+
s.Equalf(string(originalCfg.CAData), string(derivedCfg.CAData), "expected TLS CAData %s, got %s", string(originalCfg.CAData), string(derivedCfg.CAData))
99+
100+
s.Equalf("aiTana-julIA", derivedCfg.BearerToken, "expected BearerToken %s, got %s", "aiTana-julIA", derivedCfg.BearerToken)
101+
s.Equalf("kubernetes-mcp-server/bearer-token-auth", derivedCfg.UserAgent, "expected UserAgent \"kubernetes-mcp-server/bearer-token-auth\", got %s", derivedCfg.UserAgent)
102+
103+
// Verify that sensitive fields are NOT copied to prevent credential leakage
104+
// The derived config should only use the bearer token from the Authorization header
105+
// and not inherit any authentication credentials from the original kubeconfig
106+
s.Emptyf(derivedCfg.CertFile, "expected TLS CertFile to be empty, got %s", derivedCfg.CertFile)
107+
s.Emptyf(derivedCfg.KeyFile, "expected TLS KeyFile to be empty, got %s", derivedCfg.KeyFile)
108+
s.Emptyf(len(derivedCfg.CertData), "expected TLS CertData to be empty, got %v", derivedCfg.CertData)
109+
s.Emptyf(len(derivedCfg.KeyData), "expected TLS KeyData to be empty, got %v", derivedCfg.KeyData)
110+
111+
s.Emptyf(derivedCfg.Username, "expected Username to be empty, got %s", derivedCfg.Username)
112+
s.Emptyf(derivedCfg.Password, "expected Password to be empty, got %s", derivedCfg.Password)
113+
s.Nilf(derivedCfg.AuthProvider, "expected AuthProvider to be nil, got %v", derivedCfg.AuthProvider)
114+
s.Nilf(derivedCfg.ExecProvider, "expected ExecProvider to be nil, got %v", derivedCfg.ExecProvider)
115+
s.Emptyf(derivedCfg.BearerTokenFile, "expected BearerTokenFile to be empty, got %s", derivedCfg.BearerTokenFile)
116+
s.Emptyf(derivedCfg.Impersonate.UserName, "expected Impersonate.UserName to be empty, got %s", derivedCfg.Impersonate.UserName)
117+
118+
// Verify that the original manager still has the sensitive data
119+
s.Falsef(originalCfg.Username == "" && originalCfg.Password == "", "original kubeconfig shouldn't be modified")
120+
121+
})
122+
s.Run("derived manager has initialized clients", func() {
123+
// Verify that the derived manager has proper clients initialized
124+
s.NotNilf(derived.manager.accessControlClientSet, "expected accessControlClientSet to be initialized")
125+
s.Equalf(testStaticConfig, derived.manager.accessControlClientSet.staticConfig, "staticConfig not properly wired to derived manager")
126+
s.NotNilf(derived.manager.discoveryClient, "expected discoveryClient to be initialized")
127+
s.NotNilf(derived.manager.accessControlRESTMapper, "expected accessControlRESTMapper to be initialized")
128+
s.Equalf(testStaticConfig, derived.manager.accessControlRESTMapper.staticConfig, "staticConfig not properly wired to derived manager")
129+
s.NotNilf(derived.manager.dynamicClient, "expected dynamicClient to be initialized")
130+
})
131+
})
132+
})
133+
134+
s.Run("with RequireOAuth=true", func() {
135+
testStaticConfig := test.Must(config.ReadToml([]byte(`
136+
kubeconfig = "` + strings.ReplaceAll(kubeconfigPath, `\`, `\\`) + `"
137+
require_oauth = true
138+
`)))
139+
140+
s.Run("with no authorization header returns oauth token required error", func() {
141+
testManager, err := NewManager(testStaticConfig)
142+
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
143+
s.T().Cleanup(testManager.Close)
144+
145+
derived, err := testManager.Derived(s.T().Context())
146+
s.Require().Error(err, "expected error for missing oauth token, got nil")
147+
s.EqualError(err, "oauth token required", "expected error 'oauth token required', got %s", err.Error())
148+
s.Nil(derived, "expected nil derived manager when oauth token required")
149+
})
150+
151+
s.Run("with invalid authorization header returns oauth token required error", func() {
152+
testManager, err := NewManager(testStaticConfig)
153+
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
154+
s.T().Cleanup(testManager.Close)
155+
156+
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "invalid-token")
157+
derived, err := testManager.Derived(ctx)
158+
s.Require().Error(err, "expected error for invalid oauth token, got nil")
159+
s.EqualError(err, "oauth token required", "expected error 'oauth token required', got %s", err.Error())
160+
s.Nil(derived, "expected nil derived manager when oauth token required")
161+
})
162+
163+
s.Run("with valid bearer token creates derived manager", func() {
164+
testManager, err := NewManager(testStaticConfig)
165+
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
166+
s.T().Cleanup(testManager.Close)
167+
168+
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "Bearer aiTana-julIA")
169+
derived, err := testManager.Derived(ctx)
170+
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
171+
172+
s.NotEqual(derived.manager, testManager, "expected new derived manager, got original manager")
173+
s.Equal(derived.manager.staticConfig, testStaticConfig, "staticConfig not properly wired to derived manager")
174+
175+
derivedCfg := derived.manager.cfg
176+
s.Require().NotNil(derivedCfg, "derived config is nil")
177+
178+
s.Equalf("aiTana-julIA", derivedCfg.BearerToken, "expected BearerToken %s, got %s", "aiTana-julIA", derivedCfg.BearerToken)
179+
})
180+
})
181+
}
182+
183+
func TestDerived(t *testing.T) {
184+
suite.Run(t, new(DerivedTestSuite))
185+
}

0 commit comments

Comments
 (0)