mirror of
https://github.com/openshift/openshift-mcp-server.git
synced 2025-10-17 14:27:48 +03:00
test(mcp): refactor helm toolset tests (#333)
Refactor tests to new approach before adding more functionality. Signed-off-by: Marc Nuri <marc@marcnuri.com>
This commit is contained in:
@@ -104,7 +104,7 @@ func (s *EventsSuite) TestEventsList() {
|
||||
func (s *EventsSuite) TestEventsListDenied() {
|
||||
s.Require().NoError(toml.Unmarshal([]byte(`
|
||||
denied_resources = [ { version = "v1", kind = "Event" } ]
|
||||
`), s.Cfg), "Expected to parse denied resources config")
|
||||
`), s.Cfg), "Expected to parse denied resources config")
|
||||
s.InitMcpClient()
|
||||
s.Run("events_list (denied)", func() {
|
||||
toolResult, err := s.CallTool("events_list", map[string]interface{}{})
|
||||
|
||||
@@ -8,255 +8,260 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/kubernetes-mcp-server/internal/test"
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/stretchr/testify/suite"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/containers/kubernetes-mcp-server/pkg/config"
|
||||
)
|
||||
|
||||
func TestHelmInstall(t *testing.T) {
|
||||
testCase(t, func(c *mcpContext) {
|
||||
c.withEnvTest()
|
||||
type HelmSuite struct {
|
||||
BaseMcpSuite
|
||||
}
|
||||
|
||||
func (s *HelmSuite) SetupTest() {
|
||||
s.BaseMcpSuite.SetupTest()
|
||||
clearHelmReleases(s.T().Context(), kubernetes.NewForConfigOrDie(envTestRestConfig))
|
||||
}
|
||||
|
||||
func (s *HelmSuite) TestHelmInstall() {
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_install(chart=helm-chart-no-op)", func() {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
chartPath := filepath.Join(filepath.Dir(file), "testdata", "helm-chart-no-op")
|
||||
toolResult, err := c.callTool("helm_install", map[string]interface{}{
|
||||
toolResult, err := s.CallTool("helm_install", map[string]interface{}{
|
||||
"chart": chartPath,
|
||||
})
|
||||
t.Run("helm_install with local chart and no release name, returns installed chart", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
s.Run("returns installed chart", func() {
|
||||
var decoded []map[string]interface{}
|
||||
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid tool result content %v", err)
|
||||
}
|
||||
if !strings.HasPrefix(decoded[0]["name"].(string), "helm-chart-no-op-") {
|
||||
t.Fatalf("invalid helm install name, expected no-op-*, got %v", decoded[0]["name"])
|
||||
}
|
||||
if decoded[0]["namespace"] != "default" {
|
||||
t.Fatalf("invalid helm install namespace, expected default, got %v", decoded[0]["namespace"])
|
||||
}
|
||||
if decoded[0]["chart"] != "no-op" {
|
||||
t.Fatalf("invalid helm install name, expected release name, got empty")
|
||||
}
|
||||
if decoded[0]["chartVersion"] != "1.33.7" {
|
||||
t.Fatalf("invalid helm install version, expected 1.33.7, got empty")
|
||||
}
|
||||
if decoded[0]["status"] != "deployed" {
|
||||
t.Fatalf("invalid helm install status, expected deployed, got %v", decoded[0]["status"])
|
||||
}
|
||||
if decoded[0]["revision"] != float64(1) {
|
||||
t.Fatalf("invalid helm install revision, expected 1, got %v", decoded[0]["revision"])
|
||||
}
|
||||
s.Run("has yaml content", func() {
|
||||
s.Nilf(err, "invalid tool result content %v", err)
|
||||
})
|
||||
s.Run("has 1 item", func() {
|
||||
s.Lenf(decoded, 1, "invalid helm install count, expected 1, got %v", len(decoded))
|
||||
})
|
||||
s.Run("has valid name", func() {
|
||||
s.Truef(strings.HasPrefix(decoded[0]["name"].(string), "helm-chart-no-op-"), "invalid helm install name, expected no-op-*, got %v", decoded[0]["name"])
|
||||
})
|
||||
s.Run("has valid namespace", func() {
|
||||
s.Equalf("default", decoded[0]["namespace"], "invalid helm install namespace, expected default, got %v", decoded[0]["namespace"])
|
||||
})
|
||||
s.Run("has valid chart", func() {
|
||||
s.Equalf("no-op", decoded[0]["chart"], "invalid helm install name, expected release name, got empty")
|
||||
})
|
||||
s.Run("has valid chartVersion", func() {
|
||||
s.Equalf("1.33.7", decoded[0]["chartVersion"], "invalid helm install version, expected 1.33.7, got empty")
|
||||
})
|
||||
s.Run("has valid status", func() {
|
||||
s.Equalf("deployed", decoded[0]["status"], "invalid helm install status, expected deployed, got %v", decoded[0]["status"])
|
||||
})
|
||||
s.Run("has valid revision", func() {
|
||||
s.Equalf(float64(1), decoded[0]["revision"], "invalid helm install revision, expected 1, got %v", decoded[0]["revision"])
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmInstallDenied(t *testing.T) {
|
||||
deniedResourcesServer := test.Must(config.ReadToml([]byte(`
|
||||
func (s *HelmSuite) TestHelmInstallDenied() {
|
||||
s.Require().NoError(toml.Unmarshal([]byte(`
|
||||
denied_resources = [ { version = "v1", kind = "Secret" } ]
|
||||
`)))
|
||||
testCaseWithContext(t, &mcpContext{staticConfig: deniedResourcesServer}, func(c *mcpContext) {
|
||||
c.withEnvTest()
|
||||
`), s.Cfg), "Expected to parse denied resources config")
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_install(chart=helm-chart-secret, denied)", func() {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
chartPath := filepath.Join(filepath.Dir(file), "testdata", "helm-chart-secret")
|
||||
helmInstall, _ := c.callTool("helm_install", map[string]interface{}{
|
||||
toolResult, err := s.CallTool("helm_install", map[string]interface{}{
|
||||
"chart": chartPath,
|
||||
})
|
||||
t.Run("helm_install has error", func(t *testing.T) {
|
||||
if !helmInstall.IsError {
|
||||
t.Fatalf("call tool should fail")
|
||||
}
|
||||
s.Run("has error", func() {
|
||||
s.Truef(toolResult.IsError, "call tool should fail")
|
||||
s.Nilf(err, "call tool should not return error object")
|
||||
})
|
||||
t.Run("helm_install describes denial", func(t *testing.T) {
|
||||
toolOutput := helmInstall.Content[0].(mcp.TextContent).Text
|
||||
s.Run("describes denial", func() {
|
||||
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "failed to install helm chart"), "expected descriptive error, got %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
expectedMessage := ": resource not allowed: /v1, Kind=Secret"
|
||||
if !strings.HasPrefix(toolOutput, "failed to install helm chart") || !strings.HasSuffix(toolOutput, expectedMessage) {
|
||||
t.Fatalf("expected descriptive error '%s', got %v", expectedMessage, helmInstall.Content[0].(mcp.TextContent).Text)
|
||||
}
|
||||
s.Truef(strings.HasSuffix(toolResult.Content[0].(mcp.TextContent).Text, expectedMessage), "expected descriptive error '%s', got %v", expectedMessage, toolResult.Content[0].(mcp.TextContent).Text)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmList(t *testing.T) {
|
||||
testCase(t, func(c *mcpContext) {
|
||||
c.withEnvTest()
|
||||
kc := c.newKubernetesClient()
|
||||
clearHelmReleases(c.ctx, kc)
|
||||
toolResult, err := c.callTool("helm_list", map[string]interface{}{})
|
||||
t.Run("helm_list with no releases, returns not found", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
if toolResult.Content[0].(mcp.TextContent).Text != "No Helm releases found" {
|
||||
t.Fatalf("unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
}
|
||||
func (s *HelmSuite) TestHelmListNoReleases() {
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_list() with no releases", func() {
|
||||
toolResult, err := s.CallTool("helm_list", map[string]interface{}{})
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
_, _ = kc.CoreV1().Secrets("default").Create(c.ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sh.helm.release.v1.release-to-list",
|
||||
Labels: map[string]string{"owner": "helm", "name": "release-to-list"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"release": []byte(base64.StdEncoding.EncodeToString([]byte("{" +
|
||||
"\"name\":\"release-to-list\"," +
|
||||
"\"info\":{\"status\":\"deployed\"}" +
|
||||
"}"))),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
toolResult, err = c.callTool("helm_list", map[string]interface{}{})
|
||||
t.Run("helm_list with deployed release, returns release", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
var decoded []map[string]interface{}
|
||||
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid tool result content %v", err)
|
||||
}
|
||||
if len(decoded) != 1 {
|
||||
t.Fatalf("invalid helm list count, expected 1, got %v", len(decoded))
|
||||
}
|
||||
if decoded[0]["name"] != "release-to-list" {
|
||||
t.Fatalf("invalid helm list name, expected release-to-list, got %v", decoded[0]["name"])
|
||||
}
|
||||
if decoded[0]["status"] != "deployed" {
|
||||
t.Fatalf("invalid helm list status, expected deployed, got %v", decoded[0]["status"])
|
||||
}
|
||||
})
|
||||
toolResult, err = c.callTool("helm_list", map[string]interface{}{"namespace": "ns-1"})
|
||||
t.Run("helm_list with deployed release in other namespaces, returns not found", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
if toolResult.Content[0].(mcp.TextContent).Text != "No Helm releases found" {
|
||||
t.Fatalf("unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
}
|
||||
})
|
||||
toolResult, err = c.callTool("helm_list", map[string]interface{}{"namespace": "ns-1", "all_namespaces": true})
|
||||
t.Run("helm_list with deployed release in all namespaces, returns release", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
var decoded []map[string]interface{}
|
||||
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid tool result content %v", err)
|
||||
}
|
||||
if len(decoded) != 1 {
|
||||
t.Fatalf("invalid helm list count, expected 1, got %v", len(decoded))
|
||||
}
|
||||
if decoded[0]["name"] != "release-to-list" {
|
||||
t.Fatalf("invalid helm list name, expected release-to-list, got %v", decoded[0]["name"])
|
||||
}
|
||||
if decoded[0]["status"] != "deployed" {
|
||||
t.Fatalf("invalid helm list status, expected deployed, got %v", decoded[0]["status"])
|
||||
}
|
||||
s.Run("returns not found", func() {
|
||||
s.Equalf("No Helm releases found", toolResult.Content[0].(mcp.TextContent).Text, "unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmUninstall(t *testing.T) {
|
||||
testCase(t, func(c *mcpContext) {
|
||||
c.withEnvTest()
|
||||
kc := c.newKubernetesClient()
|
||||
clearHelmReleases(c.ctx, kc)
|
||||
toolResult, err := c.callTool("helm_uninstall", map[string]interface{}{
|
||||
func (s *HelmSuite) TestHelmList() {
|
||||
kc := kubernetes.NewForConfigOrDie(envTestRestConfig)
|
||||
_, err := kc.CoreV1().Secrets("default").Create(s.T().Context(), &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sh.helm.release.v1.release-to-list",
|
||||
Labels: map[string]string{"owner": "helm", "name": "release-to-list"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"release": []byte(base64.StdEncoding.EncodeToString([]byte("{" +
|
||||
"\"name\":\"release-to-list\"," +
|
||||
"\"info\":{\"status\":\"deployed\"}" +
|
||||
"}"))),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
s.Require().NoError(err)
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_list() with deployed release", func() {
|
||||
toolResult, err := s.CallTool("helm_list", map[string]interface{}{})
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
s.Run("returns release", func() {
|
||||
var decoded []map[string]interface{}
|
||||
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
|
||||
s.Run("has yaml content", func() {
|
||||
s.Nilf(err, "invalid tool result content %v", err)
|
||||
})
|
||||
s.Run("has 1 item", func() {
|
||||
s.Lenf(decoded, 1, "invalid helm list count, expected 1, got %v", len(decoded))
|
||||
})
|
||||
s.Run("has valid name", func() {
|
||||
s.Equalf("release-to-list", decoded[0]["name"], "invalid helm list name, expected release-to-list, got %v", decoded[0]["name"])
|
||||
})
|
||||
s.Run("has valid status", func() {
|
||||
s.Equalf("deployed", decoded[0]["status"], "invalid helm list status, expected deployed, got %v", decoded[0]["status"])
|
||||
})
|
||||
})
|
||||
})
|
||||
s.Run("helm_list(namespace=ns-1) with deployed release in other namespaces", func() {
|
||||
toolResult, err := s.CallTool("helm_list", map[string]interface{}{"namespace": "ns-1"})
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
s.Run("returns not found", func() {
|
||||
s.Equalf("No Helm releases found", toolResult.Content[0].(mcp.TextContent).Text, "unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
})
|
||||
})
|
||||
s.Run("helm_list(namespace=ns-1, all_namespaces=true) with deployed release in all namespaces", func() {
|
||||
toolResult, err := s.CallTool("helm_list", map[string]interface{}{"namespace": "ns-1", "all_namespaces": true})
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
s.Run("returns release", func() {
|
||||
var decoded []map[string]interface{}
|
||||
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
|
||||
s.Run("has yaml content", func() {
|
||||
s.Nilf(err, "invalid tool result content %v", err)
|
||||
})
|
||||
s.Run("has 1 item", func() {
|
||||
s.Lenf(decoded, 1, "invalid helm list count, expected 1, got %v", len(decoded))
|
||||
})
|
||||
s.Run("has valid name", func() {
|
||||
s.Equalf("release-to-list", decoded[0]["name"], "invalid helm list name, expected release-to-list, got %v", decoded[0]["name"])
|
||||
})
|
||||
s.Run("has valid status", func() {
|
||||
s.Equalf("deployed", decoded[0]["status"], "invalid helm list status, expected deployed, got %v", decoded[0]["status"])
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *HelmSuite) TestHelmUninstallNoReleases() {
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_uninstall(name=release-to-uninstall) with no releases", func() {
|
||||
toolResult, err := s.CallTool("helm_uninstall", map[string]interface{}{
|
||||
"name": "release-to-uninstall",
|
||||
})
|
||||
t.Run("helm_uninstall with no releases, returns not found", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
if toolResult.Content[0].(mcp.TextContent).Text != "Release release-to-uninstall not found" {
|
||||
t.Fatalf("unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
}
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
_, _ = kc.CoreV1().Secrets("default").Create(c.ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sh.helm.release.v1.existent-release-to-uninstall.v0",
|
||||
Labels: map[string]string{"owner": "helm", "name": "existent-release-to-uninstall"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"release": []byte(base64.StdEncoding.EncodeToString([]byte("{" +
|
||||
"\"name\":\"existent-release-to-uninstall\"," +
|
||||
"\"info\":{\"status\":\"deployed\"}" +
|
||||
"}"))),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
toolResult, err = c.callTool("helm_uninstall", map[string]interface{}{
|
||||
"name": "existent-release-to-uninstall",
|
||||
})
|
||||
t.Run("helm_uninstall with deployed release, returns uninstalled", func(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("call tool failed %v", err)
|
||||
}
|
||||
if toolResult.IsError {
|
||||
t.Fatalf("call tool failed")
|
||||
}
|
||||
if !strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "Uninstalled release existent-release-to-uninstall") {
|
||||
t.Fatalf("unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
}
|
||||
_, err = kc.CoreV1().Secrets("default").Get(c.ctx, "sh.helm.release.v1.existent-release-to-uninstall.v0", metav1.GetOptions{})
|
||||
if !errors.IsNotFound(err) {
|
||||
t.Fatalf("expected release to be deleted, but it still exists")
|
||||
}
|
||||
s.Run("returns not found", func() {
|
||||
s.Equalf("Release release-to-uninstall not found", toolResult.Content[0].(mcp.TextContent).Text, "unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmUninstallDenied(t *testing.T) {
|
||||
deniedResourcesServer := test.Must(config.ReadToml([]byte(`
|
||||
denied_resources = [ { version = "v1", kind = "Secret" } ]
|
||||
`)))
|
||||
testCaseWithContext(t, &mcpContext{staticConfig: deniedResourcesServer}, func(c *mcpContext) {
|
||||
c.withEnvTest()
|
||||
kc := c.newKubernetesClient()
|
||||
clearHelmReleases(c.ctx, kc)
|
||||
_, _ = kc.CoreV1().Secrets("default").Create(c.ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sh.helm.release.v1.existent-release-to-uninstall.v0",
|
||||
Labels: map[string]string{"owner": "helm", "name": "existent-release-to-uninstall"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"release": []byte(base64.StdEncoding.EncodeToString([]byte("{" +
|
||||
"\"name\":\"existent-release-to-uninstall\"," +
|
||||
"\"info\":{\"status\":\"deployed\"}," +
|
||||
"\"manifest\":\"apiVersion: v1\\nkind: Secret\\nmetadata:\\n name: secret-to-deny\\n namespace: default\\n\"" +
|
||||
"}"))),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
helmUninstall, _ := c.callTool("helm_uninstall", map[string]interface{}{
|
||||
func (s *HelmSuite) TestHelmUninstall() {
|
||||
kc := kubernetes.NewForConfigOrDie(envTestRestConfig)
|
||||
_, err := kc.CoreV1().Secrets("default").Create(s.T().Context(), &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sh.helm.release.v1.existent-release-to-uninstall.v0",
|
||||
Labels: map[string]string{"owner": "helm", "name": "existent-release-to-uninstall"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"release": []byte(base64.StdEncoding.EncodeToString([]byte("{" +
|
||||
"\"name\":\"existent-release-to-uninstall\"," +
|
||||
"\"info\":{\"status\":\"deployed\"}" +
|
||||
"}"))),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
s.Require().NoError(err)
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_uninstall(name=existent-release-to-uninstall) with deployed release", func() {
|
||||
toolResult, err := s.CallTool("helm_uninstall", map[string]interface{}{
|
||||
"name": "existent-release-to-uninstall",
|
||||
})
|
||||
t.Run("helm_uninstall has error", func(t *testing.T) {
|
||||
if !helmUninstall.IsError {
|
||||
t.Fatalf("call tool should fail")
|
||||
}
|
||||
s.Run("no error", func() {
|
||||
s.Nilf(err, "call tool failed %v", err)
|
||||
s.Falsef(toolResult.IsError, "call tool failed")
|
||||
})
|
||||
s.Run("returns uninstalled", func() {
|
||||
s.Truef(strings.HasPrefix(toolResult.Content[0].(mcp.TextContent).Text, "Uninstalled release existent-release-to-uninstall"), "unexpected result %v", toolResult.Content[0].(mcp.TextContent).Text)
|
||||
_, err = kc.CoreV1().Secrets("default").Get(s.T().Context(), "sh.helm.release.v1.existent-release-to-uninstall.v0", metav1.GetOptions{})
|
||||
s.Truef(errors.IsNotFound(err), "expected release to be deleted, but it still exists")
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func (s *HelmSuite) TestHelmUninstallDenied() {
|
||||
s.Require().NoError(toml.Unmarshal([]byte(`
|
||||
denied_resources = [ { version = "v1", kind = "Secret" } ]
|
||||
`), s.Cfg), "Expected to parse denied resources config")
|
||||
kc := kubernetes.NewForConfigOrDie(envTestRestConfig)
|
||||
_, err := kc.CoreV1().Secrets("default").Create(s.T().Context(), &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sh.helm.release.v1.existent-release-to-uninstall.v0",
|
||||
Labels: map[string]string{"owner": "helm", "name": "existent-release-to-uninstall"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"release": []byte(base64.StdEncoding.EncodeToString([]byte("{" +
|
||||
"\"name\":\"existent-release-to-uninstall\"," +
|
||||
"\"info\":{\"status\":\"deployed\"}," +
|
||||
"\"manifest\":\"apiVersion: v1\\nkind: Secret\\nmetadata:\\n name: secret-to-deny\\n namespace: default\\n\"" +
|
||||
"}"))),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
s.Require().NoError(err)
|
||||
s.InitMcpClient()
|
||||
s.Run("helm_uninstall(name=existent-release-to-uninstall) with deployed release (denied)", func() {
|
||||
toolResult, err := s.CallTool("helm_uninstall", map[string]interface{}{
|
||||
"name": "existent-release-to-uninstall",
|
||||
})
|
||||
s.Run("has error", func() {
|
||||
s.Truef(toolResult.IsError, "call tool should fail")
|
||||
s.Nilf(err, "call tool should not return error object")
|
||||
})
|
||||
s.Run("describes denial", func() {
|
||||
s.T().Skipf("Helm won't report what underlying resource caused the failure, so we can't assert on it")
|
||||
expectedMessage := "failed to uninstall release: resource not allowed: /v1, Kind=Secret"
|
||||
s.Equalf(expectedMessage, toolResult.Content[0].(mcp.TextContent).Text, "expected descriptive error '%s', got %v", expectedMessage, toolResult.Content[0].(mcp.TextContent).Text)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -269,3 +274,7 @@ func clearHelmReleases(ctx context.Context, kc *kubernetes.Clientset) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelm(t *testing.T) {
|
||||
suite.Run(t, new(HelmSuite))
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func (s *NamespacesSuite) TestNamespacesList() {
|
||||
func (s *NamespacesSuite) TestNamespacesListDenied() {
|
||||
s.Require().NoError(toml.Unmarshal([]byte(`
|
||||
denied_resources = [ { version = "v1", kind = "Namespace" } ]
|
||||
`), s.Cfg), "Expected to parse denied resources config")
|
||||
`), s.Cfg), "Expected to parse denied resources config")
|
||||
s.InitMcpClient()
|
||||
s.Run("namespaces_list (denied)", func() {
|
||||
toolResult, err := s.CallTool("namespaces_list", map[string]interface{}{})
|
||||
|
||||
@@ -2,9 +2,6 @@ package mcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
@@ -75,13 +72,10 @@ func (s *ToolsetsSuite) TestDefaultToolsetsTools() {
|
||||
s.NoError(err, "Expected no error from ListTools")
|
||||
})
|
||||
s.Run("ListTools returns correct Tool metadata", func() {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
expectedMetadataPath := filepath.Join(filepath.Dir(file), "testdata", "toolsets-full-tools.json")
|
||||
expectedMetadataBytes, err := os.ReadFile(expectedMetadataPath)
|
||||
s.Require().NoErrorf(err, "failed to read expected tools metadata file: %v", err)
|
||||
expectedMetadata := test.ReadFile("testdata", "toolsets-full-tools.json")
|
||||
metadata, err := json.MarshalIndent(tools.Tools, "", " ")
|
||||
s.Require().NoErrorf(err, "failed to marshal tools metadata: %v", err)
|
||||
s.JSONEq(string(expectedMetadataBytes), string(metadata), "tools metadata does not match expected")
|
||||
s.JSONEq(expectedMetadata, string(metadata), "tools metadata does not match expected")
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -96,13 +90,10 @@ func (s *ToolsetsSuite) TestDefaultToolsetsToolsInOpenShift() {
|
||||
s.NoError(err, "Expected no error from ListTools")
|
||||
})
|
||||
s.Run("ListTools returns correct Tool metadata", func() {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
expectedMetadataPath := filepath.Join(filepath.Dir(file), "testdata", "toolsets-full-tools-openshift.json")
|
||||
expectedMetadataBytes, err := os.ReadFile(expectedMetadataPath)
|
||||
s.Require().NoErrorf(err, "failed to read expected tools metadata file: %v", err)
|
||||
expectedMetadata := test.ReadFile("testdata", "toolsets-full-tools-openshift.json")
|
||||
metadata, err := json.MarshalIndent(tools.Tools, "", " ")
|
||||
s.Require().NoErrorf(err, "failed to marshal tools metadata: %v", err)
|
||||
s.JSONEq(string(expectedMetadataBytes), string(metadata), "tools metadata does not match expected")
|
||||
s.JSONEq(expectedMetadata, string(metadata), "tools metadata does not match expected")
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -125,13 +116,10 @@ func (s *ToolsetsSuite) TestGranularToolsetsTools() {
|
||||
s.NoError(err, "Expected no error from ListTools")
|
||||
})
|
||||
s.Run("ListTools returns correct Tool metadata", func() {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
expectedMetadataPath := filepath.Join(filepath.Dir(file), "testdata", "toolsets-"+testCase.GetName()+"-tools.json")
|
||||
expectedMetadataBytes, err := os.ReadFile(expectedMetadataPath)
|
||||
s.Require().NoErrorf(err, "failed to read expected tools metadata file: %v", err)
|
||||
expectedMetadata := test.ReadFile("testdata", "toolsets-"+testCase.GetName()+"-tools.json")
|
||||
metadata, err := json.MarshalIndent(tools.Tools, "", " ")
|
||||
s.Require().NoErrorf(err, "failed to marshal tools metadata: %v", err)
|
||||
s.JSONEq(string(expectedMetadataBytes), string(metadata), "tools metadata does not match expected")
|
||||
s.JSONEq(expectedMetadata, string(metadata), "tools metadata does not match expected")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user