-
Notifications
You must be signed in to change notification settings - Fork 1
ROX-31495: Integration tests #50
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
Open
janisz
wants to merge
19
commits into
main
Choose a base branch
from
integration_tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
1d35a3d
ROX-31495: wiremock for central (#32)
janisz 3c617df
ci: fix golangci-lint style issues
janisz b3fcf5c
refactor: extract helper methods to fix funlen linter issue
janisz 630b3da
fix
janisz e911b97
fix
janisz 8550a0c
fix
janisz 07f2802
fix: export GetToolsets function for reusability
janisz 6424c85
fix: remove duplicate getToolsets and use app.GetToolsets
janisz c727323
test: move comprehensive TestGetToolsets to app package
janisz f3c3f8e
docs: remove irrelevant stdin/stdout comment from app.go
janisz 4aadc98
docs: remove '(production mode)' annotation from server.go
janisz 8e73225
docs: remove '(for testing)' annotation from server.go
janisz 46bb0fb
docs: remove '(production)' annotation from server.go
janisz 25d1d91
docs: add full paths to fixture comments
janisz c79028d
refactor: move helper functions to top of integration_test.go
janisz 8b90a9b
refactor: simplify RequireNoError with early return pattern
janisz afc96aa
chore: delete unused RunCommand function
janisz 6708e6b
fix: create cancellable context before client connection
janisz 46d7709
refactor: consolidate integration test helpers in testutil
janisz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| //go:build integration | ||
|
|
||
| package integration | ||
|
|
||
| // Log4ShellFixture contains expected data from wiremock/fixtures/deployments/log4j_cve.json fixture. | ||
| var Log4ShellFixture = struct { | ||
| CVEName string | ||
| DeploymentCount int | ||
| DeploymentNames []string | ||
| }{ | ||
| CVEName: "CVE-2021-44228", | ||
| DeploymentCount: 3, | ||
| DeploymentNames: []string{"elasticsearch", "kafka-broker", "spring-boot-app"}, | ||
| } | ||
|
|
||
| // AllClustersFixture contains expected data from wiremock/fixtures/clusters/all_clusters.json fixture. | ||
| var AllClustersFixture = struct { | ||
| TotalCount int | ||
| ClusterNames []string | ||
| }{ | ||
| TotalCount: 5, | ||
| ClusterNames: []string{ | ||
| "production-cluster", | ||
| "staging-cluster", | ||
| "staging-central-cluster", | ||
| "development-cluster", | ||
| "production-cluster-eu", | ||
| }, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| //go:build integration | ||
|
|
||
| package integration | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| "github.com/stackrox/stackrox-mcp/internal/testutil" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // TestIntegration_ListTools verifies that all expected tools are registered. | ||
| func TestIntegration_ListTools(t *testing.T) { | ||
| client := testutil.SetupInitializedClient(t, testutil.CreateIntegrationMCPClient) | ||
|
|
||
| ctx := context.Background() | ||
| result, err := client.ListTools(ctx) | ||
| require.NoError(t, err) | ||
|
|
||
| // Verify we have tools registered | ||
| assert.NotEmpty(t, result.Tools, "should have tools registered") | ||
|
|
||
| // Check for specific tools we expect | ||
| toolNames := make([]string, 0, len(result.Tools)) | ||
| for _, tool := range result.Tools { | ||
| toolNames = append(toolNames, tool.Name) | ||
| } | ||
|
|
||
| assert.Contains(t, toolNames, "get_deployments_for_cve", "should have get_deployments_for_cve tool") | ||
| assert.Contains(t, toolNames, "list_clusters", "should have list_clusters tool") | ||
| } | ||
|
|
||
| // TestIntegration_ToolCalls tests successful tool calls using table-driven tests. | ||
| func TestIntegration_ToolCalls(t *testing.T) { | ||
| tests := map[string]struct { | ||
| toolName string | ||
| args map[string]any | ||
| expectedInText []string // strings that must appear in response | ||
| }{ | ||
| "get_deployments_for_cve with Log4Shell": { | ||
| toolName: "get_deployments_for_cve", | ||
| args: map[string]any{"cveName": Log4ShellFixture.CVEName}, | ||
| expectedInText: Log4ShellFixture.DeploymentNames, | ||
| }, | ||
| "get_deployments_for_cve with non-existent CVE": { | ||
| toolName: "get_deployments_for_cve", | ||
| args: map[string]any{"cveName": "CVE-9999-99999"}, | ||
| expectedInText: []string{`"deployments":[]`}, | ||
| }, | ||
| "list_clusters": { | ||
| toolName: "list_clusters", | ||
| args: map[string]any{}, | ||
| expectedInText: AllClustersFixture.ClusterNames, | ||
| }, | ||
| "get_clusters_with_orchestrator_cve": { | ||
| toolName: "get_clusters_with_orchestrator_cve", | ||
| args: map[string]any{"cveName": "CVE-2099-00001"}, | ||
| expectedInText: []string{`"clusters":`}, | ||
| }, | ||
| } | ||
|
|
||
| for name, tt := range tests { | ||
| t.Run(name, func(t *testing.T) { | ||
| client := testutil.SetupInitializedClient(t, testutil.CreateIntegrationMCPClient) | ||
| result := testutil.CallToolAndGetResult(t, client, tt.toolName, tt.args) | ||
|
|
||
| responseText := testutil.GetTextContent(t, result) | ||
| for _, expected := range tt.expectedInText { | ||
| assert.Contains(t, responseText, expected) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // TestIntegration_ToolCallErrors tests error handling using table-driven tests. | ||
| func TestIntegration_ToolCallErrors(t *testing.T) { | ||
| tests := map[string]struct { | ||
| toolName string | ||
| args map[string]any | ||
| expectedErrorMsg string | ||
| }{ | ||
| "get_deployments_for_cve missing CVE name": { | ||
| toolName: "get_deployments_for_cve", | ||
| args: map[string]any{}, | ||
| expectedErrorMsg: "cveName", | ||
| }, | ||
| } | ||
|
|
||
| for name, tt := range tests { | ||
| t.Run(name, func(t *testing.T) { | ||
| client := testutil.SetupInitializedClient(t, testutil.CreateIntegrationMCPClient) | ||
|
|
||
| ctx := context.Background() | ||
| _, err := client.CallTool(ctx, tt.toolName, tt.args) | ||
|
|
||
| // Validation errors are returned as protocol errors, not tool errors | ||
| require.Error(t, err, "should receive protocol error for invalid params") | ||
| assert.Contains(t, err.Error(), tt.expectedErrorMsg) | ||
| }) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // Package app contains the main application logic for the stackrox-mcp server. | ||
| // This is separated from the main package to allow tests to run the server in-process. | ||
| package app | ||
|
|
||
| import ( | ||
| "context" | ||
| "io" | ||
| "log/slog" | ||
| "os" | ||
| "os/signal" | ||
| "syscall" | ||
|
|
||
| "github.com/pkg/errors" | ||
| "github.com/stackrox/stackrox-mcp/internal/client" | ||
| "github.com/stackrox/stackrox-mcp/internal/config" | ||
| "github.com/stackrox/stackrox-mcp/internal/server" | ||
| "github.com/stackrox/stackrox-mcp/internal/toolsets" | ||
| toolsetConfig "github.com/stackrox/stackrox-mcp/internal/toolsets/config" | ||
| toolsetVulnerability "github.com/stackrox/stackrox-mcp/internal/toolsets/vulnerability" | ||
| ) | ||
|
|
||
| // GetToolsets initializes and returns all available toolsets. | ||
| func GetToolsets(cfg *config.Config, c *client.Client) []toolsets.Toolset { | ||
| return []toolsets.Toolset{ | ||
| toolsetConfig.NewToolset(cfg, c), | ||
| toolsetVulnerability.NewToolset(cfg, c), | ||
| } | ||
| } | ||
|
|
||
| // Run executes the MCP server with the given configuration and I/O streams. | ||
| // This function is extracted from main() to allow tests to run the server in-process. | ||
| func Run(ctx context.Context, cfg *config.Config, stdin io.ReadCloser, stdout io.WriteCloser) error { | ||
| // Log full configuration with sensitive data redacted. | ||
| slog.Info("Configuration loaded successfully", "config", cfg.Redacted()) | ||
|
|
||
| // Create a cancellable context for the entire server lifecycle | ||
| ctx, cancel := context.WithCancel(ctx) | ||
| defer cancel() | ||
|
|
||
| // Set up signal handling for graceful shutdown. | ||
| sigChan := make(chan os.Signal, 1) | ||
| signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) | ||
|
|
||
| go func() { | ||
| <-sigChan | ||
| slog.Info("Received shutdown signal") | ||
| cancel() | ||
| }() | ||
|
|
||
| stackroxClient, err := client.NewClient(&cfg.Central) | ||
| if err != nil { | ||
| return errors.Wrap(err, "failed to create client") | ||
| } | ||
|
|
||
| registry := toolsets.NewRegistry(cfg, GetToolsets(cfg, stackroxClient)) | ||
| srv := server.NewServer(cfg, registry) | ||
|
|
||
| err = stackroxClient.Connect(ctx) | ||
|
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. Before, we have also used cancelable context here. Is there a reason why we are not doing that anymore? |
||
| if err != nil { | ||
| return errors.Wrap(err, "failed to connect to central") | ||
| } | ||
|
|
||
| slog.Info("Starting StackRox MCP server") | ||
|
|
||
| return errors.Wrap(srv.Start(ctx, stdin, stdout), "failed to start server") | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
nitpick: I think, this is not relevant information