Skip to content

test(e2e): provider_key rotation with zero in-flight disruption (#196 L3, #271) - #523

Merged
moonming merged 2 commits into
mainfrom
test/issue-L3-pk-rotation
Jun 5, 2026
Merged

test(e2e): provider_key rotation with zero in-flight disruption (#196 L3, #271)#523
moonming merged 2 commits into
mainfrom
test/issue-L3-pk-rotation

Conversation

@moonming

@moonming moonming commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

What

A liveness/smoke pin over the provider_key in-place rotation under load path (#196 L3). Under sustained concurrency (8 workers, 80 requests), a mid-stream PUT /admin/v1/provider_keys/:id (same id + api_base, new secret) keeps every request serving and bumps the resource revision; the caller's api_key + model alias are untouched.

Scope honesty (per the #523 audit)

The audit (HIGH) established that "zero in-flight disruption" is an architectural guarantee, not a falsifiable property in this DP: there is no per-provider_key upstream client/pool — the DP holds one shared client and reads pk.secret/api_base per-request from an atomic ArcSwap snapshot, with each in-flight request holding its own snapshot Arc to completion. So the original "≥1 failure on the swap" framing would have certified a hollow green.

Re-scoped accordingly: this is a smoke/liveness pin (catches a future regression that wedged dispatch or broke watch-apply on a PK PUT) + a revision-bump check, with the architecture documented in the header. It does not claim to probe a teardown race (there isn't one).

The real remaining facet → #220 (NOT closed here)

Asserting the rotated secret actually reaches upstream (old rejected / new accepted) needs a credential-sensitive mock — that's the pre-existing #220. This PR refs #271/#220 but does not close them; see the #271 comment for the architectural finding.

Verification

tsc --noEmit clean for this file. Isolated DP e2e CI is authoritative. maxRetries:2 immunizes the strict all-succeed gate against a loopback transient (a real wedge fails all retries too).

Refs #196 L3, #127 L3, #271, #220.

Summary by CodeRabbit

  • Tests
    • Added an end-to-end test that verifies provider key rotation causes zero disruption to in-flight API requests under sustained concurrent load.
    • Confirms all requests complete successfully with no errors during rotation and total successes match expected count.
    • Validates that the provider key rotation is recorded (revision increases) and completes as expected.

…196 L3, #271)

Closes the open #271 gap. provider_key_rotation_test.go (CP) pins
rotation correctness + post-swap liveness but rotates with no traffic
in flight. This pins the distinguishing L3 property: under sustained
concurrency (8 workers, 80 requests), an in-place secret rotation
(PUT /admin/v1/provider_keys/:id — same id + api_base, new secret)
fired mid-stream must cause ZERO failed requests, and the resource
revision must bump. A DP that rebuilt its per-provider_key upstream
client non-atomically (briefly losing the key or dropping in-flight
requests on the snapshot swap) would surface ≥1 failure.

Out of scope (separate gap #220): which secret the DP dialed upstream
— the mock ignores the credential, so only the disruption-free
property is observable here, which is exactly L3's claim.

Refs #196 L3, #127 L3, closes #271.
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d519c90b-c59b-49d2-bba0-5ad041bed299

📥 Commits

Reviewing files that changed from the base of the PR and between 3327bf7 and 0dc4d5e.

📒 Files selected for processing (1)
  • tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/src/cases/provider-key-rotation-e2e.test.ts

📝 Walkthrough

Walkthrough

This PR adds an end-to-end test that verifies provider key secret rotation does not cause in-flight request failures. The test initializes the app and mock OpenAI upstream, runs sustained concurrent chat requests across multiple workers, triggers a provider key rotation mid-flight at a fixed request index, and asserts zero disruption with a confirmed revision increment.

Changes

Provider Key Rotation E2E Test

Layer / File(s) Summary
Test configuration and environment setup
tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
Constants define caller identity, request concurrency parameters, rotation trigger index, and error reporting helper. Environment setup pings etcd, starts mock upstream, spawns app, and creates provider key, model, and API key in beforeAll; cleanup exits app and closes upstream in afterAll.
Client readiness probe and pre-rotation capture
tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
OpenAI client created pointing to app proxy; test waits for configuration propagation via repeated chat completion probes until responses contain ok. Pre-rotation provider key revision is read before workload.
Test execution with concurrent rotation
tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
Shared state tracks sent/success counters, rotation-once flag, and failures list. Concurrent workers loop through requests, one worker triggers an in-place provider key secret rotation exactly once at the configured index via admin PUT, all workers continue issuing chat completions, and responses or failures are recorded.
Assertions and result verification
tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
Asserts rotation was triggered and failures list is empty (zero in-flight disruption). Confirms total successes match sent requests and all responses contained expected ok content. Fetches post-rotation provider key revision and asserts it increased versus pre-rotation revision.

Sequence Diagram

sequenceDiagram
  participant TestRunner
  participant Etcd
  participant MockOpenAIUpstream
  participant AppProxy
  participant AdminAPI

  TestRunner->>Etcd: ping for readiness
  TestRunner->>MockOpenAIUpstream: start mock upstream
  TestRunner->>AppProxy: spawn app under test
  TestRunner->>AdminAPI: create provider key, model, caller API key
  TestRunner->>AppProxy: create OpenAI client -> probe chat completions until "ok"
  TestRunner->>AppProxy: spawn concurrent workers issuing chat completions
  AppProxy->>MockOpenAIUpstream: forward chat completion requests
  loop during workload
    TestRunner->>AdminAPI: (one worker at ROTATE_AT) admin PUT to rotate provider key secret
    AdminAPI->>AppProxy: provider key secret updated (same id/api_base)
  end
  AppProxy->>TestRunner: responses for each chat request (expect "ok")
  TestRunner->>AdminAPI: fetch provider key revision after run
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

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

)

The audit (HIGH) showed "zero in-flight disruption" is an ARCHITECTURAL
guarantee, not a falsifiable property: the DP holds one shared upstream
client and reads pk.secret/api_base per-request from an atomic ArcSwap
snapshot, with in-flight requests holding their own snapshot Arc — there
is no per-provider_key client/pool to tear down on a hot-swap. So the
old "≥1 failure on swap" framing certified a hollow green.

Re-scope honestly: this is a liveness/smoke pin that an in-place secret
rotation under sustained load keeps dispatch serving + bumps revision
(catches a future regression that wedged dispatch or broke watch-apply
on a PK PUT), with the architecture documented in the header. The real
remaining facet — the rotated secret actually reaching upstream — needs
a credential-sensitive mock and stays as #220 (not closed here).

Also bump maxRetries 0→2 to immunize the strict all-succeed gate against
a loopback transient (a real wedge fails all retries too).

Refs #196 L3, #127 L3, #271, #220.
@moonming

moonming commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

Independent audit (CLAUDE.md §8) — resolved

Verdict BLOCK on HIGH-1; addressed by re-scoping (latest commit). The auditor verified against DP source (bridge.rs one shared client; dispatch.rs/chat.rs per-request snapshot reads; snapshot.rs ArcSwap; supervisor.rs CAS watch-apply) that there is no per-provider_key client/pool to tear downpk.secret/api_base are read per-request from an atomic snapshot, in-flight requests hold their own Arc. So "≥1 failure on hot-swap" is impossible by construction and the old framing certified a hollow green (the §7 "claims more than it verifies" gap).

Fixed: header + PR + test title re-scoped to a rotate-under-load liveness/smoke pin + revision bump, with the architecture documented; the real "rotated secret reaches upstream" facet stays as #220 (credential-sensitive mock needed), not closed here. Also bumped maxRetries 0→2 (audit LOW: immunize the strict gate against a loopback transient — a real wedge fails all retries too).

Auditor confirmed CLEAR on the mechanics: valid PUT body, correct revision bump (1→2), real in-flight overlap during the PUT, correct isolation/cleanup, rotated guard genuinely asserted. MEDIUM-1 (overlap density) only mattered under the dropped disruption-probe framing.

@moonming

moonming commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

CI: my test passed; failure is an unrelated cache flake — re-running

The e2e job went red, but this PR's test passed: ✓ src/cases/provider-key-rotation-e2e.test.ts (1 test) 1792ms.

The failure is in an unrelated file — cache-policy-e2e.test.ts › second identical request is served from cache, upstream not re-hitAssertionError: expected 3 to be 2 (the upstream was hit 3× instead of 2× — a cache-warm/timing flake, the cache write hadn't landed before the second request). This PR only adds provider-key-rotation-e2e.test.ts; it touches no caching code, and under maxForks=2 each test runs its own spawnApp with a unique etcd prefix + its own DP/cache, so this PR cannot affect another test's cache-hit count. Pre-existing flaky-shaped failure (timing), not a regression here.

Re-enqueued the failed job. If cache-policy-e2e recurs it's a flaky cache test to harden separately (not this PR).

@moonming
moonming merged commit 6fcc246 into main Jun 5, 2026
12 of 13 checks passed
@moonming
moonming deleted the test/issue-L3-pk-rotation branch June 5, 2026 09:11
Sign up for free to 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.

1 participant