Skip to content

test(e2e): migrate the seed-tail tests off the Admin API onto etcd/status - #792

Merged
moonming merged 3 commits into
mainfrom
feat/e2e-seed-tail
Jul 21, 2026
Merged

test(e2e): migrate the seed-tail tests off the Admin API onto etcd/status#792
moonming merged 3 commits into
mainfrom
feat/e2e-seed-tail

Conversation

@moonming

@moonming moonming commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What

Migrate the e2e tests that still depended on the Admin API for setup or readiness onto the direct-etcd SeedClient and the metrics/status listener, and run them with admin: false. This is step 2 of retiring the Admin API (after #791 landed the admin.enabled switch): it moves the tests that don't test the Admin API itself out of the admin-on world, shrinking the Admin API's remaining e2e footprint to only the tests whose subject is the admin surface.

9 files migrated (all now spawn admin: false):

Seed-only (4) — the Admin API was used purely to set up resources or as a removable oracle:

  • cache-ttl-eviction — already seeded via SeedClient; only needed admin: false.
  • datadog-exporterAdminClientSeedClient for the exporter + routing seed (the exporter delivery is the subject, not how it was seeded; the admin-write-path coverage lives in the held-back characterization test).
  • guardrail-disabled-bypass — the Gate-A readiness probe (GET /admin/v1/guardrails, proving the rule loaded) becomes a GET /status/config check on applied.resource_counts.guardrails, which reflects the applied snapshot on the metrics listener — stronger than reading etcd back (which would only prove the write landed), preserving the anti-vacuous intent.
  • provider-key-rotation — the revision oracle (GET /admin/v1/provider_keys/:id.revision bumped) becomes an etcd read-back asserting the key now holds the rotated secret — a more direct no-op guard than a revision bump, using the existing EtcdClient.get (no shared-harness change).

Mixed (5) — seeding was already on SeedClient; the only remaining admin-shaped call is listModelStatuses, which reads GET /status/models on the metrics listener (not the admin API) and survives admin: false: background-health, runtime-mixed-filtering, cooldown-contract (7 spawn sites), retry-on-429-vs-background-ignore, runtime-status.

Out of scope (deliberately unchanged)

Tests whose subject is the Admin API surface stay admin-on until the Admin API is removed, then get deleted or split — tracked on the removal checklist (AISIX-Cloud#1007 Phase 4): seed-vs-admin-characterization (seed≡admin equivalence), file-resource-source (file-mode write-409), status-models (/status/models vs /admin/v1/models/status equivalence), health-minimal (/admin/v1/health), apikey-lifecycle/apikey-budget (rotate/budget admin ops), auth-baseline, openai-sdk-compat (RFC 9745 Deprecation-header contract), prometheus-metrics. status-config only uses /admin/v1/health as a probe (it doesn't seed via admin) — left for the readiness-default step.

Method

Classification (seed-only / mixed / held-back / probe-only) was done by fanning out one reader per candidate file, then synthesized into this migration plan — so each file's admin usage was read and classified individually rather than pattern-matched.

Verification

npx tsc --noEmit adds no new type errors in the changed files. Each migrated file passes in isolation; full e2e suite green locally. No harness or Rust changes — the direct-etcd seed infra (SeedClient/EtcdClient) already existed, and /status/models / /status/config on the metrics listener already serve with the admin listener off (#791). The migrated tests keep their assertions; only the seed/readiness transport moved off the Admin API (two readiness/no-op guards were strengthened, noted above).

Summary by CodeRabbit

  • Tests
    • Updated end-to-end test setups to run without the admin listener where it is not required.
    • Improved readiness and configuration checks using status endpoints and direct resource seeding.
    • Updated exporter, guardrail, and provider-key rotation scenarios to validate behavior through supported runtime and storage checks.
    • Preserved coverage for health, caching, routing, retries, filtering, and runtime status behavior.

…atus

Step 2 of retiring the Admin API (after the admin.enabled switch, #791):
move the e2e tests that used the Admin API only for setup or readiness —
not to test the Admin API itself — onto the direct-etcd SeedClient and
the metrics/status listener, and run them with admin: false.

Seed-only (4): cache-ttl (admin:false only); datadog (AdminClient →
SeedClient for the exporter + routing seed); guardrail-disabled-bypass
(Gate-A readiness moves from GET /admin/v1/guardrails to GET
/status/config applied resource_counts — stronger, reads the applied
snapshot not just the etcd write); provider-key-rotation (the revision
oracle becomes an etcd read-back asserting the rotated secret — a more
direct no-op guard, no shared-harness change).

Mixed (5): background-health, runtime-mixed-filtering, cooldown-contract
(7 spawn sites), retry-on-429-vs-background-ignore, runtime-status —
seeding was already on SeedClient; the only surviving admin-shaped call
is listModelStatuses, which reads GET /status/models on the metrics
listener and survives admin:false.

Tests that exercise the Admin API surface itself (characterization,
file-mode 409, status-equivalence, health, rotate, deprecation-header,
admin-auth) stay admin-on until the Admin API is removed. No harness or
Rust changes; assertions unchanged (two readiness/no-op guards
strengthened).
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@moonming, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 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: 4126fcb5-95f6-4746-8d49-ab990af3de35

📥 Commits

Reviewing files that changed from the base of the PR and between 4195422 and eafbbbb.

📒 Files selected for processing (2)
  • tests/e2e/src/cases/guardrail-disabled-bypass-e2e.test.ts
  • tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
📝 Walkthrough

Walkthrough

Changes

The E2E tests now disable the application admin listener, use metrics endpoints for status and readiness checks, seed Datadog resources through SeedClient, and verify provider-key rotation directly in etcd.

E2E harness and listener setup

Layer / File(s) Summary
Disable admin listeners across E2E suites
tests/e2e/src/cases/background-health-e2e.test.ts, tests/e2e/src/cases/cache-ttl-eviction-e2e.test.ts, tests/e2e/src/cases/cooldown-contract-e2e.test.ts, tests/e2e/src/cases/retry-on-429-vs-background-ignore-e2e.test.ts, tests/e2e/src/cases/runtime-*.test.ts
Application startup explicitly uses admin: false; model status reads remain on the metrics listener and resources are seeded through etcd.

Datadog exporter provisioning

Layer / File(s) Summary
Seed-based exporter and routing setup
tests/e2e/src/cases/datadog-exporter-e2e.test.ts
Exporter, provider, model, API key, and routing setup use SeedClient; etcd reachability is stored and reused by the suite.

Direct readiness and rotation verification

Layer / File(s) Summary
Metrics readiness and etcd state checks
tests/e2e/src/cases/guardrail-disabled-bypass-e2e.test.ts, tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
Guardrail readiness polls /status/config, while provider-key rotation asserts the rotated secret persisted in etcd.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • api7/aisix#791: Introduces the admin listener configuration and related E2E harness behavior exercised by these changes.
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning Guardrail readiness ignores fetch HTTP errors, and provider-key rotation still skips on missing setup artifacts, hiding real test failures. Check res.ok before .json(), and make provider-key rotation skip only on !etcdReachable; throw if app/upstream/etcd/pkId are missing.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: migrating E2E seed-tail tests from Admin API setup to etcd/status endpoints.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed Touched changes are test-only E2E harness migrations; no logging, persistence, auth, or secret-redaction regressions were introduced.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/e2e-seed-tail

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@moonming

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 59 minutes.

@coderabbitai coderabbitai Bot 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: 2

🤖 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/guardrail-disabled-bypass-e2e.test.ts`:
- Around line 129-133: Update the fetch call in the config/status probe to
retain the Response, validate response.ok before invoking json(), and handle a
non-OK response appropriately before accessing cfg. Preserve the existing
configuration parsing for successful responses.

In `@tests/e2e/src/cases/provider-key-rotation-e2e.test.ts`:
- Line 120: Update the prerequisite guard in the E2E test so only !etcdReachable
uses the existing skip behavior. Add a separate validation for app, upstream,
etcd, and pkId that throws an explicit error when any setup value is missing,
ensuring beforeAll provisioning failures fail the test rather than being
skipped.
🪄 Autofix (Beta)

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: 0428f7b9-4657-4266-84f6-d951422e85a8

📥 Commits

Reviewing files that changed from the base of the PR and between 51557cc and 4195422.

📒 Files selected for processing (9)
  • tests/e2e/src/cases/background-health-e2e.test.ts
  • tests/e2e/src/cases/cache-ttl-eviction-e2e.test.ts
  • tests/e2e/src/cases/cooldown-contract-e2e.test.ts
  • tests/e2e/src/cases/datadog-exporter-e2e.test.ts
  • tests/e2e/src/cases/guardrail-disabled-bypass-e2e.test.ts
  • tests/e2e/src/cases/provider-key-rotation-e2e.test.ts
  • tests/e2e/src/cases/retry-on-429-vs-background-ignore-e2e.test.ts
  • tests/e2e/src/cases/runtime-mixed-filtering-e2e.test.ts
  • tests/e2e/src/cases/runtime-status-e2e.test.ts

Comment thread tests/e2e/src/cases/guardrail-disabled-bypass-e2e.test.ts Outdated

test("an in-place secret rotation under sustained load keeps dispatch serving and bumps revision", async (ctx) => {
if (!etcdReachable || !app || !upstream || !admin || !pkId) {
if (!etcdReachable || !app || !upstream || !etcd || !pkId) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fail the test on missing prerequisites instead of gracefully skipping.

As per learnings, graceful skips in E2E tests should exclusively apply to etcd unavailability (!etcdReachable). If variables like app, upstream, etcd, or pkId are falsy, it indicates a genuine failure during the beforeAll test setup (such as a provisioning failure). Masking these as a skip hides real regressions; they must surface as explicit test failures.

🛠️ Proposed fix to isolate etcd unavailability

Replace the condition with the following structure to ensure actual setup failures correctly fail the test block:

    if (!etcdReachable) {
      // Keep the existing skip logic here (e.g., return ctx.skip())
    }
    
    if (!app || !upstream || !etcd || !pkId) {
      throw new Error("Test prerequisites not met (check beforeAll for failures)");
    }
🤖 Prompt for 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.

In `@tests/e2e/src/cases/provider-key-rotation-e2e.test.ts` at line 120, Update
the prerequisite guard in the E2E test so only !etcdReachable uses the existing
skip behavior. Add a separate validation for app, upstream, etcd, and pkId that
throws an explicit error when any setup value is missing, ensuring beforeAll
provisioning failures fail the test rather than being skipped.

Source: Learnings

…rotation

Audit LOW: the header/scope/reference comments still described the
rotation as `PUT /admin/v1/provider_keys/:id` with a revision-bump check,
but the test now rotates via a declarative etcd update and verifies the
rotated secret by reading the key back. Comment-only.
@moonming

Copy link
Copy Markdown
Collaborator Author

Independent cold audit (3 diverse lenses — coverage-preservation / admin-off-safety / classification-boundary). All three PASS, zero HIGH/MEDIUM.

  • Coverage-preservation (traced to DP source): both "strengthened" guards are genuinely stronger, not weaker. guardrail Gate-A now reads /status/config applied.resource_counts.guardrails, which is the same published snapshot the proxy consults at dispatch (supervisor.rs resource_counts over AisixSnapshot) — strictly stronger than the old admin etcd-store read, which ran one layer ahead of the applied snapshot (could clear before the rule loaded). enabled:false guardrails still load (filter is at dispatch, not load), so the gate is satisfiable. provider-key-rotation's secret read-back catches "no write" AND "wrong content" (vs revision-bump = "some write"); the rotation itself was already a declarative seed.update, and the real DP-liveness signal (success === TOTAL_REQUESTS) is unchanged. MIXED /status/models: model list comes from the applied snapshot under admin-off, but the runtime health overlay uses the same shared tracker in both modes — assertions are .find(row)! preceded by proxy traffic guaranteeing snapshot presence, so no false-PASS race.
  • Admin-off-safety: no residual admin-API call in the 9 (only listModelStatuses → metrics listener survives); AdminClient construction safe under admin:false; all 7 cooldown spawn sites hit; imports/vars clean; etcd! assertions proven safe.
  • Classification-boundary: the 9 migrated files assert nothing about the admin surface; every held-back file genuinely tests it (401s, deprecation headers, rotate endpoint, 409 path-leak, seed≡admin equivalence); status-config seeds via etcd and only probes /admin/v1/health (correctly excluded); no admin-seeding file was missed. datadog's dropped admin observability_exporter write is still covered by the held-back characterization test — no net coverage loss.

LOWs: provider-key-rotation's stale PUT /admin/v1/... comments refreshed in d102574; guardrail Gate-A >= 1 is immaterial today (exactly one guardrail seeded) and stronger on the applied-vs-written axis (kept). Verification: npx tsc clean on the changed files; each migrated file green in isolation; full suite green (the one cooldown ECONNRESET was a transient under 148-file load — passes 2× on isolated re-run).

…l gate

CodeRabbit: fetch does not throw on HTTP error status; guard the
readiness poll with res.ok before .json() so a transient non-200 retries
cleanly instead of parsing an error body.
@moonming

Copy link
Copy Markdown
Collaborator Author

CI + CodeRabbit triage

CI e2e failure = pre-existing flake, not this PR. The failing test was status-config-e2e ("clean config reports synced with revisions and resource counts") — a file this PR does not touch, and this PR makes no change to /status/config behavior (test-only, no Rust/harness change). It waits for state === "synced" then asserts resource_counts.models === 1; the same response returned synced with resource_counts not yet populated. Passes 3/3 on isolated local re-run. Filed as #793. Re-triggered CI on eafbbbb.

CodeRabbit comments:

  1. guardrail-disabled-bypass — check res.ok before .json(): fixed in eafbbbb (the readiness poll now returns false on a non-200 instead of parsing an error body).
  2. provider-key-rotation — skip only on !etcdReachable, fail on other missing prerequisites: valid convention, but this is a pre-existing suite-wide guard pattern (if (!etcdReachable || !app || …) ctx.skip()) used across the e2e cases; this PR only swapped !admin!etcd inside it and did not change the skip semantics. Aligning it belongs in a separate suite-wide consistency pass rather than piecemeal in the migrated files (which would leave the suite inconsistent) — deferring, not silently ignoring.

@moonming
moonming merged commit 9e2d9fa into main Jul 21, 2026
9 of 10 checks passed
@moonming
moonming deleted the feat/e2e-seed-tail branch July 21, 2026 09:41
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