-
Notifications
You must be signed in to change notification settings - Fork 176
test(mcp): add tests for WatchKubeConfig tools reload #449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,103 @@ | ||||||||||
| package mcp | ||||||||||
|
|
||||||||||
| import ( | ||||||||||
| "context" | ||||||||||
| "os" | ||||||||||
| "testing" | ||||||||||
| "time" | ||||||||||
|
|
||||||||||
| "github.com/containers/kubernetes-mcp-server/internal/test" | ||||||||||
| "github.com/mark3labs/mcp-go/mcp" | ||||||||||
| "github.com/stretchr/testify/suite" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| type WatchKubeConfigSuite struct { | ||||||||||
| BaseMcpSuite | ||||||||||
| mockServer *test.MockServer | ||||||||||
| } | ||||||||||
|
|
||||||||||
| func (s *WatchKubeConfigSuite) SetupTest() { | ||||||||||
| s.BaseMcpSuite.SetupTest() | ||||||||||
| s.mockServer = test.NewMockServer() | ||||||||||
| s.Cfg.KubeConfig = s.mockServer.KubeconfigFile(s.T()) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| func (s *WatchKubeConfigSuite) TearDownTest() { | ||||||||||
| s.BaseMcpSuite.TearDownTest() | ||||||||||
| if s.mockServer != nil { | ||||||||||
| s.mockServer.Close() | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| func (s *WatchKubeConfigSuite) WriteKubeconfig() { | ||||||||||
| f, _ := os.OpenFile(s.Cfg.KubeConfig, os.O_APPEND|os.O_WRONLY, 0644) | ||||||||||
| _, _ = f.WriteString("\n") | ||||||||||
| _ = f.Close() | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // WaitForNotification waits for an MCP server notification or fails the test after a timeout | ||||||||||
| func (s *WatchKubeConfigSuite) WaitForNotification() *mcp.JSONRPCNotification { | ||||||||||
| withTimeout, cancel := context.WithTimeout(s.T().Context(), 5*time.Second) | ||||||||||
| defer cancel() | ||||||||||
| var notification *mcp.JSONRPCNotification | ||||||||||
| s.OnNotification(func(n mcp.JSONRPCNotification) { | ||||||||||
| notification = &n | ||||||||||
| }) | ||||||||||
| for notification == nil { | ||||||||||
| select { | ||||||||||
| case <-withTimeout.Done(): | ||||||||||
| s.FailNow("timeout waiting for WatchKubeConfig notification") | ||||||||||
| default: | ||||||||||
| time.Sleep(100 * time.Millisecond) | ||||||||||
| } | ||||||||||
| } | ||||||||||
| return notification | ||||||||||
| } | ||||||||||
|
|
||||||||||
| func (s *WatchKubeConfigSuite) TestNotifiesToolsChange() { | ||||||||||
| // Given | ||||||||||
| s.InitMcpClient() | ||||||||||
| // When | ||||||||||
| s.WriteKubeconfig() | ||||||||||
| notification := s.WaitForNotification() | ||||||||||
| // Then | ||||||||||
| s.NotNil(notification, "WatchKubeConfig did not notify") | ||||||||||
| s.Equal("notifications/tools/list_changed", notification.Method, "WatchKubeConfig did not notify tools change") | ||||||||||
| } | ||||||||||
|
|
||||||||||
| func (s *WatchKubeConfigSuite) TestClearsNoLongerAvailableTools() { | ||||||||||
| s.mockServer.Handle(&test.InOpenShiftHandler{}) | ||||||||||
| s.InitMcpClient() | ||||||||||
|
|
||||||||||
| s.Run("OpenShift tool is available", func() { | ||||||||||
| tools, err := s.ListTools(s.T().Context(), mcp.ListToolsRequest{}) | ||||||||||
| s.Require().NoError(err, "call ListTools failed") | ||||||||||
| s.Require().NotNil(tools, "list tools failed") | ||||||||||
| var found bool | ||||||||||
| for _, tool := range tools.Tools { | ||||||||||
| if tool.Name == "projects_list" { | ||||||||||
| found = true | ||||||||||
| break | ||||||||||
| } | ||||||||||
| } | ||||||||||
| s.Truef(found, "expected OpenShift tool to be available") | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| s.Run("OpenShift tool is removed after kubeconfig change", func() { | ||||||||||
| // Reload Config without OpenShift | ||||||||||
| s.mockServer.ResetHandlers() | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It took me a bit, to see that it actually "reset" the handlers (while the other one accepts one handler). I guess no major issue
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can try to make it clearer. I will probably improve the test with a custom provider to verify other behaviors such as the Provider close if exists: kubernetes-mcp-server/pkg/mcp/mcp.go Lines 107 to 110 in 6342379
and so on. Bu It don't want to spend too much time on this at the moment since it's blocking #385 |
||||||||||
| s.WriteKubeconfig() | ||||||||||
| s.WaitForNotification() | ||||||||||
|
|
||||||||||
| tools, err := s.ListTools(s.T().Context(), mcp.ListToolsRequest{}) | ||||||||||
| s.Require().NoError(err, "call ListTools failed") | ||||||||||
| s.Require().NotNil(tools, "list tools failed") | ||||||||||
| for _, tool := range tools.Tools { | ||||||||||
| s.Require().Falsef(tool.Name == "projects_list", "expected OpenShift tool to be removed") | ||||||||||
| } | ||||||||||
| }) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| func TestWatchKubeConfig(t *testing.T) { | ||||||||||
| suite.Run(t, new(WatchKubeConfigSuite)) | ||||||||||
| } | ||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is similar code above when creating the server