test(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} 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(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

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

test(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

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

test(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } 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(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

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

test(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

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

test(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

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

test(admin): real-etcd integration tests for every admin handler - #99

Merged
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests
May 7, 2026
Merged

test(admin): real-etcd integration tests for every admin handler#99
moonming merged 2 commits into
mainfrom
feat/admin-etcd-integration-tests

Conversation

@moonming

@moonmingmoonming commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only check that the trait contract compiles. They don't catch:

  • Subkey constant drift between EtcdConfigStore::*_SUBKEY and the match arms in aisix-etcd::loader::build_snapshot — a regression on either side silently breaks the watch path.
  • JSON shape drift between the Admin write and the loader's serde parse — a field rename on one side gets logged + skipped in production rather than failing loudly.
  • gRPC + revision plumbing in EtcdConfigStore itself.

This PR adds crates/aisix-admin/tests/etcd_integration.rs driving the full HTTP → AdminStateEtcdConfigStore → real etcd path for all six resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies, ObservabilityExporters), plus a load-bearing seventh test that:

  1. Seeds one of every kind through the Admin HTTP layer
  2. Reads the raw KV bytes back via etcd_client
  3. Runs them through aisix_etcd::loader::build_snapshot
  4. Asserts every snapshot table is populated and BuildStats reports schema_rejected=0, parse_rejected=0, unknown_kind=0

That last assertion is the one the in-memory tests couldn't make — it pins the byte-level compatibility between the admin write path and the watch read path.

Skip semantics

  • Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the CACHE_TEST_REDIS_URL pattern in crates/aisix-cache/tests/redis_integration.rs). Local cargo test without docker still passes.
  • CI: ci.yml's rust-unit job gains a bitnami/etcd:3.5 service alongside the existing redis service and sets ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs on every PR.

Per-test prefix is randomised (/aisix-admin-it/<nanos>-<thread>) so concurrent tests don't collide on the etcd service.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --tests -- -D warnings clean
  • cargo check -p aisix-admin --tests clean (Docker Desktop isn't running on my dev box; deferred the live exec to CI which has docker-as-a-service).
  • CI's rust unit + coverage should pick up the etcd service and run all 7 tests against it. This PR's main verification happens in CI.

Summary by CodeRabbit

  • New Features

    • Admin service now supports etcd as an additional configuration backend with full CRUD for managed resources.
  • Tests

    • Added end-to-end integration tests exercising etcd-backed CRUD across multiple resource types and verifying loader compatibility.
  • Chores

    • CI updated to provision an etcd service for running the integration tests.

The in-memory ConfigStore tests in aisix-admin/src/lib.rs only
exercise compile-time correctness of the EtcdConfigStore trait
contract. They don't catch:
- subkey constant drift between EtcdConfigStore::*_SUBKEY and the
match arms in aisix-etcd::loader (a regression in either side
silently breaks the watch path)
- JSON shape drift between the Admin write and the loader's serde
parse (a field rename that misses one side gets logged + skipped
in production rather than failing loudly)
- gRPC + revision plumbing in EtcdConfigStore itself
This commit adds a new integration-tests file
crates/aisix-admin/tests/etcd_integration.rs that drives the full
HTTP → AdminState → EtcdConfigStore → real etcd path for all six
resources (Models, ApiKeys, ProviderKeys, Guardrails, CachePolicies,
ObservabilityExporters), then a load-bearing seventh test that:
1. Seeds one of every kind through the Admin HTTP layer
2. Reads the raw KV bytes back via etcd_client
3. Runs them through aisix_etcd::loader::build_snapshot
4. Asserts every snapshot table is populated and BuildStats
reports zero rejections
That last assertion is the one the previous in-memory tests couldn't
make — it pins the byte-level compatibility between the admin write
path and the watch read path.
Skip semantics
- Tests no-op when ADMIN_TEST_ETCD_URL is unset (mirrors the
CACHE_TEST_REDIS_URL pattern in
crates/aisix-cache/tests/redis_integration.rs). Local `cargo
test` without docker still passes.
- CI: ci.yml's rust-unit job grows a `bitnami/etcd:3.5` service
alongside the existing redis service and sets
ADMIN_TEST_ETCD_URL so the etcd-backed path actually runs in
every PR.
Per-test prefix is randomised (`/aisix-admin-it/<nanos>-<thread>`)
so concurrent tests don't collide. The prefix is left in etcd at
test exit — the container is ephemeral so cleanup isn't worth the
additional teardown noise.
Verified
- `cargo fmt --all --check` clean
- `cargo clippy --workspace --tests -- -D warnings` clean
- `cargo check -p aisix-admin --tests` clean (compile-only here;
the real run happens in CI against the etcd service. Local docker
isn't running on this machine so I deferred the live exec to CI.)
@coderabbitai

coderabbitaiBot commented May 7, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d03eeb1d-f414-4c17-b09e-51dbf76b65a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2ae8c and 3c54027.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

Adds CI provisioning for a real etcd instance and a gated Tokio-based integration test suite that exercises Admin HTTP CRUD for multiple resource types and validates loader snapshot compatibility by reading raw etcd KV entries.

Changes

Etcd Integration Testing

Layer / File(s)Summary
CI Infrastructure
.github/workflows/ci.yml
rust-unit job now provisions an etcd service container exposing port 2379 with ETCD_LISTEN_CLIENT_URLS / ETCD_ADVERTISE_CLIENT_URLS set to http://0.0.0.0:2379, and adds ADMIN_TEST_ETCD_URL=http://127.0.0.1:2379 to the job env.
Test Harness & Helpers
crates/aisix-admin/tests/etcd_integration.rs
Adds environment-gated etcd URL resolution (tests no-op when unset), per-test unique etcd key prefix generation, construction of AdminState using EtcdConfigStore, authenticated HTTP request builders, and JSON response parsing helpers.
Resource CRUD Tests
crates/aisix-admin/tests/etcd_integration.rs
Implements POST → list (GET) → DELETE → verify-empty round-trip integration tests for models, API keys, provider keys, guardrails, cache policies, and observability exporters that persist via real etcd.
Loader Compatibility Test
crates/aisix-admin/tests/etcd_integration.rs
Seeds one entry per resource type through the Admin HTTP API, reads raw KV entries directly from etcd, runs aisix_etcd::build_snapshot, and asserts zero schema/parse rejections, zero unknown kinds, exactly 6 accepted entries, and one row per resource table in the produced snapshot.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant AdminHTTP
participant AdminState
participant Etcd
participant TestHarness
participant Loader
rect rgba(200,200,255,0.5)
Client->>AdminHTTP: POST resource (auth)
AdminHTTP->>AdminState: persist via EtcdConfigStore
AdminState->>Etcd: PUT KV (prefix/resource)
Etcd-->>AdminState: OK
end
rect rgba(200,255,200,0.5)
Client->>AdminHTTP: GET list
AdminHTTP->>AdminState: range keys
AdminState->>Etcd: RANGE KV
Etcd-->>AdminState: KV list
AdminState-->>AdminHTTP: resources
AdminHTTP-->>Client: JSON list
end
rect rgba(255,200,200,0.5)
TestHarness->>Etcd: direct RANGE raw prefix
TestHarness->>Loader: build_snapshot(raw KV)
Loader-->>TestHarness: Snapshot (tables & counts)
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Bitnami retired the bitnami/etcd:3.5 floating tag from Docker Hub
sometime in 2025; the GHA runner's anonymous docker pull now fails
with 'manifest unknown' against that image. Switch to the upstream
Quay-hosted image — same set of ETCD_* env vars works there.
CopilotAI review requested due to automatic review settings May 7, 2026 04:52

CopilotAI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds real-etcd integration tests for the Admin service to validate end-to-end CRUD and loader compatibility, and wires an etcd service into CI so these tests run continuously.

Changes:

  • Added crates/aisix-admin/tests/etcd_integration.rs covering CRUD round-trips for six admin resources plus a loader compatibility “all tables populated” assertion.
  • Updated CI workflow to run an etcd service and set ADMIN_TEST_ETCD_URL so the integration tests execute in CI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

FileDescription
crates/aisix-admin/tests/etcd_integration.rsAdds real etcd-backed integration tests validating Admin → EtcdConfigStore → etcd → loader behavior.
.github/workflows/ci.ymlProvisions an etcd service in CI and exports ADMIN_TEST_ETCD_URL to enable the new integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +102
/// and assert the round-trip lands real entries in etcd. Returns `true`
/// on success so the per-resource macro can assert.

// LIST again — empty
let app = build_router(state);
let resp = app.oneshot(auth_get(list_uri)).await.unwrap();
ports: ["2379:2379"]
env:
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
Comment on lines +53 to +57
async fn build_state_with_real_etcd(url: &str, prefix: &str) -> AdminState {
let client = etcd_client::Client::connect([url], None)
.await
.expect("etcd connect");
let store: Arc<dyn ConfigStore> = Arc::new(EtcdConfigStore::new(client, prefix));
@moonming
moonming merged commit a2d2378 into mainMay 7, 2026
7 checks passed
@moonming
moonming deleted the feat/admin-etcd-integration-tests branch May 7, 2026 05:03
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.

2 participants

@moonming