Repository navigation
test: E2E tests for Admin API and Responses API - #2174
pranavraja99 wants to merge 1 commit into
Conversation
- test_admin_api.py: user CRUD, suspend/activate, secret lifecycle, delete - test_responses_api.py: non-streaming, streaming, multi-turn, GET by ID, context injection, error cases (no auth, empty input) - SECRETS_MASTER_KEY added to ironclaw_server fixture Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds new E2E scenario coverage for the Admin API and OpenAI-compatible Responses API, and updates the E2E server fixture environment to support secrets provisioning.
Changes:
- Add Admin API E2E scenarios for user CRUD, suspend/activate, and secrets lifecycle.
- Add Responses API E2E scenarios for non-streaming, multi-turn, retrieval, streaming (SDK + raw SSE), and error cases.
- Update E2E
ironclaw_serverfixture env (addsSECRETS_MASTER_KEY) and remove some fixtures/waits fromconftest.py.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
tests/e2e/scenarios/test_admin_api.py |
New E2E coverage for admin user/secrets endpoints. |
tests/e2e/scenarios/test_responses_api.py |
New E2E coverage for /v1/responses including streaming and error cases. |
tests/e2e/conftest.py |
Adjusts server env for secrets; removes SSE wait and removes Slack E2E fixtures. |
Comments suppressed due to low confidence (1)
tests/e2e/conftest.py:1145
- The Slack E2E fixtures (
fake_slack_server/slack_e2e_server) were removed fromconftest.py, but there are still scenarios that depend on them (e.g.tests/e2e/scenarios/test_slack_e2e.pyreferencesslack_e2e_server). This will cause the Slack E2E tests to fail at collection time. Either restore these fixtures or remove/update the dependent Slack scenarios in the same PR.
# ── Telegram E2E fixtures ────────────────────────────────────────────────
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await pg.goto(f"{ironclaw_server}/?token={AUTH_TOKEN}") | ||
| # Wait for the app to initialize (auth screen hidden, SSE connected) | ||
| await pg.wait_for_selector("#auth-screen", state="hidden", timeout=15000) | ||
| # Wait for SSE connection (onopen sets sseHasConnectedBefore = true) | ||
| await pg.wait_for_function( | ||
| "() => typeof sseHasConnectedBefore !== 'undefined' && sseHasConnectedBefore === true", | ||
| timeout=10000, | ||
| ) | ||
| yield pg |
There was a problem hiding this comment.
The comment says "Wait for the app to initialize (auth screen hidden, SSE connected)" but the fixture no longer waits for SSE to connect (the sseHasConnectedBefore wait was removed). Either reintroduce an explicit SSE-ready wait here, or update the comment to match what the fixture actually guarantees to reduce flakiness/confusion in tests that rely on page being fully connected.
| import httpx | ||
| import pytest | ||
| from openai import OpenAI | ||
|
|
||
| from helpers import AUTH_TOKEN |
There was a problem hiding this comment.
These tests import OpenAI from the openai Python package, but tests/e2e/pyproject.toml doesn't list openai as a dependency (it currently includes pytest/httpx/playwright/etc.). Running the stated test plan in a fresh tests/e2e virtualenv will fail with ModuleNotFoundError. Add openai to the E2E project dependencies (or switch to direct httpx calls only) so the suite is self-contained.
| @pytest.fixture() | ||
| async def openai_client(responses_user): | ||
| """OpenAI client pointed at the test IronClaw instance.""" | ||
| base_url, user_token = responses_user | ||
| return OpenAI(api_key=user_token, base_url=f"{base_url}/v1") | ||
|
|
There was a problem hiding this comment.
openai_client is used from async def tests, but it returns the synchronous OpenAI client. Its network calls (including streaming iteration) will block the pytest-asyncio event loop, which can lead to slow/flaky behavior when other async fixtures/tasks are running. Prefer AsyncOpenAI (or run sync calls in a thread via anyio.to_thread.run_sync) so the tests remain non-blocking.
| json={ | ||
| "input": "Go ahead with the transfer", | ||
| "x_context": { | ||
| "notification_response": { | ||
| "notification_id": "msg_456", | ||
| "action": "approved", | ||
| "original_signal": "convert_now", | ||
| "score": 72, | ||
| } | ||
| }, | ||
| "stream": False, | ||
| }, |
There was a problem hiding this comment.
The tests labeled as "Context injection" send an x_context field, but the server's POST /v1/responses request struct (src/channels/web/responses_api.rs:48+) doesn't define or forward x_context into the agent loop (unknown fields are ignored by serde). As written, these tests will pass without actually validating context injection behavior. Use the real field name the API supports (if any) and/or add assertions that demonstrate the injected context changed the response, or update the PR description if context injection isn't actually covered here.
| r = await admin_client.post("/api/admin/users", json={ | ||
| "display_name": "Delete Me", | ||
| "email": email, | ||
| "role": "member", | ||
| }) | ||
| uid = r.json()["id"] | ||
|
|
There was a problem hiding this comment.
test_delete_user_and_verify_gone reads r.json()["id"] without first asserting the create-user request succeeded. If the POST fails (non-200 or non-JSON), this will raise and hide the real failure. Add a status_code (and ideally schema) assertion before accessing the JSON body, consistent with the other tests in this file.
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive E2E integration tests for the Admin and Responses APIs, covering user CRUD operations, secret management, and OpenAI-compatible streaming responses. It also refactors the test configuration by removing unused Slack fixtures and adding a master key for secrets encryption. Feedback was provided regarding the use of predictable hardcoded keys, potential flakiness from removing SSE connection waits, and the recommendation to use pytest fixtures for user lifecycle management in the new test files to improve isolation and cleanup.
| email = f"test-{uuid.uuid4().hex[:8]}@example.com" | ||
| r = await admin_client.post("/api/admin/users", json={ | ||
| "display_name": "Create Test", | ||
| "email": email, | ||
| "role": "member", | ||
| }) | ||
| assert r.status_code == 200 | ||
| data = r.json() | ||
| assert "id" in data | ||
| assert "token" in data | ||
| assert data["status"] == "active" | ||
| assert data["role"] == "member" | ||
| # Cleanup | ||
| await admin_client.delete(f"/api/admin/users/{data['id']}") |
There was a problem hiding this comment.
To ensure test isolation and proper cleanup, use a pytest fixture to manage the creation and deletion of the test user instead of manual try...finally blocks. This eliminates setup code duplication and ensures resources are cleaned up even if assertions fail.
References
- In pytest, use fixtures to achieve test isolation and eliminate setup code duplication, rather than relying on sequential test execution and shared class state.
| email = f"test-{uuid.uuid4().hex[:8]}@example.com" | ||
| r = await admin_client.post("/api/admin/users", json={ | ||
| "display_name": "Delete Me", | ||
| "email": email, | ||
| "role": "member", | ||
| }) | ||
| uid = r.json()["id"] | ||
|
|
||
| r = await admin_client.delete(f"/api/admin/users/{uid}") | ||
| assert r.status_code == 200 | ||
| assert r.json()["deleted"] is True | ||
|
|
||
| r = await admin_client.get(f"/api/admin/users/{uid}") | ||
| assert r.status_code == 404 |
There was a problem hiding this comment.
Similar to other user creation tests, use a pytest fixture to handle resource lifecycle. This ensures the test user is cleaned up regardless of the test outcome, following the repository's preference for fixtures to achieve test isolation.
References
- In pytest, use fixtures to achieve test isolation and eliminate setup code duplication, rather than relying on sequential test execution and shared class state.
| "HEARTBEAT_ENABLED": "false", | ||
| "EMBEDDING_ENABLED": "false", | ||
| # Secrets encryption (needed for admin secret provisioning) | ||
| "SECRETS_MASTER_KEY": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", |
There was a problem hiding this comment.
| @@ -1113,11 +1114,6 @@ async def page(ironclaw_server, browser): | |||
| await pg.goto(f"{ironclaw_server}/?token={AUTH_TOKEN}") | |||
| # Wait for the app to initialize (auth screen hidden, SSE connected) | |||
| await pg.wait_for_selector("#auth-screen", state="hidden", timeout=15000) | |||
There was a problem hiding this comment.
Removing the explicit wait for the SSE connection could introduce flakiness. Instead of a fixed timeout, prefer using page.wait_for_function to poll for a specific condition that indicates the SSE connection is established. This ensures the test waits only as long as necessary for the expected state change.
await page.wait_for_function("window.sseReady === true")References
- In tests, prefer page.wait_for_function to poll for a specific condition instead of using a fixed wait_for_timeout.
* test(e2e): add Admin and Responses API scenarios Salvaged from #2174 commit 663a388. The original PR also edited tests/e2e/conftest.py, including removal of Slack E2E fixtures and SSE wait logic. Those stale fixture edits are intentionally omitted because current main still has Slack E2E tests and current conftest already configures SECRETS_MASTER_KEY for the shared server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(e2e): avoid optional OpenAI SDK dependency --------- Co-authored-by: Pranav Raja <pranavraja99@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Salvaged via #3267 onto current main. Your original work is credited in the salvage PR body, including the original commit SHA and co-author trailer. Stale |
…earai#3267) * test(e2e): add Admin and Responses API scenarios Salvaged from nearai#2174 commit 663a388. The original PR also edited tests/e2e/conftest.py, including removal of Slack E2E fixtures and SSE wait logic. Those stale fixture edits are intentionally omitted because current main still has Slack E2E tests and current conftest already configures SECRETS_MASTER_KEY for the shared server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(e2e): avoid optional OpenAI SDK dependency --------- Co-authored-by: Pranav Raja <pranavraja99@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Adds pytest E2E scenarios for:
Also adds
SECRETS_MASTER_KEYto theironclaw_serverfixture so admin secret provisioning tests work.Test plan
🤖 Generated with Claude Code