Skip to content

test: align E2E coverage with current EE models#2

Open
guoqqqi wants to merge 3 commits intomasterfrom
codex/e2e-service-id-coverage
Open

test: align E2E coverage with current EE models#2
guoqqqi wants to merge 3 commits intomasterfrom
codex/e2e-service-id-coverage

Conversation

@guoqqqi
Copy link
Copy Markdown

@guoqqqi guoqqqi commented Apr 7, 2026

Summary

  • rewrite route E2E coverage to use the current service + route(service_id) model
  • align key-auth E2E with consumer + credential create and make gateway assertions propagation-aware
  • update debug trace and traffic-forwarding coverage to use the current route model

Validation

  • A7_ADMIN_URL environment variable is required for E2E tests
    FAIL github.com/api7/a7/test/e2e 0.867s
    FAIL
  • A7_ADMIN_URL environment variable is required for E2E tests
    FAIL github.com/api7/a7/test/e2e 0.609s
    FAIL

Refs #1

Summary by CodeRabbit

  • Tests
    • Improved e2e stability with polling/retry helpers for gateway/auth checks to handle propagation delays.
    • Standardized tests to create/delete dedicated services and reference routes by service ID instead of inline upstreams.
    • Added shared debug/trace helpers and YAML trimming for focused config-sync roundtrips (secrets omitted).
    • Tightened test failures and diagnostics: fail on CLI/Admin errors and include command output on failure.
    • Expanded assertions (e.g., stream route description and auth success) for stronger verification.

Copilot AI review requested due to automatic review settings April 7, 2026 09:44
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

E2E tests were refactored to create dedicated services and reference them by service_id instead of inline upstream blocks; a gateway polling helper was added for eventual consistency; shared route creation and debug-wait helpers were introduced; config-dump trimming/sanitization helpers were added; admin service-deletion now fails on errors and includes response body in failures.

Changes

Cohort / File(s) Summary
Gateway polling & consumer test
test/e2e/consumer_test.go
Added waitForGatewayStatus(...) (deadline-bound retries with 500ms backoff); TestConsumer_WithKeyAuth now creates/deletes a dedicated service via CLI (svcID), uses service_id in route JSON, sets credential "name" to credID, switches credential-create failure handling to require.NoError (includes stdout/stderr), and replaces single-shot auth assertions with two polling checks (unauthenticated expect 401/403, authenticated expect 200) that skip if polling returns 404 (propagation).
Debug trace helpers & tests
test/e2e/debug_test.go
Added createDebugTraceRoute(...) and waitForDebugTraceRoute(...); refactored multiple debug-trace tests to create/delete a dedicated service per test (createTestServiceViaCLI + cleanup), use the new helper (route payload uses name/service_id/paths with extraFields), and improve failure diagnostics to include formatted stdout/stderr.
Route tests refactor
test/e2e/route_test.go
Removed old createTestRouteViaCLI; tests now provision/delete a service first and create routes referencing service_id (removed inline upstream payloads); route creation now hard-fails with require.NoError; TestRoute_TrafficForwarding uses waitForGatewayStatus to poll for HTTP 200 and skips on 404 propagation.
Config sync helpers & trimming
test/e2e/config_sync_test.go
Added YAML-processing helpers: sanitizeDumpForSync (removes top-level secrets) and trimDumpForRoundtrip (normalizes/removes top-level sections and filters services/routes to the test’s serviceID/routeID via filterResourcesByID); TestConfigSync_FullRoundtrip now calls trimDumpForRoundtrip on the dump before config sync.
Service deletion robustness
test/e2e/service_test.go
deleteServiceViaAdmin now treats Admin API call errors (err != nil) as fatal, defers response body close, treats HTTP 404 as success, and fails for non-2xx responses including status code and body in the failure message (added io import).
Stream route test updates
test/e2e/stream_route_test.go
TestStreamRoute_CRUD now creates a dedicated service (svcID) via CLI and cleans it up; stream route creation payload changed from inline upstream to name/service_id/desc; test now asserts returned JSON contains the description string.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
E2e Test Quality Review ⚠️ Warning PR fails critical error handling in cleanup helpers: deleteStreamRouteViaAdmin, deleteConsumerViaAdmin, and deleteRouteViaAdmin swallow transport errors while deleteServiceViaAdmin demonstrates correct pattern, creating test reliability issues. Apply uniform error handling fix to all three broken helpers: add import io, check transport errors fatally, use defer resp.Body.Close(), handle HTTP 404 as success, fail on non-2xx responses with body details.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test: align E2E coverage with current EE models' directly and clearly describes the main change—refactoring E2E tests to use the current service + route model with service_id, as evidenced across all modified test files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/e2e-service-id-coverage

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

❤️ Share

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the Go E2E test suite to match the current API7 EE data model where routes reference services via service_id, and improves gateway-facing assertions to be propagation-aware.

Changes:

  • Refactors route E2E tests to create a service first and then create routes with service_id (removing inline upstream from route fixtures).
  • Reworks debug trace E2E coverage to use the current route model (paths + service_id).
  • Aligns key-auth E2E to create a consumer + credential, and polls the gateway until expected status codes appear.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
test/e2e/route_test.go Rewrites route CRUD/export/label/traffic tests to use service-backed routes and adds propagation-aware gateway polling.
test/e2e/debug_test.go Introduces a helper to create traceable routes using service_id + paths, updating all debug trace tests accordingly.
test/e2e/consumer_test.go Adds a gateway status polling helper and updates key-auth E2E to use consumer + credential creation and propagation-aware assertions.
Comments suppressed due to low confidence (1)

test/e2e/route_test.go:49

  • createTestRouteViaCLI is currently unused within this file (no call sites) and duplicates the existing shared helper createTestRouteWithServiceViaCLI in setup_test.go. Consider removing this dead code, or reusing/adjusting the shared helper to avoid divergent route fixture formats across the E2E suite.
func createTestRouteViaCLI(t *testing.T, env []string, id, serviceID string) string {
	t.Helper()
	routeJSON := fmt.Sprintf(`{
		"id": %q,
		"name": "e2e-route-%s",
		"service_id": %q,
		"paths": ["/test-%s"],
		"methods": ["GET"]
	}`, id, id, serviceID, id)

	tmpFile := filepath.Join(t.TempDir(), "route.json")
	require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))

	stdout, stderr, err := runA7WithEnv(env, "route", "create", "-f", tmpFile, "-g", gatewayGroup)
	require.NoError(t, err, "stdout=%s stderr=%s", stdout, stderr)
	return id
}

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

Comment on lines +17 to +43
func waitForGatewayStatus(url string, buildRequest func() (*http.Request, error), want func(int) bool, timeout time.Duration) (int, error) {
deadline := time.Now().Add(timeout)
lastStatus := 0
var lastErr error
for time.Now().Before(deadline) {
req, err := buildRequest()
if err != nil {
return 0, err
}
resp, err := insecureClient.Do(req)
if err != nil {
lastErr = err
time.Sleep(500 * time.Millisecond)
continue
}
lastStatus = resp.StatusCode
resp.Body.Close()
if want(resp.StatusCode) {
return resp.StatusCode, nil
}
time.Sleep(500 * time.Millisecond)
}
if lastErr != nil {
return lastStatus, lastErr
}
return lastStatus, nil
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

waitForGatewayStatus is now used across multiple test files (e.g., route and consumer tests), but it’s defined in consumer_test.go, which creates a non-obvious cross-file dependency. Consider moving it to the shared test utilities (e.g., setup_test.go) and either remove the unused url parameter or use it (callers currently pass it but it’s ignored).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/e2e/debug_test.go (1)

32-51: ⚠️ Potential issue | 🟠 Major

Add a sync check before running debug trace to prevent race conditions on route propagation.

The debug trace command at pkg/cmd/debug/trace/trace.go makes a single unretried HTTP request with a 15-second timeout and does not include retry logic. These tests create routes and immediately invoke debug trace without waiting for the route to reach the data plane first. Other live-gateway tests in this PR use waitForGatewayStatus to poll for readiness. Add the same synchronization here (either by reusing waitForGatewayStatus or a minimal check against the gateway URL) before calling debug trace to avoid transient 404s during route sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/e2e/debug_test.go` around lines 32 - 51, The test
TestDebugTrace_JSONOutput must wait for the route to be propagated before
invoking debug trace; add a synchronization call (reuse waitForGatewayStatus
used in other tests) after createDebugTraceRoute and before calling runA7WithEnv
so the data plane has the route available (use gatewayGroup and gatewayURL as
arguments to waitForGatewayStatus or the minimal gateway URL check your test
suite provides) to avoid transient 404s when runA7WithEnv executes the debug
trace.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/e2e/consumer_test.go`:
- Around line 17-42: The loop in waitForGatewayStatus can block on
insecureClient.Do because buildRequest returns a request without a bound
context; update waitForGatewayStatus to create a per-iteration context tied to
the remaining deadline (use ctx, cancel :=
context.WithDeadline(context.Background(), deadline) or
context.WithTimeout(context.Background(), time.Until(deadline))), attach it to
the request via req = req.WithContext(ctx) before calling insecureClient.Do,
defer cancel() (or call cancel after the request completes) to avoid leaks, and
ensure you still handle lastErr, resp.Body.Close(), and the
want(resp.StatusCode) check as before.

In `@test/e2e/route_test.go`:
- Around line 75-80: The cleanup currently calls deleteServiceViaAdmin(t, svcID)
but that helper silently ignores non-2xx responses elsewhere, which can leak a
fixed svcID and break subsequent runs; update the service cleanup to assert
success (or allow only 404) by changing deleteServiceViaAdmin to return an
error/status and making the t.Cleanup caller (or the helper itself) fail the
test on unexpected responses, or modify the helper to treat any non-2xx other
than 404 as a test failure so createTestServiceViaCLI can safely reuse fixed
IDs; locate and update the deleteServiceViaAdmin helper and the t.Cleanup
invocation around createTestServiceViaCLI to enforce this behavior.

---

Outside diff comments:
In `@test/e2e/debug_test.go`:
- Around line 32-51: The test TestDebugTrace_JSONOutput must wait for the route
to be propagated before invoking debug trace; add a synchronization call (reuse
waitForGatewayStatus used in other tests) after createDebugTraceRoute and before
calling runA7WithEnv so the data plane has the route available (use gatewayGroup
and gatewayURL as arguments to waitForGatewayStatus or the minimal gateway URL
check your test suite provides) to avoid transient 404s when runA7WithEnv
executes the debug trace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4060788d-c895-49eb-b178-90ef239f1d9c

📥 Commits

Reviewing files that changed from the base of the PR and between 69c5178 and 5e5b5b3.

📒 Files selected for processing (3)
  • test/e2e/consumer_test.go
  • test/e2e/debug_test.go
  • test/e2e/route_test.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/e2e/config_sync_test.go`:
- Around line 146-152: The test removes secrets via sanitizeDumpForSync but then
calls runA7WithEnv to execute the "config sync" command (in the test's call
site) without disabling deletions; update the test to call runA7WithEnv with the
sync flag that disables deletions (e.g., add "--delete=false" or equivalent) so
remote secrets not present in the sanitized dump are not removed, referencing
the sanitizeDumpForSync and runA7WithEnv call sites and the "config sync"
invocation to locate where to add the flag.

In `@test/e2e/consumer_test.go`:
- Around line 34-49: The polling helper currently leaves lastErr set when a
transient transport error occurs, making that error sticky; modify the loop so
that when a request succeeds (err == nil) you clear lastErr (set lastErr = nil)
before assigning lastStatus and closing resp.Body, ensuring subsequent
successful HTTP statuses (checked via want(resp.StatusCode)) aren't shadowed by
an earlier transport error; update the code around the resp handling (variables
lastErr, lastStatus, resp, want) to clear lastErr on success.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d56fb339-4b02-47a0-8461-ebcbe18552b5

📥 Commits

Reviewing files that changed from the base of the PR and between 5e5b5b3 and d93f1c5.

📒 Files selected for processing (5)
  • test/e2e/config_sync_test.go
  • test/e2e/consumer_test.go
  • test/e2e/debug_test.go
  • test/e2e/route_test.go
  • test/e2e/service_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/e2e/debug_test.go

Comment on lines 146 to 152
// The shared EE test environment may contain secrets with legacy IDs that
// fail current local validation rules. Remove them so the roundtrip stays
// focused on the resources this test created.
sanitizeDumpForSync(t, dumpFile)

stdout, stderr, err = runA7WithEnv(env, "config", "sync", "-f", dumpFile, "-g", gatewayGroup)
require.NoError(t, err, "stdout=%s stderr=%s", stdout, stderr)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sanitized dump + default sync can delete shared secrets.

After removing secrets (Lines 146-150), Line 151 runs config sync without --delete=false. If deletion is enabled, this can remove remote secrets that are intentionally excluded from the local file, which is risky in shared EE environments.

Suggested safe fix
- stdout, stderr, err = runA7WithEnv(env, "config", "sync", "-f", dumpFile, "-g", gatewayGroup)
+ stdout, stderr, err = runA7WithEnv(env, "config", "sync", "-f", dumpFile, "--delete=false", "-g", gatewayGroup)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// The shared EE test environment may contain secrets with legacy IDs that
// fail current local validation rules. Remove them so the roundtrip stays
// focused on the resources this test created.
sanitizeDumpForSync(t, dumpFile)
stdout, stderr, err = runA7WithEnv(env, "config", "sync", "-f", dumpFile, "-g", gatewayGroup)
require.NoError(t, err, "stdout=%s stderr=%s", stdout, stderr)
// The shared EE test environment may contain secrets with legacy IDs that
// fail current local validation rules. Remove them so the roundtrip stays
// focused on the resources this test created.
sanitizeDumpForSync(t, dumpFile)
stdout, stderr, err = runA7WithEnv(env, "config", "sync", "-f", dumpFile, "--delete=false", "-g", gatewayGroup)
require.NoError(t, err, "stdout=%s stderr=%s", stdout, stderr)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/e2e/config_sync_test.go` around lines 146 - 152, The test removes
secrets via sanitizeDumpForSync but then calls runA7WithEnv to execute the
"config sync" command (in the test's call site) without disabling deletions;
update the test to call runA7WithEnv with the sync flag that disables deletions
(e.g., add "--delete=false" or equivalent) so remote secrets not present in the
sanitized dump are not removed, referencing the sanitizeDumpForSync and
runA7WithEnv call sites and the "config sync" invocation to locate where to add
the flag.

Comment on lines +34 to +49
if err != nil {
lastErr = err
time.Sleep(500 * time.Millisecond)
continue
}
lastStatus = resp.StatusCode
resp.Body.Close()
if want(resp.StatusCode) {
return resp.StatusCode, nil
}
time.Sleep(500 * time.Millisecond)
}
if lastErr != nil {
return lastStatus, lastErr
}
return lastStatus, nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clear lastErr after a successful poll.

Line 35 makes transient transport failures sticky for the rest of the wait. If the gateway briefly refuses connections and then only returns 404 until timeout, this helper returns that old error, so the callers fail before reaching their status == 404 skip path.

🛠 Suggested fix
 		resp, err := insecureClient.Do(req)
 		cancel()
 		if err != nil {
 			lastErr = err
 			time.Sleep(500 * time.Millisecond)
 			continue
 		}
+		lastErr = nil
 		lastStatus = resp.StatusCode
 		resp.Body.Close()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err != nil {
lastErr = err
time.Sleep(500 * time.Millisecond)
continue
}
lastStatus = resp.StatusCode
resp.Body.Close()
if want(resp.StatusCode) {
return resp.StatusCode, nil
}
time.Sleep(500 * time.Millisecond)
}
if lastErr != nil {
return lastStatus, lastErr
}
return lastStatus, nil
if err != nil {
lastErr = err
time.Sleep(500 * time.Millisecond)
continue
}
lastErr = nil
lastStatus = resp.StatusCode
resp.Body.Close()
if want(resp.StatusCode) {
return resp.StatusCode, nil
}
time.Sleep(500 * time.Millisecond)
}
if lastErr != nil {
return lastStatus, lastErr
}
return lastStatus, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/e2e/consumer_test.go` around lines 34 - 49, The polling helper currently
leaves lastErr set when a transient transport error occurs, making that error
sticky; modify the loop so that when a request succeeds (err == nil) you clear
lastErr (set lastErr = nil) before assigning lastStatus and closing resp.Body,
ensuring subsequent successful HTTP statuses (checked via want(resp.StatusCode))
aren't shadowed by an earlier transport error; update the code around the resp
handling (variables lastErr, lastStatus, resp, want) to clear lastErr on
success.

Copilot AI review requested due to automatic review settings April 7, 2026 15:00
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/e2e/stream_route_test.go (1)

47-48: Consider unique test IDs to reduce cross-run collisions.

Lines 47-48 use fixed IDs. If a prior run leaves stale resources (or tests run concurrently), this can cause nondeterministic create failures.

Optional hardening
+import "time"
+
-	svcID := "e2e-stream-route-svc"
-	srID := "e2e-stream-route-crud"
+	suffix := fmt.Sprintf("%d", time.Now().UnixNano())
+	svcID := "e2e-stream-route-svc-" + suffix
+	srID := "e2e-stream-route-crud-" + suffix
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/e2e/stream_route_test.go` around lines 47 - 48, Replace the fixed test
IDs svcID and srID in test/e2e/stream_route_test.go with uniquely generated
values to avoid collisions (e.g., append a timestamp, test name, or random/UUID
suffix); update all uses of svcID and srID in the test to reference the new
unique values (for example derive a suffix via time.Now().UnixNano() or
uuid.NewString() or t.Name()) so each test run gets distinct resource names and
prevents nondeterministic create failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/e2e/stream_route_test.go`:
- Around line 49-52: The cleanup currently hides failures from
deleteStreamRouteViaAdmin; change the helper to return an error instead of
swallowing transport/status errors (update deleteStreamRouteViaAdmin signature
to return error and propagate underlying HTTP/transport error details), then
modify the t.Cleanup closure in the test to call the new
deleteStreamRouteViaAdmin(t, srID) and check/handle its returned error (e.g.,
call t.Fatalf or t.Errorf with the error details) before calling
deleteServiceViaAdmin(t, svcID) so route-delete failures are surfaced and not
masked by service-delete errors.

---

Nitpick comments:
In `@test/e2e/stream_route_test.go`:
- Around line 47-48: Replace the fixed test IDs svcID and srID in
test/e2e/stream_route_test.go with uniquely generated values to avoid collisions
(e.g., append a timestamp, test name, or random/UUID suffix); update all uses of
svcID and srID in the test to reference the new unique values (for example
derive a suffix via time.Now().UnixNano() or uuid.NewString() or t.Name()) so
each test run gets distinct resource names and prevents nondeterministic create
failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2f3f852a-75af-43e7-b2ba-ee1e86b9ddc3

📥 Commits

Reviewing files that changed from the base of the PR and between d93f1c5 and 05ba3f8.

📒 Files selected for processing (2)
  • test/e2e/config_sync_test.go
  • test/e2e/stream_route_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/e2e/config_sync_test.go

Comment on lines +49 to +52
t.Cleanup(func() {
deleteStreamRouteViaAdmin(t, srID)
deleteServiceViaAdmin(t, svcID)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Cleanup currently masks stream-route deletion failures.

Line 50 calls deleteStreamRouteViaAdmin, but that helper swallows transport/status errors. With the new service dependency, hidden route-delete failures make cleanup diagnosis harder and can surface as misleading service-delete failures.

Suggested fix (tighten helper failure handling)
+import "io"
+
 func deleteStreamRouteViaAdmin(t *testing.T, id string) {
 	t.Helper()
 	resp, err := runtimeAdminAPI("DELETE", fmt.Sprintf("/apisix/admin/stream_routes/%s", id), nil)
-	if err == nil {
-		resp.Body.Close()
-	}
+	if err != nil {
+		t.Fatalf("delete stream route %s via admin API failed: %v", id, err)
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode == 404 {
+		return
+	}
+	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+		body, _ := io.ReadAll(resp.Body)
+		t.Fatalf("delete stream route %s via admin API returned %d: %s", id, resp.StatusCode, string(body))
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/e2e/stream_route_test.go` around lines 49 - 52, The cleanup currently
hides failures from deleteStreamRouteViaAdmin; change the helper to return an
error instead of swallowing transport/status errors (update
deleteStreamRouteViaAdmin signature to return error and propagate underlying
HTTP/transport error details), then modify the t.Cleanup closure in the test to
call the new deleteStreamRouteViaAdmin(t, srID) and check/handle its returned
error (e.g., call t.Fatalf or t.Errorf with the error details) before calling
deleteServiceViaAdmin(t, svcID) so route-delete failures are surfaced and not
masked by service-delete errors.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.


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

Comment on lines 23 to 36
func deleteServiceViaAdmin(t *testing.T, id string) {
t.Helper()
resp, err := runtimeAdminAPI("DELETE", fmt.Sprintf("/apisix/admin/services/%s", id), nil)
if err == nil {
resp.Body.Close()
if err != nil {
t.Fatalf("delete service %s via admin API failed: %v", id, err)
}
defer resp.Body.Close()
if resp.StatusCode == 404 {
return
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(resp.Body)
t.Fatalf("delete service %s via admin API returned %d: %s", id, resp.StatusCode, string(body))
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

deleteServiceViaAdmin now calls t.Fatalf on Admin API errors / non-2xx responses. Since this function is primarily used from t.Cleanup, a transient Admin API issue (or an already-deleted resource) will fail the whole test run during cleanup. This is inconsistent with other delete*ViaAdmin helpers in this package, which treat cleanup as best-effort. Consider returning silently on errors (or using t.Logf) and only enforcing strict status checks in the main test flow, not in cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +23
// waitForGatewayStatus polls the gateway until the desired status is observed
// or the timeout expires. Each request is bound to the remaining deadline so a
// stalled HTTP call cannot outlive the caller-provided timeout.
func waitForGatewayStatus(url string, buildRequest func() (*http.Request, error), want func(int) bool, timeout time.Duration) (int, error) {
deadline := time.Now().Add(timeout)
lastStatus := 0
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

waitForGatewayStatus takes a url parameter but never uses it (all requests come from buildRequest). This is misleading for callers and makes it easier to pass inconsistent values. Consider removing the url parameter, or using it inside the helper (e.g., as the default request URL and/or in a timeout error message).

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +50
// waitForGatewayStatus polls the gateway until the desired status is observed
// or the timeout expires. Each request is bound to the remaining deadline so a
// stalled HTTP call cannot outlive the caller-provided timeout.
func waitForGatewayStatus(url string, buildRequest func() (*http.Request, error), want func(int) bool, timeout time.Duration) (int, error) {
deadline := time.Now().Add(timeout)
lastStatus := 0
var lastErr error
for time.Now().Before(deadline) {
req, err := buildRequest()
if err != nil {
return 0, err
}
ctx, cancel := context.WithDeadline(context.Background(), deadline)
req = req.WithContext(ctx)
resp, err := insecureClient.Do(req)
cancel()
if err != nil {
lastErr = err
time.Sleep(500 * time.Millisecond)
continue
}
lastStatus = resp.StatusCode
resp.Body.Close()
if want(resp.StatusCode) {
return resp.StatusCode, nil
}
time.Sleep(500 * time.Millisecond)
}
if lastErr != nil {
return lastStatus, lastErr
}
return lastStatus, nil
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

This polling helper is defined in consumer_test.go but is used by other test files (e.g. route/debug tests). That cross-file dependency is easy to miss and can cause accidental breakage when refactoring tests. Consider moving waitForGatewayStatus to a shared test helper file (e.g. setup_test.go or a new helpers_test.go) alongside other shared E2E utilities.

Copilot uses AI. Check for mistakes.
Comment on lines 150 to +177
username := "e2e-consumer-keyauth"
svcID := "e2e-service-keyauth"
routeID := "e2e-route-keyauth"
credID := "e2e-cred-keyauth"
t.Cleanup(func() {
deleteRouteViaAdmin(t, routeID)
deleteServiceViaAdmin(t, svcID)
deleteConsumerViaAdmin(t, username)
})

// Create consumer (no auth plugins — API7 EE requires credentials).
createTestConsumerViaCLI(t, env, username)
createTestServiceViaCLI(t, env, svcID)

// Create credential with key-auth plugin.
credJSON := fmt.Sprintf(`{
"name": %q,
"plugins": {
"key-auth": {
"key": "e2e-key-%s"
}
}
}`, username)
}`, credID, username)
credFile := filepath.Join(t.TempDir(), "credential.json")
require.NoError(t, os.WriteFile(credFile, []byte(credJSON), 0644))
_, stderr, err := runA7WithEnv(env, "credential", "create", credID,
stdout, stderr, err := runA7WithEnv(env, "credential", "create", credID,
"--consumer", username, "-f", credFile, "-g", gatewayGroup)
if err != nil {
t.Skipf("credential create failed: %s", stderr)
}
require.NoError(t, err, "stdout=%s stderr=%s", stdout, stderr)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

TestConsumer_WithKeyAuth creates a credential (credential create) but cleanup only deletes the route/service/consumer. If credentials are not automatically deleted when the consumer is deleted, this will leak test data in shared EE environments. Consider adding explicit credential cleanup (via a7 credential delete ... --consumer <username> --force) in the t.Cleanup block.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +29
func sanitizeDumpForSync(t *testing.T, file string) {
t.Helper()
data, err := os.ReadFile(file)
require.NoError(t, err)

var cfg map[string]interface{}
require.NoError(t, yaml.Unmarshal(data, &cfg))
delete(cfg, "secrets")

updated, err := yaml.Marshal(cfg)
require.NoError(t, err)
require.NoError(t, os.WriteFile(file, updated, 0644))
}

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

sanitizeDumpForSync is introduced but not used anywhere in this test file. If it’s no longer needed, consider removing it to avoid dead code; if it is intended for the roundtrip sync flow, consider calling it (or folding it into trimDumpForRoundtrip).

Suggested change
func sanitizeDumpForSync(t *testing.T, file string) {
t.Helper()
data, err := os.ReadFile(file)
require.NoError(t, err)
var cfg map[string]interface{}
require.NoError(t, yaml.Unmarshal(data, &cfg))
delete(cfg, "secrets")
updated, err := yaml.Marshal(cfg)
require.NoError(t, err)
require.NoError(t, os.WriteFile(file, updated, 0644))
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants