test(kubernetes): refactor tests for Derived manager functionality to use testify (#369)

Signed-off-by: Marc Nuri <marc@marcnuri.com>
This commit is contained in:
Marc Nuri
2025-10-13 13:12:29 +02:00
committed by GitHub
parent 68619b57ad
commit 1e154d7587
2 changed files with 185 additions and 316 deletions

View File

@@ -0,0 +1,185 @@
package kubernetes
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/containers/kubernetes-mcp-server/internal/test"
"github.com/containers/kubernetes-mcp-server/pkg/config"
"github.com/stretchr/testify/suite"
)
type DerivedTestSuite struct {
suite.Suite
}
func (s *DerivedTestSuite) TestKubeConfig() {
// Create a temporary kubeconfig file for testing
tempDir := s.T().TempDir()
kubeconfigPath := filepath.Join(tempDir, "config")
kubeconfigContent := `
apiVersion: v1
kind: Config
clusters:
- cluster:
server: https://test-cluster.example.com
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
name: test-context
current-context: test-context
users:
- name: test-user
user:
username: test-username
password: test-password
`
err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644)
s.Require().NoError(err, "failed to create kubeconfig file")
s.Run("with no RequireOAuth (default) config", func() {
testStaticConfig := test.Must(config.ReadToml([]byte(`
kubeconfig = "` + strings.ReplaceAll(kubeconfigPath, `\`, `\\`) + `"
`)))
s.Run("without authorization header returns original manager", func() {
testManager, err := NewManager(testStaticConfig)
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
s.T().Cleanup(testManager.Close)
derived, err := testManager.Derived(s.T().Context())
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
s.Equal(derived.manager, testManager, "expected original manager, got different manager")
})
s.Run("with invalid authorization header returns original manager", func() {
testManager, err := NewManager(testStaticConfig)
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
s.T().Cleanup(testManager.Close)
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "invalid-token")
derived, err := testManager.Derived(ctx)
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
s.Equal(derived.manager, testManager, "expected original manager, got different manager")
})
s.Run("with valid bearer token creates derived manager with correct configuration", func() {
testManager, err := NewManager(testStaticConfig)
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
s.T().Cleanup(testManager.Close)
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "Bearer aiTana-julIA")
derived, err := testManager.Derived(ctx)
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
s.NotEqual(derived.manager, testManager, "expected new derived manager, got original manager")
s.Equal(derived.manager.staticConfig, testStaticConfig, "staticConfig not properly wired to derived manager")
s.Run("RestConfig is correctly copied and sensitive fields are omitted", func() {
derivedCfg := derived.manager.cfg
s.Require().NotNil(derivedCfg, "derived config is nil")
originalCfg := testManager.cfg
s.Equalf(originalCfg.Host, derivedCfg.Host, "expected Host %s, got %s", originalCfg.Host, derivedCfg.Host)
s.Equalf(originalCfg.APIPath, derivedCfg.APIPath, "expected APIPath %s, got %s", originalCfg.APIPath, derivedCfg.APIPath)
s.Equalf(originalCfg.QPS, derivedCfg.QPS, "expected QPS %f, got %f", originalCfg.QPS, derivedCfg.QPS)
s.Equalf(originalCfg.Burst, derivedCfg.Burst, "expected Burst %d, got %d", originalCfg.Burst, derivedCfg.Burst)
s.Equalf(originalCfg.Timeout, derivedCfg.Timeout, "expected Timeout %v, got %v", originalCfg.Timeout, derivedCfg.Timeout)
s.Equalf(originalCfg.Insecure, derivedCfg.Insecure, "expected TLS Insecure %v, got %v", originalCfg.Insecure, derivedCfg.Insecure)
s.Equalf(originalCfg.ServerName, derivedCfg.ServerName, "expected TLS ServerName %s, got %s", originalCfg.ServerName, derivedCfg.ServerName)
s.Equalf(originalCfg.CAFile, derivedCfg.CAFile, "expected TLS CAFile %s, got %s", originalCfg.CAFile, derivedCfg.CAFile)
s.Equalf(string(originalCfg.CAData), string(derivedCfg.CAData), "expected TLS CAData %s, got %s", string(originalCfg.CAData), string(derivedCfg.CAData))
s.Equalf("aiTana-julIA", derivedCfg.BearerToken, "expected BearerToken %s, got %s", "aiTana-julIA", derivedCfg.BearerToken)
s.Equalf("kubernetes-mcp-server/bearer-token-auth", derivedCfg.UserAgent, "expected UserAgent \"kubernetes-mcp-server/bearer-token-auth\", got %s", derivedCfg.UserAgent)
// Verify that sensitive fields are NOT copied to prevent credential leakage
// The derived config should only use the bearer token from the Authorization header
// and not inherit any authentication credentials from the original kubeconfig
s.Emptyf(derivedCfg.CertFile, "expected TLS CertFile to be empty, got %s", derivedCfg.CertFile)
s.Emptyf(derivedCfg.KeyFile, "expected TLS KeyFile to be empty, got %s", derivedCfg.KeyFile)
s.Emptyf(len(derivedCfg.CertData), "expected TLS CertData to be empty, got %v", derivedCfg.CertData)
s.Emptyf(len(derivedCfg.KeyData), "expected TLS KeyData to be empty, got %v", derivedCfg.KeyData)
s.Emptyf(derivedCfg.Username, "expected Username to be empty, got %s", derivedCfg.Username)
s.Emptyf(derivedCfg.Password, "expected Password to be empty, got %s", derivedCfg.Password)
s.Nilf(derivedCfg.AuthProvider, "expected AuthProvider to be nil, got %v", derivedCfg.AuthProvider)
s.Nilf(derivedCfg.ExecProvider, "expected ExecProvider to be nil, got %v", derivedCfg.ExecProvider)
s.Emptyf(derivedCfg.BearerTokenFile, "expected BearerTokenFile to be empty, got %s", derivedCfg.BearerTokenFile)
s.Emptyf(derivedCfg.Impersonate.UserName, "expected Impersonate.UserName to be empty, got %s", derivedCfg.Impersonate.UserName)
// Verify that the original manager still has the sensitive data
s.Falsef(originalCfg.Username == "" && originalCfg.Password == "", "original kubeconfig shouldn't be modified")
})
s.Run("derived manager has initialized clients", func() {
// Verify that the derived manager has proper clients initialized
s.NotNilf(derived.manager.accessControlClientSet, "expected accessControlClientSet to be initialized")
s.Equalf(testStaticConfig, derived.manager.accessControlClientSet.staticConfig, "staticConfig not properly wired to derived manager")
s.NotNilf(derived.manager.discoveryClient, "expected discoveryClient to be initialized")
s.NotNilf(derived.manager.accessControlRESTMapper, "expected accessControlRESTMapper to be initialized")
s.Equalf(testStaticConfig, derived.manager.accessControlRESTMapper.staticConfig, "staticConfig not properly wired to derived manager")
s.NotNilf(derived.manager.dynamicClient, "expected dynamicClient to be initialized")
})
})
})
s.Run("with RequireOAuth=true", func() {
testStaticConfig := test.Must(config.ReadToml([]byte(`
kubeconfig = "` + strings.ReplaceAll(kubeconfigPath, `\`, `\\`) + `"
require_oauth = true
`)))
s.Run("with no authorization header returns oauth token required error", func() {
testManager, err := NewManager(testStaticConfig)
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
s.T().Cleanup(testManager.Close)
derived, err := testManager.Derived(s.T().Context())
s.Require().Error(err, "expected error for missing oauth token, got nil")
s.EqualError(err, "oauth token required", "expected error 'oauth token required', got %s", err.Error())
s.Nil(derived, "expected nil derived manager when oauth token required")
})
s.Run("with invalid authorization header returns oauth token required error", func() {
testManager, err := NewManager(testStaticConfig)
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
s.T().Cleanup(testManager.Close)
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "invalid-token")
derived, err := testManager.Derived(ctx)
s.Require().Error(err, "expected error for invalid oauth token, got nil")
s.EqualError(err, "oauth token required", "expected error 'oauth token required', got %s", err.Error())
s.Nil(derived, "expected nil derived manager when oauth token required")
})
s.Run("with valid bearer token creates derived manager", func() {
testManager, err := NewManager(testStaticConfig)
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
s.T().Cleanup(testManager.Close)
ctx := context.WithValue(s.T().Context(), HeaderKey("Authorization"), "Bearer aiTana-julIA")
derived, err := testManager.Derived(ctx)
s.Require().NoErrorf(err, "failed to create derived manager: %v", err)
s.NotEqual(derived.manager, testManager, "expected new derived manager, got original manager")
s.Equal(derived.manager.staticConfig, testStaticConfig, "staticConfig not properly wired to derived manager")
derivedCfg := derived.manager.cfg
s.Require().NotNil(derivedCfg, "derived config is nil")
s.Equalf("aiTana-julIA", derivedCfg.BearerToken, "expected BearerToken %s, got %s", "aiTana-julIA", derivedCfg.BearerToken)
})
})
}
func TestDerived(t *testing.T) {
suite.Run(t, new(DerivedTestSuite))
}

View File

@@ -1,316 +0,0 @@
package kubernetes
import (
"context"
"os"
"path"
"testing"
"github.com/containers/kubernetes-mcp-server/pkg/config"
)
func TestManager_Derived(t *testing.T) {
// Create a temporary kubeconfig file for testing
tempDir := t.TempDir()
kubeconfigPath := path.Join(tempDir, "config")
kubeconfigContent := `
apiVersion: v1
kind: Config
clusters:
- cluster:
server: https://test-cluster.example.com
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
name: test-context
current-context: test-context
users:
- name: test-user
user:
username: test-username
password: test-password
`
if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
t.Fatalf("failed to create kubeconfig file: %v", err)
}
t.Run("without authorization header returns original manager", func(t *testing.T) {
testStaticConfig := &config.StaticConfig{
KubeConfig: kubeconfigPath,
DisabledTools: []string{"configuration_view"},
DeniedResources: []config.GroupVersionKind{
{Group: "apps", Version: "v1", Kind: "Deployment"},
},
}
testManager, err := NewManager(testStaticConfig)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
defer testManager.Close()
ctx := context.Background()
derived, err := testManager.Derived(ctx)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
if derived.manager != testManager {
t.Errorf("expected original manager, got different manager")
}
})
t.Run("with invalid authorization header returns original manager", func(t *testing.T) {
testStaticConfig := &config.StaticConfig{
KubeConfig: kubeconfigPath,
DisabledTools: []string{"configuration_view"},
DeniedResources: []config.GroupVersionKind{
{Group: "apps", Version: "v1", Kind: "Deployment"},
},
}
testManager, err := NewManager(testStaticConfig)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
defer testManager.Close()
ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "invalid-token")
derived, err := testManager.Derived(ctx)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
if derived.manager != testManager {
t.Errorf("expected original manager, got different manager")
}
})
t.Run("with valid bearer token creates derived manager with correct configuration", func(t *testing.T) {
testStaticConfig := &config.StaticConfig{
KubeConfig: kubeconfigPath,
DisabledTools: []string{"configuration_view"},
DeniedResources: []config.GroupVersionKind{
{Group: "apps", Version: "v1", Kind: "Deployment"},
},
}
testManager, err := NewManager(testStaticConfig)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
defer testManager.Close()
testBearerToken := "test-bearer-token-123"
ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "Bearer "+testBearerToken)
derived, err := testManager.Derived(ctx)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
if derived.manager == testManager {
t.Errorf("expected new derived manager, got original manager")
}
if derived.manager.staticConfig != testStaticConfig {
t.Errorf("staticConfig not properly wired to derived manager")
}
derivedCfg := derived.manager.cfg
if derivedCfg == nil {
t.Fatalf("derived config is nil")
}
originalCfg := testManager.cfg
if derivedCfg.Host != originalCfg.Host {
t.Errorf("expected Host %s, got %s", originalCfg.Host, derivedCfg.Host)
}
if derivedCfg.APIPath != originalCfg.APIPath {
t.Errorf("expected APIPath %s, got %s", originalCfg.APIPath, derivedCfg.APIPath)
}
if derivedCfg.QPS != originalCfg.QPS {
t.Errorf("expected QPS %f, got %f", originalCfg.QPS, derivedCfg.QPS)
}
if derivedCfg.Burst != originalCfg.Burst {
t.Errorf("expected Burst %d, got %d", originalCfg.Burst, derivedCfg.Burst)
}
if derivedCfg.Timeout != originalCfg.Timeout {
t.Errorf("expected Timeout %v, got %v", originalCfg.Timeout, derivedCfg.Timeout)
}
if derivedCfg.Insecure != originalCfg.Insecure {
t.Errorf("expected TLS Insecure %v, got %v", originalCfg.Insecure, derivedCfg.Insecure)
}
if derivedCfg.ServerName != originalCfg.ServerName {
t.Errorf("expected TLS ServerName %s, got %s", originalCfg.ServerName, derivedCfg.ServerName)
}
if derivedCfg.CAFile != originalCfg.CAFile {
t.Errorf("expected TLS CAFile %s, got %s", originalCfg.CAFile, derivedCfg.CAFile)
}
if string(derivedCfg.CAData) != string(originalCfg.CAData) {
t.Errorf("expected TLS CAData %s, got %s", string(originalCfg.CAData), string(derivedCfg.CAData))
}
if derivedCfg.BearerToken != testBearerToken {
t.Errorf("expected BearerToken %s, got %s", testBearerToken, derivedCfg.BearerToken)
}
if derivedCfg.UserAgent != CustomUserAgent {
t.Errorf("expected UserAgent %s, got %s", CustomUserAgent, derivedCfg.UserAgent)
}
// Verify that sensitive fields are NOT copied to prevent credential leakage
// The derived config should only use the bearer token from the Authorization header
// and not inherit any authentication credentials from the original kubeconfig
if derivedCfg.CertFile != "" {
t.Errorf("expected TLS CertFile to be empty, got %s", derivedCfg.CertFile)
}
if derivedCfg.KeyFile != "" {
t.Errorf("expected TLS KeyFile to be empty, got %s", derivedCfg.KeyFile)
}
if len(derivedCfg.CertData) != 0 {
t.Errorf("expected TLS CertData to be empty, got %v", derivedCfg.CertData)
}
if len(derivedCfg.KeyData) != 0 {
t.Errorf("expected TLS KeyData to be empty, got %v", derivedCfg.KeyData)
}
if derivedCfg.Username != "" {
t.Errorf("expected Username to be empty, got %s", derivedCfg.Username)
}
if derivedCfg.Password != "" {
t.Errorf("expected Password to be empty, got %s", derivedCfg.Password)
}
if derivedCfg.AuthProvider != nil {
t.Errorf("expected AuthProvider to be nil, got %v", derivedCfg.AuthProvider)
}
if derivedCfg.ExecProvider != nil {
t.Errorf("expected ExecProvider to be nil, got %v", derivedCfg.ExecProvider)
}
if derivedCfg.BearerTokenFile != "" {
t.Errorf("expected BearerTokenFile to be empty, got %s", derivedCfg.BearerTokenFile)
}
if derivedCfg.Impersonate.UserName != "" {
t.Errorf("expected Impersonate.UserName to be empty, got %s", derivedCfg.Impersonate.UserName)
}
// Verify that the original manager still has the sensitive data
if originalCfg.Username == "" && originalCfg.Password == "" {
t.Logf("original kubeconfig shouldn't be modified")
}
// Verify that the derived manager has proper clients initialized
if derived.manager.accessControlClientSet == nil {
t.Error("expected accessControlClientSet to be initialized")
}
if derived.manager.accessControlClientSet.staticConfig != testStaticConfig {
t.Errorf("staticConfig not properly wired to derived manager")
}
if derived.manager.discoveryClient == nil {
t.Error("expected discoveryClient to be initialized")
}
if derived.manager.accessControlRESTMapper == nil {
t.Error("expected accessControlRESTMapper to be initialized")
}
if derived.manager.accessControlRESTMapper.staticConfig != testStaticConfig {
t.Errorf("staticConfig not properly wired to derived manager")
}
if derived.manager.dynamicClient == nil {
t.Error("expected dynamicClient to be initialized")
}
})
t.Run("with RequireOAuth=true and no authorization header returns oauth token required error", func(t *testing.T) {
testStaticConfig := &config.StaticConfig{
KubeConfig: kubeconfigPath,
RequireOAuth: true,
DisabledTools: []string{"configuration_view"},
DeniedResources: []config.GroupVersionKind{
{Group: "apps", Version: "v1", Kind: "Deployment"},
},
}
testManager, err := NewManager(testStaticConfig)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
defer testManager.Close()
ctx := context.Background()
derived, err := testManager.Derived(ctx)
if err == nil {
t.Fatal("expected error for missing oauth token, got nil")
}
if err.Error() != "oauth token required" {
t.Fatalf("expected error 'oauth token required', got %s", err.Error())
}
if derived != nil {
t.Error("expected nil derived manager when oauth token required")
}
})
t.Run("with RequireOAuth=true and invalid authorization header returns oauth token required error", func(t *testing.T) {
testStaticConfig := &config.StaticConfig{
KubeConfig: kubeconfigPath,
RequireOAuth: true,
DisabledTools: []string{"configuration_view"},
DeniedResources: []config.GroupVersionKind{
{Group: "apps", Version: "v1", Kind: "Deployment"},
},
}
testManager, err := NewManager(testStaticConfig)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
defer testManager.Close()
ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "invalid-token")
derived, err := testManager.Derived(ctx)
if err == nil {
t.Fatal("expected error for invalid oauth token, got nil")
}
if err.Error() != "oauth token required" {
t.Fatalf("expected error 'oauth token required', got %s", err.Error())
}
if derived != nil {
t.Error("expected nil derived manager when oauth token required")
}
})
t.Run("with RequireOAuth=true and valid bearer token creates derived manager", func(t *testing.T) {
testStaticConfig := &config.StaticConfig{
KubeConfig: kubeconfigPath,
RequireOAuth: true,
DisabledTools: []string{"configuration_view"},
DeniedResources: []config.GroupVersionKind{
{Group: "apps", Version: "v1", Kind: "Deployment"},
},
}
testManager, err := NewManager(testStaticConfig)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
defer testManager.Close()
testBearerToken := "test-bearer-token-123"
ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "Bearer "+testBearerToken)
derived, err := testManager.Derived(ctx)
if err != nil {
t.Fatalf("failed to create manager: %v", err)
}
if derived.manager == testManager {
t.Error("expected new derived manager, got original manager")
}
if derived.manager.staticConfig != testStaticConfig {
t.Error("staticConfig not properly wired to derived manager")
}
derivedCfg := derived.manager.cfg
if derivedCfg == nil {
t.Fatal("derived config is nil")
}
if derivedCfg.BearerToken != testBearerToken {
t.Errorf("expected BearerToken %s, got %s", testBearerToken, derivedCfg.BearerToken)
}
})
}