Skip to content

Commit 327eeb5

Browse files
authored
feat(e2e): add comprehensive E2E test coverage for MCP classifier (#743)
* feat(e2e): add comprehensive E2E test coverage for MCP classifier - Add 5 new test cases for MCP classification: - mcp-stdio-classification: Tests stdio transport - mcp-http-classification: Tests HTTP transport - mcp-model-reasoning: Tests model recommendations and reasoning flags - mcp-probability-distribution: Tests probability arrays and entropy - mcp-fallback-behavior: Tests fallback to in-tree classifier - Add test data with 34 test cases covering math, science, technology, history, and general categories - Add common helpers in mcp_common.go for test execution and validation - Update routing-strategies profile to manage MCP server lifecycle - Add values-mcp.yaml for MCP-enabled semantic router configuration - Add MCP_ROUTING_AND_REQUEST_FLOW.md documentation Test Coverage: ✅ MCP stdio transport (process communication) ✅ MCP HTTP transport (API calls) ✅ Custom classification logic via external servers ✅ Model and reasoning decisions from MCP ✅ Fallback behavior on MCP failures ✅ Probability distribution validation Signed-off-by: Senan Zedan <[email protected]> * feat(e2e): add comprehensive E2E test coverage for MCP classifier - Add 5 new test cases: stdio/HTTP transport, model reasoning, probability distribution, and fallback behavior - Add 34 test cases covering math, science, technology, history, and general categories - Add mcp_common.go with shared helper functions - Update routing-strategies profile to manage MCP server lifecycle - Add values-mcp.yaml configuration for MCP-enabled deployment Test Coverage: ✅ MCP stdio transport (process communication) ✅ MCP HTTP transport (API calls) ✅ Custom classification via external servers ✅ Model and reasoning decisions from MCP ✅ Fallback to in-tree classifier on failures ✅ Probability distribution validation Signed-off-by: Senan Zedan <[email protected]> * fix: apply go fmt to MCP test files Signed-off-by: Senan Zedan <[email protected]> * fix: make MCP tests optional and fix server startup - Fix MCP server filename (server_keyword.py.py) - Make MCP server startup optional and non-blocking - Comment out MCP tests from default routing-strategies suite - MCP tests are still registered and can be run explicitly with E2E_TESTS parameter - This prevents CI failures when Python dependencies or MCP servers are unavailable Signed-off-by: Senan Zedan <[email protected]> * fix: rename server_keyword.py.py to server_keyword.py - Remove duplicate .py extension from MCP keyword server filename - Update profile.go to reference correct filename - This fixes the inconsistency with other server files (server_embedding.py, server_generative.py) Signed-off-by: Senan Zedan <[email protected]> --------- Signed-off-by: Senan Zedan <[email protected]>
1 parent ed3ad2b commit 327eeb5

13 files changed

+1478
-2
lines changed

e2e/profiles/routing-strategies/profile.go

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020

2121
// Profile implements the Routing Strategies test profile
2222
type Profile struct {
23-
verbose bool
23+
verbose bool
24+
mcpStdioProcess *exec.Cmd
25+
mcpHTTPProcess *exec.Cmd
2426
}
2527

2628
// NewProfile creates a new Routing Strategies profile
@@ -70,11 +72,19 @@ func (p *Profile) Setup(ctx context.Context, opts *framework.SetupOptions) error
7072
}
7173

7274
// Step 5: Verify all components are ready
73-
p.log("Step 5/5: Verifying all components are ready")
75+
p.log("Step 5/6: Verifying all components are ready")
7476
if err := p.verifyEnvironment(ctx, opts); err != nil {
7577
return fmt.Errorf("failed to verify environment: %w", err)
7678
}
7779

80+
// Step 6: Start MCP servers for testing (optional - tests will skip if unavailable)
81+
p.log("Step 6/6: Starting MCP classification servers (optional)")
82+
if err := p.startMCPServers(ctx); err != nil {
83+
p.log("Warning: MCP servers not started: %v", err)
84+
p.log("MCP-related tests will be skipped")
85+
// Don't fail setup - MCP tests are optional
86+
}
87+
7888
p.log("Routing Strategies test environment setup complete")
7989
return nil
8090
}
@@ -84,6 +94,15 @@ func (p *Profile) Teardown(ctx context.Context, opts *framework.TeardownOptions)
8494
p.verbose = opts.Verbose
8595
p.log("Tearing down Routing Strategies test environment")
8696

97+
// Stop MCP servers first
98+
p.log("Stopping MCP servers")
99+
if p.mcpStdioProcess != nil {
100+
p.mcpStdioProcess.Process.Kill()
101+
}
102+
if p.mcpHTTPProcess != nil {
103+
p.mcpHTTPProcess.Process.Kill()
104+
}
105+
87106
deployer := helm.NewDeployer(opts.KubeConfig, opts.Verbose)
88107

89108
// Clean up in reverse order
@@ -108,6 +127,13 @@ func (p *Profile) Teardown(ctx context.Context, opts *framework.TeardownOptions)
108127
func (p *Profile) GetTestCases() []string {
109128
return []string{
110129
"keyword-routing",
130+
// MCP tests are registered but not run by default
131+
// To run MCP tests, use: E2E_TESTS="mcp-stdio-classification,mcp-http-classification,..."
132+
// "mcp-stdio-classification",
133+
// "mcp-http-classification",
134+
// "mcp-model-reasoning",
135+
// "mcp-probability-distribution",
136+
// "mcp-fallback-behavior",
111137
}
112138
}
113139

@@ -323,3 +349,64 @@ func (p *Profile) log(format string, args ...interface{}) {
323349
fmt.Printf("[Routing-Strategies] "+format+"\n", args...)
324350
}
325351
}
352+
353+
func (p *Profile) startMCPServers(ctx context.Context) error {
354+
p.log("Starting MCP classification servers")
355+
356+
// Check if Python 3 is available
357+
if _, err := exec.LookPath("python3"); err != nil {
358+
p.log("Warning: python3 not found, skipping MCP server startup")
359+
p.log("MCP tests will be skipped or may fail")
360+
return nil
361+
}
362+
363+
// Start stdio MCP server (keyword-based classifier)
364+
p.log("Starting stdio MCP server (keyword-based)")
365+
p.mcpStdioProcess = exec.CommandContext(ctx,
366+
"python3",
367+
"examples/mcp-classifier-server/server_keyword.py")
368+
369+
// Capture output for debugging
370+
if p.verbose {
371+
p.mcpStdioProcess.Stdout = os.Stdout
372+
p.mcpStdioProcess.Stderr = os.Stderr
373+
}
374+
375+
if err := p.mcpStdioProcess.Start(); err != nil {
376+
p.log("Warning: failed to start stdio MCP server: %v", err)
377+
// Continue without stdio server - tests may skip or fail gracefully
378+
} else {
379+
p.log("Stdio MCP server started (PID: %d)", p.mcpStdioProcess.Process.Pid)
380+
}
381+
382+
// Start HTTP MCP server (embedding-based classifier)
383+
p.log("Starting HTTP MCP server (embedding-based)")
384+
p.mcpHTTPProcess = exec.CommandContext(ctx,
385+
"python3",
386+
"examples/mcp-classifier-server/server_embedding.py",
387+
"--port", "8090")
388+
389+
// Capture output for debugging
390+
if p.verbose {
391+
p.mcpHTTPProcess.Stdout = os.Stdout
392+
p.mcpHTTPProcess.Stderr = os.Stderr
393+
}
394+
395+
if err := p.mcpHTTPProcess.Start(); err != nil {
396+
p.log("Warning: failed to start HTTP MCP server: %v", err)
397+
// If stdio server failed too, return error
398+
if p.mcpStdioProcess == nil {
399+
return fmt.Errorf("failed to start any MCP servers: %w", err)
400+
}
401+
p.log("Continuing with only stdio MCP server")
402+
} else {
403+
p.log("HTTP MCP server started (PID: %d)", p.mcpHTTPProcess.Process.Pid)
404+
}
405+
406+
// Wait for servers to be ready
407+
p.log("Waiting for MCP servers to initialize...")
408+
time.Sleep(3 * time.Second)
409+
410+
p.log("MCP servers started successfully")
411+
return nil
412+
}

0 commit comments

Comments
 (0)