Skip to content

Resolve #230, #231, #233, #237: event poller ingestion and test coverage - #307

Merged
Tybravo merged 2 commits into
SwiftChainn:mainfrom
Vera-Auth:feat/indexer-poller-and-issue-tests
Oct 21, 2026
Merged

Tybravo merged 2 commits into
SwiftChainn:mainfrom
Vera-Auth:feat/indexer-poller-and-issue-tests

Conversation

@Vera-Auth

Copy link
Copy Markdown
Contributor

Summary

This PR resolves four issues in one change set: three test-coverage issues and one indexer feature. The feature is the event poller, which was a stub.

Commit: f2deed3 (author and committer Vera-Auth).

Implementation by issue

#230 NotificationPreferenceRepository tests

File: tests/notificationPreferenceRepository.test.ts. Runs against in-memory MongoDB, so the unique devices.token index is exercised for real.

  • findOrCreateByUserId creates defaults on first call and returns the same document afterwards.
  • registerDevice attaches a token and detaches it from the previous owner.
  • Re-registering an owned token is idempotent and keeps the user's other tokens.
  • removeDevice removes only the given token.
  • pruneTokens removes the listed tokens across users, returns the modified count, and does nothing for an empty list.

Note on the issue text: #230 says pruneTokens removes stale tokens "based on lastSeenAt". The implementation takes an explicit token list and never reads lastSeenAt. The tests cover the real behaviour. If age-based pruning is wanted, it is a separate change.

#231 Fleet routes integration tests

Files: tests/integration/fleetRoutes.test.ts (new, 23 tests) and tests/fleet.test.ts (existing suite, fixtures repaired).

The existing tests/fleet.test.ts could not pass against the current API. Its fixtures omitted treasuryAddress and businessMetadata, which the controller and model require, and some used a drivers field the setup did not populate. Its metrics expectation also lacked totalEscrowValueAsset. Those fixtures are repaired, and the suite is green (32/32).

Behaviour added to the controller, because the issue and the docs call for it:

  • POST /api/v1/fleets now rejects a treasury address that does not match ^G[A-Z0-9]{55}$ (docs/fleet.md) with 400. Before this change any non-empty string was accepted.
  • POST /api/v1/fleets returns 409 when the same owner already has a fleet with the same name. The service layer already had this check, but the route did not call the service.

Both are behaviour changes. Clients that send non-Stellar treasury addresses, or repeat a fleet name, will now get 400 or 409.

#233 ProofOfDeliveryService unit tests

File: tests/proofOfDeliveryService.test.ts. Real Delivery documents on in-memory MongoDB. Only the storage driver is mocked.

Covers the MIME allow-list (accepted and rejected types), the size cap (exact limit and one byte over), stored metadata and the storage key, assigned-driver authorization, 409 for completed and cancelled deliveries, 400 and 404 lookups, and the assertProofOfDeliveryExists guard (passes with proof, 422 without).

Not covered here, and tracked as follow-ups below: the escrow release path that calls the guard (escrow.service.ts), and the admin role check.

#237 Event poller ingestion

Files: src/services/eventPoller.ts (rewritten), tests/eventPoller.test.ts (new, 12 tests), backend/docs/indexer.md, docs/architecture.md, TODO.md.

Each cycle:

  1. Reads the IndexerStatus checkpoint for STELLAR_NETWORK and calls getLatestLedger().
  2. Pages through getEvents() from the ledger after the checkpoint, filtered on the escrow funded topic. It stops at the first empty page, which avoids stopping early when the RPC caps the page size.
  3. Records each subscribed event in EventLog and dispatches it to handleEscrowFundedEvent. Dedupe uses the existing unique index on {transactionHash, eventType}. A record already processed or failed is skipped. A record left pending by an interrupted cycle is processed again.
  4. A handler that throws marks its record failed with errorMessage. The checkpoint still advances.
  5. Advances IndexerStatus.lastProcessedLedger to the synced ledger. RPC or database errors leave it unchanged.

startEventPoller() and stopEventPoller() keep their signatures, so server.ts and the graceful-shutdown service need no change. The poller does not start when ESCROW_CONTRACT_ID is unset.

Design choices to review:

  • The issue asks for a "polling loop abstraction" that does not exist in the repository. The loop is written inside EventPoller. Subscriptions are injected, so the poller is testable without the network.
  • The issue says to keep the checkpoint from passing unprocessed events, but it also says to mark failures for later retry. This PR follows the retry requirement. A failed event is recorded and does not block the checkpoint. The indexer doc says so.
  • With no checkpoint, the poller starts at the current ledger and does not backfill history.
  • Lag is still computed by monitorService.checkIndexerLag, not by the poller.

Tests and results

Run from the repository root after pnpm install --frozen-lockfile.

Command Result
npx jest tests/notificationPreferenceRepository.test.ts tests/proofOfDeliveryService.test.ts 34 passed, 34 total
npx jest tests/fleet.test.ts 32 passed, 32 total
npx jest tests/integration/fleetRoutes.test.ts 23 passed, 23 total
npx jest tests/eventPoller.test.ts 12 passed, 12 total
npx jest (full suite) 1102 tests: 1036 passed, 66 failed, across 74 suites (14 failing suites)
npx eslint on every changed source and test file 0 errors, 0 warnings
npx tsc --noEmit 1 error, in tests/deliveryStatusTransition.test.ts:96 (stray characters). This is on main too.

The 14 failing suites are the same suites that fail on main: disputes, delivery status update and transition, QR code, delivery ETA, escrow E2E, escrow handlers, indexer API, Swagger, auth flow, delivery flow and socket location. A run of those suites on a clean main worktree produced the same failing set. Those failures are outside this change. On main several of these suites could not load at all, because of the validate bug fixed below. They now load and run, and the remaining failures are in their own logic.

Compatibility and security

  • src/routes/delivery.routes.ts: the fee-estimate route called validate(...), which was never imported. Because app.ts loads this route, the whole app failed to load in tests. The call now uses the existing validateRequest import. No behaviour change beyond the app loading.
  • Fleet create: treasury format validation and the duplicate-name 409 are described above.
  • The poller issues getEvents and writes EventLog/IndexerStatus once the server starts with ESCROW_CONTRACT_ID set. This is new runtime behaviour. Handlers must be idempotent, because a pending record can be processed again.
  • No secrets, credentials or production data are added. Test fixtures use placeholder addresses and emails.

Known limitations and follow-ups

  • Failed EventLog records are not retried yet. They are marked failed for a later retry tool.
  • Deduplication keys on the transaction hash, so two escrow events in one transaction produce one record and the second is skipped. The issue asked for this key, so it is kept. Changing it would need a schema change.
  • Only the escrow funded topic is subscribed. The delivery, dispute, reputation and milestone handlers need their own subscriptions.
  • ProofOfDeliveryService authorization only checks the assigned driver. An unassigned delivery accepts an upload from any authenticated user, and the admin role is not checked at the service layer. The issue asks for driver-or-admin only. This PR does not change that. Tests lock in only the assigned-driver rule.
  • The escrow release path that calls assertProofOfDeliveryExists is not tested directly. The guard itself is tested.
  • Issue [Testing] Write integration tests for the fleet routes (create, members, invites, metrics) #231 paths refer to backend/tests/..., but test files live in the repository root tests/ directory. The new files follow that layout.

closes #230
closes #231
closes #233
closes #237

…ainn#237: tests and event poller ingestion

SwiftChainn#230: add repository tests for NotificationPreferenceRepository covering
findOrCreate defaults, cross-user device detach on registerDevice,
idempotent re-registration, removeDevice scoping and pruneTokens.

SwiftChainn#231: add an integration suite for the fleet routes (member add/remove
rules, treasury validation, fleet lookup, invite -> accept -> metrics and
invite -> decline). Repair the existing fleet suite fixtures, which were
written against an older model (no treasuryAddress, businessMetadata or
members) and could not pass against the current API.

SwiftChainn#233: add unit tests for ProofOfDeliveryService covering the MIME
allow-list, the size cap, the upload flow and metadata, assigned-driver
authorization, delivery state checks, lookups and the
assertProofOfDeliveryExists guard.

SwiftChainn#237: replace the event poller stub with a getEvents ingestion loop. Each
cycle pages through escrow_funded events from the checkpoint, records
them in EventLog (deduplicated on transactionHash and eventType),
dispatches them to the matching handler, marks handler failures as
failed with errorMessage, and advances IndexerStatus.lastProcessedLedger.

Supporting fixes:
- src/routes/delivery.routes.ts: the fee-estimate route called an
  undefined validate(); it now uses the validateRequest import. This
  made app.ts fail to load, which broke every suite that imports it.
- src/controllers/fleetController.ts: enforce the documented Stellar
  public-key format for treasury addresses and reject a duplicate fleet
  name for the same owner with 409.

Docs: indexer reference and architecture notes now describe the
implemented poller; TODO.md updated.
…hainn#233)

Add an integration test that runs releaseEscrow against the real escrow
and proof-of-delivery services. It covers a release rejected with 422
when no proof is on record, a release that completes the delivery once
proof is uploaded, and a release rejected again after the proof is
removed. The existing escrow E2E suite mocks the whole proof-of-delivery
service, so it could not exercise this guard.
@drips-wave

drips-wave Bot commented Oct 21, 2026

Copy link
Copy Markdown

@Vera-Auth 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

@Tybravo

Tybravo commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

@Vera-Auth

Thank you for contributing.

@Tybravo
Tybravo merged commit a7734ae into SwiftChainn:main Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment