Skip to content

feat(s4): infrastructure persistence tests — 32 adapter ITs (STA-135)#126

Merged
Puneethkumarck merged 1 commit into
mainfrom
feature/STA-135-s4-persistence-tests
Mar 8, 2026
Merged

feat(s4): infrastructure persistence tests — 32 adapter ITs (STA-135)#126
Puneethkumarck merged 1 commit into
mainfrom
feature/STA-135-s4-persistence-tests

Conversation

@Puneethkumarck

@Puneethkumarck Puneethkumarck commented Mar 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • ChainTransferPersistenceAdapterIT (9 tests): CRUD, findByPaymentIdAndType, findByStatus, full happy path (PENDING→CONFIRMED), resubmission path, failure path, return transfer with parent ID, nullable chain_id
  • WalletPersistenceAdapterIT (7 tests): CRUD, findByAddress, findByChainIdAndPurpose (active only), deactivate update, unique address+chain constraint
  • WalletBalancePersistenceAdapterIT (8 tests): CRUD, findByWalletIdAndStablecoin, findByWalletId, reserve, release + confirmDebit, unique constraint, decimal precision (8 places)
  • TransferParticipantPersistenceAdapterIT (4 tests): CRUD, multiple participants (INPUT/OUTPUT/FEE), all participant types enum round-trip
  • TransferLifecycleEventPersistenceAdapterIT (4 tests): CRUD, multiple events, event with participant details
  • V4 migration: fix NUMERIC(20,0) → BIGINT for block_number, nonce, last_indexed_block (Hibernate maps Long to BIGINT)

Total: 281 tests (249 unit + 32 IT)

Test plan

  • All 32 integration tests pass with TestContainers PostgreSQL
  • V4 migration validates correctly with Hibernate ddl-auto=validate
  • Spotless formatting verified
  • Single-assert pattern used throughout (recursive comparison)

Closes STA-135

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Added comprehensive integration tests for persistence: wallet storage, wallet balances, chain transfers, transfer lifecycle events, and transfer participants — covering save/retrieve, queries, state transitions, uniqueness, and precision checks.
  • Chores
    • Database migration converting several numeric columns to bigint to align storage with long-integer precision.

@Puneethkumarck Puneethkumarck added the phase-3 Value Movement MVP label Mar 8, 2026
@coderabbitai

coderabbitai Bot commented Mar 8, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

Adds five new integration test classes covering persistence for chain transfers, transfer participants, lifecycle events, wallet balances, and wallets, plus a DB migration converting several NUMERIC(20,0) columns to BIGINT to align with Java Long mapping.

Changes

Cohort / File(s) Summary
Integration tests — chain transfer & lifecycle
blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/ChainTransferPersistenceAdapterIT.java, .../TransferLifecycleEventPersistenceAdapterIT.java
New integration tests exercising ChainTransfer and TransferLifecycleEvent repositories: save/retrieve, queries by transfer/payment IDs and status, multi-event sequences, deep recursive comparisons (BigDecimal precision, ignoring Instant).
Integration tests — participants & wallets
.../TransferParticipantPersistenceAdapterIT.java, .../WalletPersistenceAdapterIT.java
New tests for TransferParticipant and Wallet repositories: CRUD flows, enum round-trip verification, find-by-address/id, filtering by chain/purpose, update/deactivate, and unique-constraint assertions.
Integration tests — wallet balances
.../WalletBalancePersistenceAdapterIT.java
New WalletBalance tests covering sync/reserve/release/confirm flows, unique (walletId, stablecoin) constraint, decimal precision persistence, and various query behaviors.
Database migration
blockchain-custody/blockchain-custody/src/main/resources/db/migration/V4__fix_numeric_to_bigint.sql
Adds migration converting NUMERIC(20,0) columns to BIGINT for: chain_transfers (block_number, nonce), wallet_balances (last_indexed_block), and wallet_nonces (current_nonce).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

service-s4, test

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes the main change: adding 32 integration tests for persistence adapters with clear reference to the story identifier STA-135.
Description check ✅ Passed Description covers all required sections: summary of test counts per adapter, test plan confirmation, and issue closure; closely follows template structure with appropriate checklist items marked.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/STA-135-s4-persistence-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/TransferParticipantPersistenceAdapterIT.java`:
- Around line 104-112: The test currently only checks row count; instead
retrieve the saved participants via
adapter.findByTransferId(transfer.transferId()) and assert their ParticipantType
values match ParticipantType.values() (e.g., compare sets or sorted lists) to
ensure enum mapping is persisted correctly; locate where
adapter.save(TransferParticipant.create(...)) is used and replace/extend the
final assertion to verify the retrieved participants' getType() (or equivalent)
equals the expected ParticipantType values rather than only checking size.

In
`@blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/WalletPersistenceAdapterIT.java`:
- Around line 69-82: The test should verify that the returned wallet is the
active one, not just the count: after calling
adapter.findByChainIdAndPurpose(CHAIN_BASE, PURPOSE_ON_RAMP) assert that the
single result matches the saved active wallet (the variable active returned by
adapter.save(anActiveWallet())) or at minimum assert that result.isActive() /
!result.isDeactivated() is true; update the test method
shouldFindByChainIdAndPurposeOnlyActiveWallets to fetch the single wallet from
results and compare its identity/fields against active (or assert it's not
deactivated) so a returned deactivated row would fail.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0c058517-60b7-4090-95c5-2dc04ff444ec

📥 Commits

Reviewing files that changed from the base of the PR and between a6ac620 and 84654e2.

📒 Files selected for processing (6)
  • blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/ChainTransferPersistenceAdapterIT.java
  • blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/TransferLifecycleEventPersistenceAdapterIT.java
  • blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/TransferParticipantPersistenceAdapterIT.java
  • blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/WalletBalancePersistenceAdapterIT.java
  • blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/WalletPersistenceAdapterIT.java
  • blockchain-custody/blockchain-custody/src/main/resources/db/migration/V4__fix_numeric_to_bigint.sql

Comment on lines +104 to +112
for (var type : ParticipantType.values()) {
adapter.save(TransferParticipant.create(
transfer.transferId(), type,
FROM_ADDRESS, wallet.walletId(),
new BigDecimal("100.00"), "USDC"));
}

assertThat(adapter.findByTransferId(transfer.transferId()))
.hasSize(ParticipantType.values().length);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assert the persisted enum values, not just the row count.

Lines 111-112 only prove that one row was written per loop iteration. A broken enum mapping that stores multiple ParticipantType values as the same database value would still pass. Assert the retrieved participant types match ParticipantType.values().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/TransferParticipantPersistenceAdapterIT.java`
around lines 104 - 112, The test currently only checks row count; instead
retrieve the saved participants via
adapter.findByTransferId(transfer.transferId()) and assert their ParticipantType
values match ParticipantType.values() (e.g., compare sets or sorted lists) to
ensure enum mapping is persisted correctly; locate where
adapter.save(TransferParticipant.create(...)) is used and replace/extend the
final assertion to verify the retrieved participants' getType() (or equivalent)
equals the expected ParticipantType values rather than only checking size.

Comment on lines +69 to +82
@Test
@DisplayName("should find by chain id and purpose only active wallets")
void shouldFindByChainIdAndPurposeOnlyActiveWallets() {
var active = adapter.save(anActiveWallet());
var deactivated = Wallet.create(
CHAIN_BASE, "0xDeactivatedWallet999", "0xDeactivatedWallet999",
WalletTier.HOT, WalletPurpose.ON_RAMP, "fireblocks", "vault-002",
StablecoinTicker.of("USDC"));
var savedDeactivated = adapter.save(deactivated);
adapter.save(savedDeactivated.deactivate());

var results = adapter.findByChainIdAndPurpose(CHAIN_BASE, PURPOSE_ON_RAMP);
assertThat(results).hasSize(1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assert the returned wallet is the active one.

Lines 80-81 only verify cardinality. If the query returns the deactivated row and drops the active one, this test still passes. Compare the single result with active, or at least assert it is not deactivated.

Suggested assertion
         var results = adapter.findByChainIdAndPurpose(CHAIN_BASE, PURPOSE_ON_RAMP);
-        assertThat(results).hasSize(1);
+        assertThat(results).singleElement()
+                .usingRecursiveComparison()
+                .ignoringFieldsOfTypes(Instant.class)
+                .isEqualTo(active);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
@DisplayName("should find by chain id and purpose only active wallets")
void shouldFindByChainIdAndPurposeOnlyActiveWallets() {
var active = adapter.save(anActiveWallet());
var deactivated = Wallet.create(
CHAIN_BASE, "0xDeactivatedWallet999", "0xDeactivatedWallet999",
WalletTier.HOT, WalletPurpose.ON_RAMP, "fireblocks", "vault-002",
StablecoinTicker.of("USDC"));
var savedDeactivated = adapter.save(deactivated);
adapter.save(savedDeactivated.deactivate());
var results = adapter.findByChainIdAndPurpose(CHAIN_BASE, PURPOSE_ON_RAMP);
assertThat(results).hasSize(1);
}
`@Test`
`@DisplayName`("should find by chain id and purpose only active wallets")
void shouldFindByChainIdAndPurposeOnlyActiveWallets() {
var active = adapter.save(anActiveWallet());
var deactivated = Wallet.create(
CHAIN_BASE, "0xDeactivatedWallet999", "0xDeactivatedWallet999",
WalletTier.HOT, WalletPurpose.ON_RAMP, "fireblocks", "vault-002",
StablecoinTicker.of("USDC"));
var savedDeactivated = adapter.save(deactivated);
adapter.save(savedDeactivated.deactivate());
var results = adapter.findByChainIdAndPurpose(CHAIN_BASE, PURPOSE_ON_RAMP);
assertThat(results).singleElement()
.usingRecursiveComparison()
.ignoringFieldsOfTypes(Instant.class)
.isEqualTo(active);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@blockchain-custody/blockchain-custody/src/integration-test/java/com/stablecoin/payments/custody/infrastructure/persistence/WalletPersistenceAdapterIT.java`
around lines 69 - 82, The test should verify that the returned wallet is the
active one, not just the count: after calling
adapter.findByChainIdAndPurpose(CHAIN_BASE, PURPOSE_ON_RAMP) assert that the
single result matches the saved active wallet (the variable active returned by
adapter.save(anActiveWallet())) or at minimum assert that result.isActive() /
!result.isDeactivated() is true; update the test method
shouldFindByChainIdAndPurposeOnlyActiveWallets to fetch the single wallet from
results and compare its identity/fields against active (or assert it's not
deactivated) so a returned deactivated row would fail.

ChainTransferPersistenceAdapterIT (10 tests): CRUD, findByPaymentIdAndType,
findByStatus, full happy path, resubmission path, failure path, return
transfer with parent id, nullable chain id.

WalletPersistenceAdapterIT (7 tests): CRUD, findByAddress,
findByChainIdAndPurpose (only active), deactivate update, unique constraint.

WalletBalancePersistenceAdapterIT (8 tests): CRUD, findByWalletIdAndStablecoin,
findByWalletId, reserve, release + confirmDebit, unique constraint,
decimal precision (8 places).

TransferParticipantPersistenceAdapterIT (4 tests): CRUD, multiple
participants (INPUT/OUTPUT/FEE), all participant types enum round-trip.

TransferLifecycleEventPersistenceAdapterIT (4 tests): CRUD, multiple
events, event with participant details.

V4 migration: fix NUMERIC(20,0) → BIGINT for block_number, nonce,
last_indexed_block, current_nonce (Hibernate maps Long to BIGINT).

Closes STA-135

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Puneethkumarck Puneethkumarck force-pushed the feature/STA-135-s4-persistence-tests branch from 84654e2 to ae06ee3 Compare March 8, 2026 18:34
@Puneethkumarck Puneethkumarck merged commit 5438a74 into main Mar 8, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

phase-3 Value Movement MVP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant