Skip to content

Commit be45bc2

Browse files
committed
update
Signed-off-by: bitliu <[email protected]>
1 parent 1162445 commit be45bc2

File tree

6 files changed

+211
-31
lines changed

6 files changed

+211
-31
lines changed

e2e/pkg/framework/runner.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,19 +209,29 @@ func (r *Runner) buildAndLoadImages(ctx context.Context) error {
209209
func (r *Runner) runTests(ctx context.Context, kubeClient *kubernetes.Clientset) ([]TestResult, error) {
210210
r.log("Running tests")
211211

212+
// Debug: List all registered test cases
213+
if r.opts.Verbose {
214+
r.log("All registered test cases:")
215+
for _, tc := range testcases.List() {
216+
r.log(" - %s: %s", tc.Name, tc.Description)
217+
}
218+
}
219+
212220
// Get test cases to run
213221
var testCasesToRun []testcases.TestCase
214222
var err error
215223

216224
if len(r.opts.TestCases) > 0 {
217225
// Run specific test cases
226+
r.log("Requested test cases: %v", r.opts.TestCases)
218227
testCasesToRun, err = testcases.ListByNames(r.opts.TestCases...)
219228
if err != nil {
220229
return nil, err
221230
}
222231
} else {
223232
// Run all test cases for the profile
224233
profileTestCases := r.profile.GetTestCases()
234+
r.log("Profile test cases: %v", profileTestCases)
225235
testCasesToRun, err = testcases.ListByNames(profileTestCases...)
226236
if err != nil {
227237
return nil, err

e2e/pkg/testcases/registry.go

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -105,28 +105,6 @@ func List() []TestCase {
105105
return cases
106106
}
107107

108-
// ListByTags returns test cases matching any of the given tags
109-
func ListByTags(tags ...string) []TestCase {
110-
mu.RLock()
111-
defer mu.RUnlock()
112-
113-
tagSet := make(map[string]bool)
114-
for _, tag := range tags {
115-
tagSet[tag] = true
116-
}
117-
118-
cases := make([]TestCase, 0)
119-
for _, tc := range registry {
120-
for _, tag := range tc.Tags {
121-
if tagSet[tag] {
122-
cases = append(cases, tc)
123-
break
124-
}
125-
}
126-
}
127-
return cases
128-
}
129-
130108
// ListByNames returns test cases matching the given names
131109
func ListByNames(names ...string) ([]TestCase, error) {
132110
mu.RLock()

e2e/profiles/ai-gateway/profile.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ func (p *Profile) Teardown(ctx context.Context, opts *framework.TeardownOptions)
108108
func (p *Profile) GetTestCases() []string {
109109
return []string{
110110
"chat-completions-request",
111+
"chat-completions-stress-request",
111112
"domain-classify",
112-
"cache",
113+
"semantic-cache",
113114
"pii-detection",
114115
"jailbreak-detection",
115116
}
@@ -154,7 +155,7 @@ func (p *Profile) deploySemanticRouter(ctx context.Context, deployer *helm.Deplo
154155
return deployer.WaitForDeployment(ctx, "vllm-semantic-router-system", "semantic-router", 10*time.Minute)
155156
}
156157

157-
func (p *Profile) deployEnvoyGateway(ctx context.Context, deployer *helm.Deployer, opts *framework.SetupOptions) error {
158+
func (p *Profile) deployEnvoyGateway(ctx context.Context, deployer *helm.Deployer, _ *framework.SetupOptions) error {
158159
installOpts := helm.InstallOptions{
159160
ReleaseName: "eg",
160161
Chart: "oci://docker.io/envoyproxy/gateway-helm",
@@ -172,7 +173,7 @@ func (p *Profile) deployEnvoyGateway(ctx context.Context, deployer *helm.Deploye
172173
return deployer.WaitForDeployment(ctx, "envoy-gateway-system", "envoy-gateway", 5*time.Minute)
173174
}
174175

175-
func (p *Profile) deployEnvoyAIGateway(ctx context.Context, deployer *helm.Deployer, opts *framework.SetupOptions) error {
176+
func (p *Profile) deployEnvoyAIGateway(ctx context.Context, deployer *helm.Deployer, _ *framework.SetupOptions) error {
176177
// Install AI Gateway CRDs
177178
crdOpts := helm.InstallOptions{
178179
ReleaseName: "aieg-crd",

e2e/testcases/cache.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import (
1616
)
1717

1818
func init() {
19-
pkgtestcases.Register("cache", pkgtestcases.TestCase{
19+
pkgtestcases.Register("semantic-cache", pkgtestcases.TestCase{
2020
Description: "Test semantic cache hit rate with similar questions",
21-
Tags: []string{"ai-gateway", "cache", "performance"},
21+
Tags: []string{"ai-gateway", "semantic-cache", "performance"},
2222
Fn: testCache,
2323
})
2424
}
@@ -169,8 +169,6 @@ func sendChatRequest(ctx context.Context, question, localPort string, verbose bo
169169
"messages": []map[string]string{
170170
{"role": "user", "content": question},
171171
},
172-
"max_tokens": 100,
173-
"stream": false,
174172
}
175173

176174
jsonData, err := json.Marshal(requestBody)

e2e/testcases/chat_completions_request.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ import (
99
"net/http"
1010
"time"
1111

12-
"github.com/vllm-project/semantic-router/e2e/pkg/testcases"
1312
pkgtestcases "github.com/vllm-project/semantic-router/e2e/pkg/testcases"
1413
"k8s.io/client-go/kubernetes"
1514
)
1615

1716
func init() {
18-
testcases.Register("chat-completions-request", testcases.TestCase{
17+
pkgtestcases.Register("chat-completions-request", pkgtestcases.TestCase{
1918
Description: "Send a chat completions request and verify 200 OK response",
2019
Tags: []string{"llm", "functional"},
2120
Fn: testChatCompletionsRequest,

e2e/testcases/stress_test.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package testcases
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
"strings"
11+
"time"
12+
13+
pkgtestcases "github.com/vllm-project/semantic-router/e2e/pkg/testcases"
14+
"k8s.io/client-go/kubernetes"
15+
)
16+
17+
func init() {
18+
pkgtestcases.Register("chat-completions-stress-request", pkgtestcases.TestCase{
19+
Description: "Send 1000 sequential requests and measure success rate",
20+
Tags: []string{"llm", "stress", "reliability"},
21+
Fn: testStressTest,
22+
})
23+
}
24+
25+
// StressTestResult tracks the result of a single request
26+
type StressTestResult struct {
27+
RequestID int
28+
Success bool
29+
StatusCode int
30+
Duration time.Duration
31+
ErrorMessage string
32+
}
33+
34+
func testStressTest(ctx context.Context, client *kubernetes.Clientset, opts pkgtestcases.TestCaseOptions) error {
35+
if opts.Verbose {
36+
fmt.Println("[Test] Starting stress test: 1000 sequential requests")
37+
}
38+
39+
// Setup service connection and get local port
40+
localPort, stopPortForward, err := setupServiceConnection(ctx, client, opts)
41+
if err != nil {
42+
return err
43+
}
44+
defer stopPortForward() // Ensure port forwarding is stopped when test completes
45+
46+
const totalRequests = 1000
47+
var results []StressTestResult
48+
successCount := 0
49+
totalDuration := time.Duration(0)
50+
51+
// Send 1000 requests sequentially
52+
for i := 1; i <= totalRequests; i++ {
53+
result := sendSingleRequest(ctx, i, localPort, opts.Verbose)
54+
results = append(results, result)
55+
56+
if result.Success {
57+
successCount++
58+
}
59+
totalDuration += result.Duration
60+
61+
// Print progress every 100 requests
62+
if opts.Verbose && i%100 == 0 {
63+
currentSuccessRate := float64(successCount) / float64(i) * 100
64+
fmt.Printf("[Test] Progress: %d/%d requests completed (%.2f%% success rate)\n",
65+
i, totalRequests, currentSuccessRate)
66+
}
67+
}
68+
69+
// Calculate statistics
70+
successRate := float64(successCount) / float64(totalRequests) * 100
71+
failureCount := totalRequests - successCount
72+
avgDuration := totalDuration / time.Duration(totalRequests)
73+
74+
// Set details for reporting
75+
if opts.SetDetails != nil {
76+
opts.SetDetails(map[string]interface{}{
77+
"total_requests": totalRequests,
78+
"successful": successCount,
79+
"failed": failureCount,
80+
"success_rate": fmt.Sprintf("%.2f%%", successRate),
81+
"avg_duration_ms": avgDuration.Milliseconds(),
82+
})
83+
}
84+
85+
// Print summary
86+
printStressTestResults(results, totalRequests, successCount, successRate, avgDuration)
87+
88+
if opts.Verbose {
89+
fmt.Printf("[Test] Stress test completed: %d/%d successful (%.2f%% success rate)\n",
90+
successCount, totalRequests, successRate)
91+
}
92+
93+
return nil
94+
}
95+
96+
func sendSingleRequest(ctx context.Context, requestID int, localPort string, verbose bool) StressTestResult {
97+
result := StressTestResult{
98+
RequestID: requestID,
99+
Success: false,
100+
}
101+
102+
start := time.Now()
103+
104+
// Prepare request body
105+
requestBody := map[string]interface{}{
106+
"model": "MoM",
107+
"messages": []map[string]string{
108+
{
109+
"role": "user",
110+
"content": fmt.Sprintf("Request #%d: What is 2+2?", requestID),
111+
},
112+
},
113+
}
114+
115+
jsonData, err := json.Marshal(requestBody)
116+
if err != nil {
117+
result.ErrorMessage = fmt.Sprintf("marshal error: %v", err)
118+
result.Duration = time.Since(start)
119+
return result
120+
}
121+
122+
url := fmt.Sprintf("http://localhost:%s/v1/chat/completions", localPort)
123+
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
124+
if err != nil {
125+
result.ErrorMessage = fmt.Sprintf("create request error: %v", err)
126+
result.Duration = time.Since(start)
127+
return result
128+
}
129+
130+
req.Header.Set("Content-Type", "application/json")
131+
132+
httpClient := &http.Client{
133+
Timeout: 30 * time.Second,
134+
}
135+
136+
resp, err := httpClient.Do(req)
137+
if err != nil {
138+
result.ErrorMessage = fmt.Sprintf("send request error: %v", err)
139+
result.Duration = time.Since(start)
140+
return result
141+
}
142+
defer resp.Body.Close()
143+
144+
body, err := io.ReadAll(resp.Body)
145+
if err != nil {
146+
result.ErrorMessage = fmt.Sprintf("read response error: %v", err)
147+
result.Duration = time.Since(start)
148+
result.StatusCode = resp.StatusCode
149+
return result
150+
}
151+
152+
result.Duration = time.Since(start)
153+
result.StatusCode = resp.StatusCode
154+
155+
if resp.StatusCode == http.StatusOK {
156+
result.Success = true
157+
} else {
158+
result.ErrorMessage = fmt.Sprintf("status %d: %s", resp.StatusCode, string(body))
159+
}
160+
161+
return result
162+
}
163+
164+
func printStressTestResults(results []StressTestResult, totalRequests, successCount int, successRate float64, avgDuration time.Duration) {
165+
separator := strings.Repeat("=", 80)
166+
fmt.Println("\n" + separator)
167+
fmt.Println("Stress Test Results")
168+
fmt.Println(separator)
169+
fmt.Printf("Total Requests: %d\n", totalRequests)
170+
fmt.Printf("Successful: %d\n", successCount)
171+
fmt.Printf("Failed: %d\n", totalRequests-successCount)
172+
fmt.Printf("Success Rate: %.2f%%\n", successRate)
173+
fmt.Printf("Average Duration: %v\n", avgDuration)
174+
fmt.Println(separator)
175+
176+
// Show first 10 failures if any
177+
failureCount := 0
178+
fmt.Println("\nFirst 10 Failures (if any):")
179+
for _, result := range results {
180+
if !result.Success && failureCount < 10 {
181+
failureCount++
182+
fmt.Printf(" Request #%d: %s (duration: %v)\n",
183+
result.RequestID, result.ErrorMessage, result.Duration)
184+
}
185+
if failureCount >= 10 {
186+
break
187+
}
188+
}
189+
190+
if failureCount == 0 {
191+
fmt.Println(" No failures! 🎉")
192+
}
193+
fmt.Println()
194+
}

0 commit comments

Comments
 (0)