feat(replay): add TestCaseMutator hook for pre-replay test case mutations#4238
feat(replay): add TestCaseMutator hook for pre-replay test case mutations#4238Vedanshi27vishu wants to merge 16 commits into
Conversation
|
CLA Assistant Lite bot: I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request |
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
There was a problem hiding this comment.
Pull request overview
Adds replay-time support for decrypting ENC:<keyId>:<base64> request values (recorded with encryption_protection.enabled=true) by fetching AES-256 keys from the api-server KMS, so replayed requests send real secret values rather than the raw ENC: strings.
Changes:
- Introduces a per-run KMS key cache + AES-256-GCM decryption utility for
ENC:-prefixed values. - Wires request decryption into the HTTP replay path (including deferred streaming HTTP tests) before
SimulateRequest.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| pkg/service/replay/decrypt.go | New KMS key fetch/cache and ENC: decryption logic for HTTP request fields. |
| pkg/service/replay/replay.go | Integrates decryption into RunTestSet before request simulation (standard + streaming HTTP). |
Comments suppressed due to low confidence (2)
pkg/service/replay/replay.go:1877
- The streaming path also decrypts the request in-place and then persists
tc.HTTPReqinto the report (models.TestResult.Req), which can re-expose secrets that were stored asENC:in the recording. Decrypt only for the outgoing request and restore the original encrypted values before writing results.
MockPath: filepath.Join(r.config.Path, testSetID, "mocks.yaml"),
Noise: testCase.Noise,
Result: *testResult,
TimeTaken: time.Since(started).String(),
pkg/service/replay/replay.go:1455
- Decrypting in-place mutates
testCase.HTTPReqand later persists the decrypted header/params/body intomodels.TestResult.Req, which can leak secrets that were intentionally stored asENC:in the recording. Consider decrypting only for the outgoing simulation and then restoring the original encrypted values before building reports/results.
var activeTestCases []*models.TestCase
var streamingTests []streamingTest
for _, testCase := range testCases {
if _, ok := selectedTests[testCase.Name]; !ok && len(selectedTests) != 0 {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
1 similar comment
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
Signed-off-by: Vedanshi Aggarwal <vedanshi27vishu@gmail.com>
…r, Debug logs, unit tests Signed-off-by: Vedanshi Aggarwal <vedanshi27vishu@gmail.com>
e19df50 to
f92d2d3
Compare
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
Replace the inline kmsCache + decrypt.go approach with a minimal BeforeTestCaseRun hook in the TestHooks interface. OSS keploy now only calls the hook; actual AES-256-GCM + KMS decryption lives in enterprise's DecryptingTestHooks implementation. - Add BeforeTestCaseRun(ctx, tc, testSetID) to TestHooks interface - Add no-op default in Hooks struct - Call hook in replay.go before SimulateRequest (both normal and streaming) - Remove decrypt.go, decrypt_test.go (moved to enterprise) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Vedanshi Aggarwal <vedanshi27vishu@gmail.com>
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
Move BeforeTestCaseRun hook call to execute before ReplaceBaseURL so that encrypted URL query params are decrypted before the base URL replacement parses them. Decryption must be the first operation on the test case — before any field-level processing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Vedanshi Aggarwal <vedanshi27vishu@gmail.com>
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
- Fix log message: "unmodified test case" → "skipping pre-replay mutation" (tc may be partially mutated before error returns) - Gate ReplaceBaseURL with replay==0 to prevent path.Join doubling the base-path prefix on retry passes - Add comment on streaming path explaining why replay==0 guard is not needed (Phase 2 has no retry loop) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Vedanshi Aggarwal <vedanshi27vishu@gmail.com>
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
- Move exitLoopChan check before BeforeTestCaseRun so we don't mutate test cases that will never execute when an exit signal is pending - Move streaming BeforeTestCaseRun before effectiveStreamMockWindow so any timestamps decrypted by the mutator feed into the mock filter window - Neutralise hook error log message — 'skipping' implied rollback but tc is passed by pointer and may be partially mutated before the error Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
| if err := mutator.BeforeTestCaseRun(runTestSetCtx, testCase, testSetID); err != nil { | ||
| utils.LogError(r.logger, err, "BeforeTestCaseRun hook failed; replay continues with test case in current state", | ||
| zap.String("testcase", testCase.Name), | ||
| zap.String("next_step", "check hook implementation or api-server connectivity")) |
| if err := mutator.BeforeTestCaseRun(runTestSetCtx, tc, testSetID); err != nil { | ||
| utils.LogError(r.logger, err, "BeforeTestCaseRun hook failed; replay continues with test case in current state", | ||
| zap.String("testcase", tc.Name), | ||
| zap.String("next_step", "check hook implementation or api-server connectivity")) |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
next_step was hard-coded to "api-server connectivity" but TestCaseMutator is a general-purpose hook — the hint now lists common dependency types (KMS, auth, network) so it stays accurate for all custom implementations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
The comment said "before any in-place mutations" but IsLast is already set earlier in the loop — narrowed to "before the hook and URL mutations" which is what the gate actually protects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The CLA check failed. Please ensure you have:
After fixing these issues, comment 'recheck' to trigger the workflow again. |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
Summary
Adds an optional
TestCaseMutatorinterface to the replay service so that enterprise/custom implementations can mutate test cases (e.g. decrypt secrets, inject headers) immediately before each test case is replayed — without modifying the coreTestHooksinterface.Why a separate interface?
Extending
TestHooksdirectly would be a compile-time breaking change for any external hook implementations.TestCaseMutatoris detected via type assertion inRunTestSet, so existing implementations that do not implement it continue to work unchanged.Changes
pkg/service/replay/service.goTestCaseMutatorinterface with a single method:pkg/service/replay/replay.goBeforeTestCaseRunvia type assertion beforeReplaceBaseURLandSimulateRequestreplay == 0to prevent double-mutation on retry passesHow decryption fits in
The actual AES-256-GCM + KMS decryption of
ENC:values lives in the enterprise repo (private). Enterprise implementsTestCaseMutatorand wires it in at startup. This PR only adds the OSS hook point.Merge order
Must merge before enterprise and k8s-proxy PRs.