Skip to content

Commit e3f8159

Browse files
committed
test(kubernetes): refactor tests for Derived manager functionality to use testify
Signed-off-by: Marc Nuri <[email protected]>
1 parent 65cc304 commit e3f8159

File tree

2 files changed

+184
-316
lines changed

2 files changed

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

0 commit comments

Comments
 (0)