Skip to content

test(identity): full status-code coverage for the identity contract e2e suite - #2005

Merged
ktursunov merged 4 commits into
mainfrom
test/identity-e2e-status-code-gaps
Jul 29, 2026
Merged

test(identity): full status-code coverage for the identity contract e2e suite#2005
ktursunov merged 4 commits into
mainfrom
test/identity-e2e-status-code-gaps

Conversation

@mozhaev-dev

@mozhaev-dev mozhaev-dev commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What

Closes the per-status-code gaps of the identity contract suite (#1753) and makes status-code coverage blocking in CI.

New e2e module — identity/test_error_contracts.py (40 tests)

  • Validation 400s on every mutating endpoint (empty/too-long role name, nil ids, reason > 500 on CREATE, unsupported seed mode).
  • 404s for unknown ids on every DELETE and GET /v1/persons-seed/{id}.
  • 401/403 gate proven on every route individually (previously only on a sibling per group).
  • Malformed-uuid / query-param 400s (DELETE .../not-a-uuid, ?limit=abc, ?person=not-a-uuid, ?active=maybe) — probed identical on both implementations.
  • Nil-tenant JWT → 400 tenant_unresolved (was untested on any route).
  • Rust-only strict validation behind a new explicit capability supports_strict_input_validation (same pattern as the existing capabilities, never probed from runtime): revoke-reason cap in DELETE bodies, present-but-nil viewed_person_id refusal (.NET creates the grant!), malformed subchart person_id → 400 (.NET binder answers 404). Skipped before any request on dotnet.

Coverage gate is now blocking per status code

The committed identity spec declares only 200 per route, so the per-code advisory was vacuous. REQUIRED_EXTRA is extended from the 7 mutation success codes to the full observed error-code matrix, with per-suite deltas: dotnet (+422 family, legacy lookup 404) vs rust (+409 family) via the new IDENTITY_RUST_REQUIRED_EXTRA. A disappearing error test or a handler regressing to a different code now fails identity(-rust)-endpoint-coverage-gate instead of dimming an advisory. Self-cleaning once the spec declares real codes.

The persons-seed 503 (queue full)

Not deterministically inducible black-box: the channel capacity is a compile-time constant (gear.rs, 100) and the refusal needs the channel full at the instant of the POST. Instead the refusal path is extracted into try_enqueue_job (behavior unchanged) and pinned by Rust unit tests (closed + full channel → 503). sea-orm MockDatabase was evaluated and rejected: enabling its mock feature breaks cf-gears-toolkit-db (mock DatabaseConnection is not Clone).

Verification

check result
E2E_IDENTITY_IMPLEMENTATION=rust ./e2e.sh test identity/ 110 passed, 2 skipped
E2E_IDENTITY_IMPLEMENTATION=dotnet ./e2e.sh test identity/ 107 passed, 5 skipped (4 = Rust-only capability, 1 = known Octonica)
./e2e.sh gates identity (rust) ✅ PASS, 69/70 coverable codes (the miss is the legacy endpoint's 200 — approved removal on Rust)
./e2e.sh gates identity (dotnet) ✅ PASS, 71/71
cargo test -p identity-resolution 61 passed
cargo clippy --all-targets / cargo fmt clean

Part of epic #1602 / contract-test issue #1753.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved seed-operation reliability by returning a clear 503 Service Unavailable when the seed queue can’t accept new work, avoiding orphaned operations.
    • Standardized identity API error responses across invalid input, unknown IDs, authentication/authorization, and insufficient permissions.
    • Ensured nil-tenant JWT handling returns 400 tenant_unresolved, and malformed request parameters produce consistent 400 errors.
  • Tests

    • Expanded identity end-to-end error/authorization contract coverage, including strict-input-validation behavior for the Rust implementation.
    • Updated the API coverage gate to require a broader set of expected status codes per implementation.

@mozhaev-dev
mozhaev-dev requested a review from a team as a code owner July 29, 2026 03:21
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Regenerate the connectors-ddl snapshot

This PR changes src/ingestion/**. If your change affects any
bronze / silver / gold schema, regenerate the committed DDL snapshot
and include it in this PR.

Prerequisites (details: src/ingestion/scripts/bootstrap-db/README.md):

  • docker + a fresh throwaway ClickHouse 25.7.5 (README "Local ClickHouse for testing")
  • .env from .env.bootstrap.example pointing at it; use the host LAN IP,
    reachable from both the host and connector containers
    (host.docker.internal does not resolve on the macOS host itself)
  • python3.12 or python3.11 on PATH (pinned dbt venv)
  • HubSpot + Salesforce credentials in .env — their discover calls the
    live APIs; without them, apply ../connectors-ddl/{hubspot,salesforce}.sql
    (relative to bootstrap-db/) to seed their bronze, then run the dbt step
cd src/ingestion/scripts/bootstrap-db
set -a; source pins.env; source .env; set +a
./bootstrap-db.sh connectors-config.yaml   # fresh ClickHouse 25.7.5
./dump-ddl.sh                              # writes scripts/connectors-ddl/*.sql

Commit the resulting scripts/connectors-ddl/*.sql diff. If nothing
changed, no snapshot update is needed. (Regeneration is manual for now.)

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a9c9ee64-7c87-4b85-9337-f765fe48a184

📥 Commits

Reviewing files that changed from the base of the PR and between 12776f0e52137e77644e3606f7f6014c93a90b36 and 4c321af.

📒 Files selected for processing (4)
  • src/backend/services/identity-resolution/src/api/seed.rs
  • src/ingestion/tests/e2e/identity/test_error_contracts.py
  • src/ingestion/tests/e2e/lib/api_coverage.py
  • src/ingestion/tests/e2e/lib/identity.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/ingestion/tests/e2e/lib/identity.py
  • src/backend/services/identity-resolution/src/api/seed.rs
  • src/ingestion/tests/e2e/identity/test_error_contracts.py

📝 Walkthrough

Walkthrough

The change adds canonical 503 handling for persons-seed queue failures, introduces comprehensive identity endpoint error-contract tests, gates Rust-specific strict validation checks, and expands implementation-specific API coverage requirements.

Changes

Identity error contracts

Layer / File(s) Summary
Seed enqueue failure handling
src/backend/services/identity-resolution/src/api/seed.rs
Persons-seed enqueue failures now mark the operation failed and return a canonical 503 response, with unit tests for full and closed channels.
Identity endpoint error-contract tests
src/ingestion/tests/e2e/identity/test_error_contracts.py, src/ingestion/tests/e2e/lib/identity.py
E2E tests cover validation, authorization, unknown IDs, malformed inputs, nil tenants, and Rust-only strict validation behavior.
Implementation-specific coverage requirements
src/ingestion/tests/e2e/lib/api_coverage.py
Common, .NET-specific, and Rust-specific required status-code mappings are defined, and the Rust suite uses its dedicated mapping.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: mitasovr, cyberantonz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: expanded status-code coverage for the identity contract E2E suite.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 test/identity-e2e-status-code-gaps

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/services/identity-resolution/src/api/seed.rs`:
- Around line 222-226: Handle the result of ops_repo::fail in the
try_enqueue_job error path instead of discarding it. Log the database failure
and propagate an appropriate error when updating the operation state fails; only
return the clean retryable 503 response after the failure update succeeds, so
the operation is not left queued without a job.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c46e9943-dd85-4be7-8b2a-3b6b78f8a02f

📥 Commits

Reviewing files that changed from the base of the PR and between 6a2d12a and 12776f0e52137e77644e3606f7f6014c93a90b36.

📒 Files selected for processing (4)
  • src/backend/services/identity-resolution/src/api/seed.rs
  • src/ingestion/tests/e2e/identity/test_error_contracts.py
  • src/ingestion/tests/e2e/lib/api_coverage.py
  • src/ingestion/tests/e2e/lib/identity.py

Comment thread src/backend/services/identity-resolution/src/api/seed.rs
@mozhaev-dev

Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit finding in ef969292: the ops_repo::fail result on the queue-full path is now logged instead of discarded. The 503 is still returned either way — retry-later remains the right caller signal, and a row left queued is reclaimed by the startup zombie sweep (sweep_zombies) — so only the observability changes, not the contract. Also rebased with --signoff to fix the DCO check.

@mozhaev-dev
mozhaev-dev force-pushed the test/identity-e2e-status-code-gaps branch from 12776f0 to ef96929 Compare July 29, 2026 03:30
mozhaev-dev and others added 3 commits July 29, 2026 06:30
Close the per-status-code gaps of the identity coverage report: validation
400s on the mutating endpoints, 404s for unknown ids on DELETE/GET, the
401/403 gate proven per-route (persons-seed reads, roles/person-roles/
visibility deletes), forest-route param validation, and the nil-tenant 400.

Every case was checked against BOTH implementations' source before being
added; the two Rust-only behaviors found (revoke-reason validation in
DELETE bodies, nil viewed_person_id rejection) are documented divergences
and stay out. 28 new tests, green on rust (99 passed) and dotnet (100
passed); coverage gate PASS 18/18.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
… 503 cases

Review round on the error-contract tests:

- Malformed-uuid / query-param 400s proven on both implementations
  (DELETE ids, GET /v1/persons-seed/{id}, ?limit= / ?person= / ?active=,
  including GET /v1/persons-seed?limit=abc).
- Rust-only strict validation behind a new explicit capability
  (supports_strict_input_validation): revoke-reason cap in DELETE bodies,
  nil viewed_person_id refusal, malformed subchart person_id.
- Coverage gate now BLOCKS on the full observed code matrix: REQUIRED_EXTRA
  extended from the 7 mutation success codes to every proven error code,
  with per-suite deltas (dotnet 422-family + legacy lookup vs rust
  409-family) via the new IDENTITY_RUST_REQUIRED_EXTRA.
- The queue-full 503 on POST /v1/persons-seed is not deterministically
  inducible black-box (capacity is a compile-time constant), so the refusal
  is extracted into try_enqueue_job and pinned by Rust unit tests
  (closed + full channel -> 503); sea-orm MockDatabase was not an option
  (its feature breaks toolkit-db's Clone on DatabaseConnection).

Verified: e2e identity suite green on rust and dotnet; gates PASS
(rust 69/70 coverable codes — the one miss is the legacy endpoint's 200,
an approved removal; dotnet 71/71); cargo test 61 passed; clippy clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
CodeRabbit finding on #2005: the ops_repo::fail result was discarded.
The 503 still goes out either way (retry-later is the right caller
signal; a row left queued is reclaimed by the startup zombie sweep),
but the DB failure is now logged instead of silently swallowed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sergei Mozhaev <mozhaev.dev@gmail.com>
@mozhaev-dev
mozhaev-dev force-pushed the test/identity-e2e-status-code-gaps branch from ef96929 to c07772a Compare July 29, 2026 03:30
@ktursunov
ktursunov enabled auto-merge July 29, 2026 04:08
@ktursunov
ktursunov merged commit f95b3f7 into main Jul 29, 2026
47 checks passed
@ktursunov
ktursunov deleted the test/identity-e2e-status-code-gaps branch July 29, 2026 08:30
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