test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic - #890

Merged
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race
Aug 4, 2026
Merged

test(e2e): stop the SLS masking spec asserting over its own warm-up traffic#890
jarvis9443 merged 2 commits into
mainfrom
test/sls-masked-capture-warmup-race

Conversation

@jarvis9443

@jarvis9443jarvis9443 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes#889

Fixes a CI-only failure in sls-content-capture-masked-e2e, seen on #888:

AssertionError: expected '...' not to contain 'alice@example.com'

Cause

The spec waits for config propagation by sending warm-up chats until the masked form shows up on the wire. Those warm-up requests are exported to SLS too, and the ones served before the guardrail went live carry the unmasked reply — which is correct behaviour for a gateway that has no masking rule yet.

decodedTextFor joins every PutLogs body the mock has ever received, so expect(fullText).not.toContain(EMAIL) was also asserting over those pre-propagation exports. On a fast machine the guardrail is live by the first warm-up, nothing unmasked is ever exported, and the spec passes; on slower CI it is not, and the raw address is there.

The product behaviour under test is fine — the assertion population was wrong.

Fix

Snapshot sls.requests.length once the config is confirmed live, and scope the assertions to what was exported after it. decodedTextFor and waitForToken take a fromIndex (default 0, so the other SLS specs are unchanged).

Verification

The condition is timing-dependent, so I forced it rather than guessing: inserting one request that is served after the model is live but before the guardrail exists reproduces the CI failure exactly with the old assertion scope, and passes with this change. All four SLS specs pass locally.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation of masked chat and bridge responses.
    • Excluded configuration warm-up traffic from exporter assertions to prevent false results.
    • Enhanced test polling and decoding to focus on responses received after setup.
    • Continued verifying that masked responses contain no raw email data.

…raffic
`sls-content-capture-masked-e2e` waits for config propagation by sending
warm-up chats until the masked form appears on the wire. Those warm-up
requests are exported too, and the ones served before the guardrail went
live carry the UNMASKED reply — correct behaviour for a gateway that has
no masking rule yet.
`decodedTextFor` joins every PutLogs body the mock ever received, so
`expect(fullText).not.toContain(EMAIL)` was also asserting over those
pre-propagation exports. On a fast machine the guardrail is live by the
first warm-up and nothing unmasked is ever exported; on slower CI it is
not, and the spec fails with the raw address present.
Snapshot `sls.requests.length` once the config is confirmed live and scope
the assertions to what was exported after it, via a `fromIndex` argument
on `decodedTextFor` / `waitForToken` (default 0, so other callers are
unchanged).
Verified by forcing the condition: a request served after the model is
live but before the guardrail exists reproduces the CI failure exactly,
and passes with this change.
@coderabbitai

coderabbitaiBot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 271099aa-fe6e-4c9d-8ed7-307cd0ce640c

📥 Commits

Reviewing files that changed from the base of the PR and between d31e80d and 99fd09e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
📝 Walkthrough

Walkthrough

The SLS mock now supports request-index filtering for decoded text and token polling. The masked content capture test records the post-warm-up index and applies it to chat and bridge export assertions.

Changes

Masked SLS capture assertions

Layer / File(s)Summary
Scope SLS decoding and polling
tests/e2e/src/harness/sls-mock.ts
decodedTextFor and waitForToken accept fromIndex and ignore earlier SLS requests.
Apply the post-warm-up request index
tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
The test records the request index after warm-up and uses it for chat and bridge export checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately describes the main change: fixing a test that was asserting over warm-up traffic instead of excluding it from the SLS masking specification.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
E2e Test Quality Review✅ PassedE2E test correctly isolates warm-up traffic from assertions by snapshotting request count after guardrail propagation. All real services used appropriately, assertions straightforward, error handli...
Security Check✅ PassedThis PR modifies only test code (E2E test harness and test case). The changes add an optional fromIndex parameter to decodedTextFor and waitForToken functions with default value 0, maintain...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/sls-masked-capture-warmup-race

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts`:
- Around line 194-200: Ensure the warm-up boundary in the test aligns with
exporter completion before capturing afterWarmup: after waitConfigPropagation,
explicitly drain or flush the SLS exporter, or wait long enough for its 5-second
buffer interval to elapse before snapshotting sls.requests.length.
Alternatively, add and use a correlation marker or request ID so decodedTextFor
can distinguish warm-up records from post-mask traffic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f37a747-62d5-46d1-83e5-f4ba9db55b65

📥 Commits

Reviewing files that changed from the base of the PR and between 81e4fc6 and d31e80d.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
  • tests/e2e/src/harness/sls-mock.ts

Comment threadtests/e2e/src/cases/sls-content-capture-masked-e2e.test.ts
…boundary
An index boundary alone is not deterministic: the sink buffers records and
flushes on a 5s tick (`PipelineConfig { max_batch: 100, flush_interval: 5s }`),
so a warm-up record still in that buffer ships in the SAME PutLogs body as
the requests under test, and slicing by request index cannot separate them.
Send one marker request after the guardrail is live — masked, so it carries
no raw PII — and wait for it to arrive before snapshotting. The sink flushes
its buffer in order, so the marker being visible proves every warm-up record
has already shipped.
@jarvis9443
jarvis9443 merged commit 17141a6 into mainAug 4, 2026
11 checks passed
@jarvis9443
jarvis9443 deleted the test/sls-masked-capture-warmup-race branch August 4, 2026 14:55
jarvis9443 added a commit that referenced this pull request Aug 31, 2026
…ne copy of it
Two findings from a cross-plane review of the previous commits.
The previous commit said a rename "splits the counter into a before and
an after series". A rename does not reach the data plane at all on its
own: `user_name` is stamped when cp-api PROJECTS the api_keys row, and
nothing reprojects a key because its member was renamed — SCIM's
`UpdateMember` writes `auth."user".name` and only cascades to the
member's keys on an `Active` flip, the dashboard's self-service rename
goes to better-auth without touching cp-api, and none of the eight
`reprojectAPIKey` call sites is a rename. So the label holds the name as
of that key's last write, and a rename shows up only once the key is
written again for some other reason. Both label structs now say so, and
the budget-gauge note points at it rather than implying renames strand
samples on their own.
`apply_caller_identity` also ran `user_name` through `sanitize_tag` while
the four #890 families stamp the same value raw off the same row. That
call protects the cp-api wire and the provider-call log line, and
`user_name` reaches neither — it is `#[serde(skip)]` and
`log_provider_call` does not render it. Its only destination is the
metric label, so sanitising this one copy could make a long or odd name
read differently on `aisix_usage_event*` than on `aisix_llm_*` and drop
that member out of a cross-family join on the pair. Removed; `user_id`
keeps it, because that one does travel to cp-api.
Also corrects a type name in the new `CallerIdentity` doc: the
allowlist is `HEADER_TEMPLATE_VARS`, resolved by `HeaderVars::resolve`.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky/possible mask race: streaming SLS full-capture intermittently contains raw PII on CI (sls-content-capture-masked-e2e)

1 participant

@jarvis9443