test(api): HTTP integration tests for pending-operations endpoints (#81) - #178
Conversation
There was a problem hiding this comment.
One correctness bug: StepUpVerify_ExceedsAuthRateLimit_ReturnsTooManyRequests exhausts _userId's auth rate-limit quota (5 req/min, partitioned per-user in DistributedRateLimitAttribute), so Execute_AfterConfirmAndStepUp_Succeeds — which calls the same verify endpoint as the same user immediately after — will receive 429 instead of 200 whenever both tests land in the same clock-minute window. Detailed on the inline comment. Fix: give the rate-limit test its own user account so the exhausted bucket is isolated.
There was a problem hiding this comment.
Solid integration test suite for the pending-operations flow. The full confirm → step-up → step-up/verify → execute path is exercised end-to-end against a real DB, all gates (cooldown, invalid code, missing step-up, rate limit) are covered, and the rate-limit case is correctly isolated to its own user to prevent bleed across tests. The CapturingEmailService seam is cleanly wired via ConfigureTestServices / RemoveAll, and the JSONB-normalization trick in NormalizeToJsonb correctly handles the key-reordering that would otherwise break fingerprint matching at execute time. No correctness, security, or contract issues found.
Add PendingOperationsControllerTests driving the full agent confirmation flow over real HTTP against Postgres: confirm -> step-up -> step-up/verify -> execute, anchored on the api_keys.manage (StepUp) capability. Adds a capturing IEmailService fake (CapturingEmailService) to the test factory via ConfigureTestServices so the step-up code (emailed only) is readable by the test. Asserts confirmation-token persistence, step-up challenge issuance + 60s service cooldown, valid/invalid code verification, the auth rate limiter on repeated verifies, execute-time step_up_required enforcement, and the full happy path creating a real API key. Refs thomasluizon/orbit-ui-mobile#81 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The auth rate-limit (5/min, partitioned by user) is shared across every step-up and verify call. StepUpVerify_ExceedsAuthRateLimit deliberately exhausts it, and as a sibling of the other pending-ops tests on the same class-wide account that bled 429s into them whenever they shared a clock-minute. Move it into a dedicated nested RateLimitTests class with its own IAsyncLifetime and its own registered account so the exhausted bucket is isolated. Shared seeding helpers become static and parameterized. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Adds
PendingOperationsControllerTestsdriving the full agent confirmation flow over real HTTP against Postgres (orbit_test):POST /api/ai/pending-operations/{id}/confirm->/step-up->/step-up/verify->/execute, anchored on theapi_keys.manage(StepUp) capability so every gate is exercised.What it covers
ConfirmedAtUtc/ConfirmationTokenHashin DB); unknown id -> 404.StepUpSatisfiedAtUtc; invalid code -> 400; repeated verifies trip theauthrate limiter (429).Status=PendingConfirmation,PolicyReason=step_up_required; full confirm+step-up path ->Succeeded(creates a real API key); unknown id -> 404.Seam added
IntegrationTestWebApplicationFactoryregisters a capturingIEmailService(CapturingEmailService) viaConfigureTestServices, exposingLastVerificationCodeso the emailed-only step-up code is readable. MirrorsTestEmailServiceinAgentStepUpServiceTests. The existing JWTUseSettingis preserved (added to, not clobbered).Notes
Jwt:SecretKeylength fix). Base ischore/fix-integration-jwt-secret, NOTmain. Merge chore(api): fix integration-test Jwt:SecretKey length (>=32 bytes) #176 first.buildis clean (full solution: 0 errors). Locally all 10 tests pass against a Postgres-backedorbit_test; CI does not runOrbit.IntegrationTests, so they need a Postgres-backed env (local/user machine) to execute.ArgumentsJsonis ajsonbcolumn, so the seed normalizes args through Postgres before computing the operation fingerprint — otherwise jsonb key-reordering breaks the execute-time fingerprint match.Refs thomasluizon/orbit-ui-mobile#81
🤖 Generated with Claude Code