Skip to content

Commit 5ee0143

Browse files
committed
update
Signed-off-by: bitliu <[email protected]>
1 parent 2506f8d commit 5ee0143

File tree

11 files changed

+405
-34
lines changed

11 files changed

+405
-34
lines changed

.github/workflows/integration-test-k8s.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,10 @@ jobs:
6565
id: e2e-test
6666
run: |
6767
set +e # Don't exit on error, we want to capture the result
68-
make e2e-test PROFILE=ai-gateway
68+
make e2e-test E2E_PROFILE=ai-gateway E2E_VERBOSE=true E2E_KEEP_CLUSTER=false
6969
TEST_EXIT_CODE=$?
7070
echo "test_exit_code=${TEST_EXIT_CODE}" >> $GITHUB_OUTPUT
7171
exit ${TEST_EXIT_CODE}
72-
env:
73-
E2E_VERBOSE: "true"
74-
KEEP_CLUSTER: "true"
7572
7673
- name: Upload test reports
7774
if: always()

e2e/README.md

Lines changed: 101 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,30 +84,85 @@ make e2e-test
8484
### Run specific profile
8585

8686
```bash
87-
make e2e-test PROFILE=ai-gateway
87+
make e2e-test E2E_PROFILE=ai-gateway
8888
```
8989

9090
### Run specific test cases
9191

9292
```bash
93-
# Run only the progressive stress test
94-
./e2e/cmd/e2e/e2e --profile ai-gateway --test-cases chat-completions-progressive-stress --verbose
93+
# Run only specific test cases using make
94+
make e2e-test-specific E2E_TESTS="chat-completions-progressive-stress"
9595

96-
# Run multiple specific test cases
97-
./e2e/cmd/e2e/e2e --profile ai-gateway --test-cases chat-completions-request,chat-completions-progressive-stress
96+
# Run multiple specific test cases using make
97+
make e2e-test-specific E2E_TESTS="chat-completions-request,chat-completions-progressive-stress"
98+
99+
# Or run directly with the binary
100+
./bin/e2e -profile ai-gateway -tests chat-completions-progressive-stress -verbose
101+
102+
# Run multiple specific test cases with the binary
103+
./bin/e2e -profile ai-gateway -tests "chat-completions-request,chat-completions-progressive-stress"
98104
```
99105

100106
### Run with custom options
101107

102108
```bash
103109
# Keep cluster after test
104-
make e2e-test KEEP_CLUSTER=true
110+
make e2e-test E2E_KEEP_CLUSTER=true
105111

106112
# Use existing cluster
107-
make e2e-test USE_EXISTING_CLUSTER=true
113+
make e2e-test E2E_USE_EXISTING_CLUSTER=true
114+
115+
# Disable verbose output
116+
make e2e-test E2E_VERBOSE=false
117+
118+
# Run tests in parallel
119+
make e2e-test E2E_PARALLEL=true
120+
121+
# Combine multiple options
122+
make e2e-test E2E_PROFILE=ai-gateway E2E_KEEP_CLUSTER=true E2E_VERBOSE=true
123+
```
124+
125+
### Debug mode
126+
127+
```bash
128+
# Run tests with debug mode (keeps cluster and enables verbose logging)
129+
make e2e-test-debug
130+
```
131+
132+
### Advanced Workflows
133+
134+
#### Setup environment once, run tests multiple times
135+
136+
This is useful for development and debugging:
137+
138+
```bash
139+
# Step 1: Setup environment only (no tests)
140+
make e2e-setup
141+
142+
# Step 2: Run all tests (skip setup)
143+
make e2e-test-only
144+
145+
# Step 3: Run specific tests (skip setup)
146+
make e2e-test-only E2E_TESTS="chat-completions-request"
147+
148+
# Step 4: Make code changes and re-run tests
149+
make e2e-test-only E2E_TESTS="domain-classify"
150+
151+
# Step 5: Clean up when done
152+
make e2e-cleanup
153+
```
154+
155+
#### Using binary directly
108156

109-
# Verbose output
110-
make e2e-test VERBOSE=true
157+
```bash
158+
# Setup only
159+
./bin/e2e -profile ai-gateway -setup-only -keep-cluster -verbose
160+
161+
# Run tests only (assumes environment is already deployed)
162+
./bin/e2e -profile ai-gateway -skip-setup -use-existing-cluster -verbose
163+
164+
# Run specific tests only
165+
./bin/e2e -profile ai-gateway -skip-setup -use-existing-cluster -tests "chat-completions-request"
111166
```
112167

113168
### Test Reports
@@ -116,9 +171,46 @@ After running tests, reports are generated:
116171

117172
- `test-report.json`: Structured test results
118173
- `test-report.md`: Human-readable Markdown report
174+
- `semantic-router-logs.txt`: Complete semantic-router pod logs
119175

120176
Each test case also prints detailed statistics to the console.
121177

178+
## Environment Variables
179+
180+
The following environment variables can be used to customize test execution:
181+
182+
| Variable | Description | Default | Example |
183+
|----------|-------------|---------|---------|
184+
| `E2E_PROFILE` | Test profile to run | `ai-gateway` | `make e2e-test E2E_PROFILE=ai-gateway` |
185+
| `E2E_CLUSTER_NAME` | Kind cluster name | `semantic-router-e2e` | `make e2e-test E2E_CLUSTER_NAME=my-cluster` |
186+
| `E2E_IMAGE_TAG` | Docker image tag | `e2e-test` | `make e2e-test E2E_IMAGE_TAG=v1.0.0` |
187+
| `E2E_KEEP_CLUSTER` | Keep cluster after tests | `false` | `make e2e-test E2E_KEEP_CLUSTER=true` |
188+
| `E2E_USE_EXISTING_CLUSTER` | Use existing cluster | `false` | `make e2e-test E2E_USE_EXISTING_CLUSTER=true` |
189+
| `E2E_VERBOSE` | Enable verbose logging | `true` | `make e2e-test E2E_VERBOSE=false` |
190+
| `E2E_PARALLEL` | Run tests in parallel | `false` | `make e2e-test E2E_PARALLEL=true` |
191+
| `E2E_TESTS` | Specific test cases to run | (all tests) | `make e2e-test-specific E2E_TESTS="test1,test2"` |
192+
| `E2E_SETUP_ONLY` | Only setup profile without running tests | `false` | `make e2e-test E2E_SETUP_ONLY=true` |
193+
| `E2E_SKIP_SETUP` | Skip setup and only run tests | `false` | `make e2e-test E2E_SKIP_SETUP=true` |
194+
195+
**Note**: When using the binary directly (`./bin/e2e`), use command-line flags instead:
196+
197+
- `-profile` instead of `E2E_PROFILE`
198+
- `-cluster` instead of `E2E_CLUSTER_NAME`
199+
- `-image-tag` instead of `E2E_IMAGE_TAG`
200+
- `-keep-cluster` instead of `E2E_KEEP_CLUSTER`
201+
- `-use-existing-cluster` instead of `E2E_USE_EXISTING_CLUSTER`
202+
- `-verbose` instead of `E2E_VERBOSE`
203+
- `-parallel` instead of `E2E_PARALLEL`
204+
- `-tests` instead of `E2E_TESTS`
205+
- `-setup-only` instead of `E2E_SETUP_ONLY`
206+
- `-skip-setup` instead of `E2E_SKIP_SETUP`
207+
208+
Example:
209+
210+
```bash
211+
./bin/e2e -profile ai-gateway -keep-cluster -verbose -tests "chat-completions-request"
212+
```
213+
122214
## Adding New Test Profiles
123215

124216
1. Create a new directory under `profiles/`

e2e/cmd/e2e/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ func main() {
2525
verbose = flag.Bool("verbose", false, "Enable verbose logging")
2626
parallel = flag.Bool("parallel", false, "Run tests in parallel")
2727
testCases = flag.String("tests", "", "Comma-separated list of test cases to run (empty means all)")
28+
setupOnly = flag.Bool("setup-only", false, "Only setup the profile without running tests")
29+
skipSetup = flag.Bool("skip-setup", false, "Skip profile setup and only run tests (assumes environment is already deployed)")
2830
)
2931

3032
flag.Parse()
3133

34+
// Validate flags
35+
if *setupOnly && *skipSetup {
36+
fmt.Fprintf(os.Stderr, "Error: --setup-only and --skip-setup cannot be used together\n")
37+
os.Exit(1)
38+
}
39+
3240
// Parse test cases
3341
var testCasesList []string
3442
if *testCases != "" {
@@ -48,6 +56,8 @@ func main() {
4856
Verbose: *verbose,
4957
Parallel: *parallel,
5058
TestCases: testCasesList,
59+
SetupOnly: *setupOnly,
60+
SkipSetup: *skipSetup,
5161
}
5262

5363
// Get the profile implementation

e2e/pkg/framework/runner.go

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,28 +115,42 @@ func (r *Runner) Run(ctx context.Context) error {
115115
r.reporter.SetKubeClient(kubeClient)
116116

117117
// Step 4: Setup profile (deploy Helm charts, etc.)
118-
setupOpts := &SetupOptions{
119-
KubeClient: kubeClient,
120-
KubeConfig: kubeConfig,
121-
ClusterName: r.opts.ClusterName,
122-
ImageTag: r.opts.ImageTag,
123-
Verbose: r.opts.Verbose,
124-
}
125-
126-
if err := r.profile.Setup(ctx, setupOpts); err != nil {
127-
exitCode = 1
128-
return fmt.Errorf("failed to setup profile: %w", err)
129-
}
130-
131-
defer func() {
132-
teardownOpts := &TeardownOptions{
118+
if !r.opts.SkipSetup {
119+
setupOpts := &SetupOptions{
133120
KubeClient: kubeClient,
134121
KubeConfig: kubeConfig,
135122
ClusterName: r.opts.ClusterName,
123+
ImageTag: r.opts.ImageTag,
136124
Verbose: r.opts.Verbose,
137125
}
138-
r.profile.Teardown(context.Background(), teardownOpts)
139-
}()
126+
127+
if err := r.profile.Setup(ctx, setupOpts); err != nil {
128+
exitCode = 1
129+
return fmt.Errorf("failed to setup profile: %w", err)
130+
}
131+
132+
defer func() {
133+
teardownOpts := &TeardownOptions{
134+
KubeClient: kubeClient,
135+
KubeConfig: kubeConfig,
136+
ClusterName: r.opts.ClusterName,
137+
Verbose: r.opts.Verbose,
138+
}
139+
r.profile.Teardown(context.Background(), teardownOpts)
140+
}()
141+
} else {
142+
r.log("⏭️ Skipping profile setup (--skip-setup enabled)")
143+
}
144+
145+
// If setup-only mode, stop here
146+
if r.opts.SetupOnly {
147+
r.log("✅ Profile setup complete (--setup-only mode)")
148+
r.log("💡 Cluster is ready. You can now:")
149+
r.log(" - Run tests manually: ./bin/e2e -profile %s -skip-setup -use-existing-cluster", r.opts.Profile)
150+
r.log(" - Inspect the cluster: kubectl --context kind-%s get pods -A", r.opts.ClusterName)
151+
r.log(" - Clean up when done: make e2e-cleanup")
152+
return nil
153+
}
140154

141155
// Step 5: Run tests
142156
results, err := r.runTests(ctx, kubeClient)

e2e/pkg/framework/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ type TestOptions struct {
9696

9797
// TestCases is a list of specific test cases to run (empty means all)
9898
TestCases []string
99+
100+
// SetupOnly only sets up the profile without running tests
101+
SetupOnly bool
102+
103+
// SkipSetup skips profile setup and only runs tests (assumes environment is already deployed)
104+
SkipSetup bool
99105
}
100106

101107
// TestResult represents the result of a test case

e2e/profiles/ai-gateway/profile.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ func (p *Profile) GetTestCases() []string {
109109
return []string{
110110
"chat-completions-request",
111111
"chat-completions-stress-request",
112-
"chat-completions-progressive-stress",
113112
"domain-classify",
114113
"semantic-cache",
115114
"pii-detection",

e2e/testcases/chat_completions_stress_request.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616

1717
func init() {
1818
pkgtestcases.Register("chat-completions-stress-request", pkgtestcases.TestCase{
19-
Description: "Send 1000 sequential requests and measure success rate",
19+
Description: "Send 200 sequential requests and measure success rate",
2020
Tags: []string{"llm", "stress", "reliability"},
2121
Fn: testStressTest,
2222
})

src/semantic-router/pkg/utils/pii/policy.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,18 @@ type PolicyChecker struct {
1414
}
1515

1616
// IsPIIEnabled checks if PII detection is enabled and properly configured
17+
// For LoRA adapters, it falls back to the base model's PII policy if not found
1718
func (c *PolicyChecker) IsPIIEnabled(model string) bool {
1819
modelConfig, exists := c.ModelConfigs[model]
20+
if !exists {
21+
// Try to find base model for LoRA adapters
22+
baseModel := c.findBaseModelForLoRA(model)
23+
if baseModel != "" {
24+
logging.Infof("LoRA adapter '%s' not found in model configs, falling back to base model '%s'", model, baseModel)
25+
modelConfig, exists = c.ModelConfigs[baseModel]
26+
}
27+
}
28+
1929
if !exists {
2030
logging.Infof("No PII policy found for model %s, allowing request", model)
2131
return false
@@ -33,13 +43,23 @@ func NewPolicyChecker(cfg *config.RouterConfig, modelConfigs map[string]config.M
3343
}
3444

3545
// CheckPolicy checks if the detected PII types are allowed for the given model
46+
// For LoRA adapters, it falls back to the base model's PII policy if not found
3647
func (pc *PolicyChecker) CheckPolicy(model string, detectedPII []string) (bool, []string, error) {
3748
if !pc.IsPIIEnabled(model) {
3849
logging.Infof("PII detection is disabled, allowing request")
3950
return true, nil, nil
4051
}
4152

4253
modelConfig, exists := pc.ModelConfigs[model]
54+
if !exists {
55+
// Try to find base model for LoRA adapters
56+
baseModel := pc.findBaseModelForLoRA(model)
57+
if baseModel != "" {
58+
logging.Infof("LoRA adapter '%s' not found in model configs, falling back to base model '%s' for PII policy", model, baseModel)
59+
modelConfig, exists = pc.ModelConfigs[baseModel]
60+
}
61+
}
62+
4363
if !exists {
4464
// If no specific config, allow by default
4565
logging.Infof("No PII policy found for model %s, allowing request", model)
@@ -102,3 +122,17 @@ func ExtractAllContent(userContent string, nonUserMessages []string) []string {
102122
allContent = append(allContent, nonUserMessages...)
103123
return allContent
104124
}
125+
126+
// findBaseModelForLoRA finds the base model for a given LoRA adapter name
127+
// Returns empty string if the LoRA adapter is not found in any model's LoRA list
128+
func (pc *PolicyChecker) findBaseModelForLoRA(loraName string) string {
129+
for modelName, modelConfig := range pc.ModelConfigs {
130+
for _, lora := range modelConfig.LoRAs {
131+
if lora.Name == loraName {
132+
logging.Debugf("Found base model '%s' for LoRA adapter '%s'", modelName, loraName)
133+
return modelName
134+
}
135+
}
136+
}
137+
return ""
138+
}

0 commit comments

Comments
 (0)