test(procurement): verify pending commands and late successful receipts

This commit is contained in:
2026-10-10 14:01:50 +08:00
parent 721d92ac7b
commit 369613a2e7
@@ -120,6 +120,67 @@ class ProcurementFinancePersistenceTest {
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_procurement_wallet_attempt WHERE fee_id=?",Integer.class,fee.getId())).isEqualTo(2);
}
@Test void pendingOriginalRejectsReplacementAndLateSuccessKeepsOriginalAmountAndLedger() {
var original=confirm(ProcurementFeePurpose.DUTY,"POSITIVE","10");
var originalFreeze=result(original,"FREEZE","SUCCEEDED","FROZEN","10");
assertPendingVersionCannotBeReplaced(original);
// Replaying a timed-out confirmation returns the original command, never a new attempt.
assertThat(confirm(ProcurementFeePurpose.DUTY,"POSITIVE","10").getWalletCommandEventId())
.isEqualTo(originalFreeze.commandEventId());
handle(originalFreeze);
var settling=fees.selectById(original.getId());
assertPendingVersionCannotBeReplaced(settling);
assertThat(confirm(ProcurementFeePurpose.DUTY,"POSITIVE","10").getWalletCommandEventId())
.isEqualTo(settling.getWalletCommandEventId());
var lateSuccess=result(settling,"SETTLE","SUCCEEDED","SETTLED","10");
handle(lateSuccess);
// A delayed freeze receipt after settlement must not regress the settled projection.
handle(originalFreeze);
handle(lateSuccess);
var saved=fees.selectById(original.getId());
assertThat(saved.getFeeVersion()).isEqualTo(1);
assertThat(saved.getOriginalAmount()).isEqualByComparingTo("10");
assertThat(saved.getAmount()).isEqualByComparingTo("10");
assertThat(saved.getWalletStatus()).isEqualTo("SETTLED");
assertThat(saved.getSettledCommandEventId()).isEqualTo(lateSuccess.commandEventId());
assertThat(saved.getWalletLedgerId()).isEqualTo(lateSuccess.walletLedgerId());
assertThat(saved.getSourceFeeId()).isEqualTo(901L);
assertThat(service.all(11)).hasSize(1);
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_procurement_wallet_attempt WHERE fee_id=? AND status='SUCCEEDED'",Integer.class,original.getId())).isEqualTo(2);
assertThat(jdbc.queryForObject("SELECT fee_id FROM ll_procurement_wallet_attempt WHERE command_event_id=?",Long.class,lateSuccess.commandEventId())).isEqualTo(original.getId());
verify(commands,times(2)).publish(anyString(),anyLong(),anyString(),anyString(),any(),anyString(),anyLong());
verify(commands,never()).publishRefund(anyLong(),anyString(),anyString(),any(),anyString(),anyString(),anyLong());
verify(sources).publishSettled(anyString(),anyString(),argThat(r -> r.amount().compareTo(new BigDecimal("10"))==0),any(),
argThat(ref -> ref.commandEventId().equals(lateSuccess.commandEventId())));
}
private void assertPendingVersionCannotBeReplaced(OverseasProcurementFee original) {
assertThatThrownBy(() -> confirm(ProcurementFeePurpose.DUTY,"POSITIVE","20")).hasMessageContaining("已确认");
assertThatThrownBy(() -> tx.execute(s -> service.replaceFailed("9","PO-1","7",original.getId(),new BigDecimal("20"),1,"corrected target")))
.hasMessageContaining("须先终止");
assertThatThrownBy(() -> tx.execute(s -> service.retry("9","PO-1",original.getId()))).hasMessageContaining("未明确失败");
assertThatThrownBy(() -> tx.executeWithoutResult(s -> service.terminateFailed("9","PO-1",original.getId()))).hasMessageContaining("明确失败");
assertThatThrownBy(() -> tx.execute(s -> service.refund("9","PO-1","7",original.getId(),BigDecimal.ONE,1,"corrected target","pending-refund")))
.hasMessageContaining("已成功实扣");
assertThat(fees.selectById(original.getId()).getWalletCommandEventId()).isEqualTo(original.getWalletCommandEventId());
}
@Test void pendingEstimateCannotBeReleasedOrReplacedBeforeLateFreezeResult() {
var estimate=confirm(ProcurementFeePurpose.INTERNATIONAL_ESTIMATE,"POSITIVE","10");
assertThatThrownBy(() -> tx.executeWithoutResult(s -> service.releaseEstimate("9","PO-1",estimate.getId())))
.hasMessageContaining("未决");
assertThatThrownBy(() -> confirm(ProcurementFeePurpose.INTERNATIONAL_ESTIMATE,"POSITIVE","20")).hasMessageContaining("已确认");
assertThat(confirm(ProcurementFeePurpose.INTERNATIONAL_ESTIMATE,"POSITIVE","10").getId()).isEqualTo(estimate.getId());
var lateFreeze=result(estimate,"FREEZE","SUCCEEDED","FROZEN","10");
handle(lateFreeze);handle(lateFreeze);
assertThat(fees.selectById(estimate.getId()).getWalletStatus()).isEqualTo("FROZEN");
assertThat(fees.selectById(estimate.getId()).getAmount()).isEqualByComparingTo("10");
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_procurement_wallet_attempt",Integer.class)).isEqualTo(1);
verify(commands).publish(eq("FREEZE"),anyLong(),anyString(),eq(estimate.getFeeNo()),eq(new BigDecimal("10")),anyString(),anyLong());
verifyNoMoreInteractions(commands);
verifyNoInteractions(sources);
}
@Test void sourceFailureRollsBackResultProjectionAndRetriesWithoutAnotherDebit() {
var fee=confirm(ProcurementFeePurpose.DUTY,"POSITIVE","10");
handle(result(fee,"FREEZE","SUCCEEDED","FROZEN","10"));