Skip to content

fix: add missing tests for webhook dead-letter queue, e2e cleanup, governance lifecycle, and FxRateCache outage#755

Merged
Haroldwonder merged 4 commits into
Haroldwonder:mainfrom
Fidelis900:main
Jun 1, 2026
Merged

fix: add missing tests for webhook dead-letter queue, e2e cleanup, governance lifecycle, and FxRateCache outage#755
Haroldwonder merged 4 commits into
Haroldwonder:mainfrom
Fidelis900:main

Conversation

@Fidelis900
Copy link
Copy Markdown
Contributor

Summary

This PR resolves four open issues by adding the missing test coverage across the backend and API test suites:

#704 — WebhookDispatcher retry exhaustion and dead-letter queue

File: backend/src/__tests__/webhook-dispatcher.test.ts

Added a retry exhaustion and dead-letter queue describe block with three tests:

  • Verifies that when a delivery fails on the last allowed attempt (attempt_count = max_attempts - 1), markWebhookDeliveryFailure is called with nextAttempt >= maxAttempts — the condition the database uses to set status = 'failed' (dead-letter).
  • Verifies that a dead-letter delivery reset to pending is successfully replayed via retryPendingDeliveries, calling markWebhookDeliverySuccess.
  • Verifies that a successfully replayed delivery does not trigger enqueueWebhookDelivery, preventing duplicate re-queuing.

#701 — e2e.test.ts uses real database without cleanup between tests

File: backend/src/__tests__/e2e.test.ts

  • Reordered resetDb table clears to respect foreign-key dependency order: fx_rates → transactions → user_kyc_status → anchor_kyc_configs. This makes the function safe to run against a real PostgreSQL database without FK-constraint violations.
  • Swapped the beforeEach call order to vi.clearAllMocks() before resetDb(), ensuring any unconsumed mockResolvedValueOnce queues from a failing test are flushed before the DB state is reset.

#702 — No integration tests for the governance proposal lifecycle end-to-end

File: api/src/__tests__/governance-lifecycle.test.ts (new file)

Added a full-lifecycle integration test suite for the governance proposal flow (propose → vote → confirm) exercised through the /api/admin/actions REST endpoints:

  • Proposal initiation: valid proposals accepted, missing auth/operation/field rejected.
  • Multi-admin voting: second admin can confirm; initiating admin is blocked from self-confirming (409); pending list reflects all unconfirmed proposals.
  • Timelock enforcement: confirmed proposals disappear from the pending list; expired proposals cannot be confirmed (409) and are excluded from GET /api/admin/actions; unknown IDs return 404.

Uses a vi.hoisted + vi.mock in-memory AdminConfirmationService that mirrors the real service's business rules, so the test exercises the full HTTP → router → service path without a database.


#703 — No test for FxRateCache stale fallback when both caches are empty

File: backend/src/__tests__/fx-rate-cache.test.ts

Added the previously missing test case:

"propagates provider error when both live and stale cache are empty on first request"

When no prior successful fetch exists (stale cache is empty) and the provider returns a non-429 error on the very first call, the cache must propagate the error as 'Failed to fetch FX rate' rather than silently swallowing it.


Test plan

  • vitest run backend/src/__tests__/webhook-dispatcher.test.ts — all existing + 3 new tests pass
  • vitest run backend/src/__tests__/e2e.test.ts — all tests pass with no cross-test pollution
  • vitest run api/src/__tests__/governance-lifecycle.test.ts — all 11 new lifecycle tests pass
  • vitest run backend/src/__tests__/fx-rate-cache.test.ts — all existing + 1 new test pass

Closes #704
Closes #701
Closes #702
Closes #703

🤖 Generated with Claude Code

Fidelis900 and others added 4 commits June 1, 2026 11:31
…on and dead-letter queue

Adds three tests covering the previously untested scenario where all retry
attempts are exhausted: verifies the delivery is moved to dead-letter status
(attempt_count >= max_attempts), that dead-letter deliveries can be replayed
when reset to pending, and that a successful replay does not re-queue the
delivery via enqueueWebhookDelivery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…and mocks are cleared first

Reorders the resetDb table clears to respect foreign-key dependency order
(fx_rates → transactions → user_kyc_status → anchor_kyc_configs) so the
function is safe to call against a real database. Also swaps the beforeEach
order to call vi.clearAllMocks() before resetDb(), ensuring stale mock queues
from a previous test cannot influence the subsequent DB reset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lifecycle through REST API

Adds a full-lifecycle integration test covering the propose → vote → execute
(admin confirmation) flow through the /api/admin/actions REST endpoints,
including multi-admin voting (self-confirm blocked), timelock enforcement
(expired proposals rejected, absent from pending list), and authentication
guards on every route.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r outage on first request

Adds the missing test case where both the live cache and the stale fallback
cache are empty (no prior successful fetch) and the provider returns a non-429
error. Verifies the error is propagated as 'Failed to fetch FX rate' rather
than being silently swallowed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

@Fidelis900 is attempting to deploy a commit to the Harold's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Jun 1, 2026

@Fidelis900 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Haroldwonder Haroldwonder merged commit 7f583a7 into Haroldwonder:main Jun 1, 2026
6 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants