mirror of
https://github.com/aljazceru/lnd-manageJ.git
synced 2025-12-18 14:44:26 +01:00
bugfix: properly exclude self-payments when computing amount received via payments
This commit is contained in:
@@ -76,10 +76,8 @@ public class FlowService {
|
||||
Coins rebalanceSupportReceived = rebalanceService.getSupportAsTargetAmountToChannel(channelId, maxAge);
|
||||
Coins rebalanceFeesSent = rebalanceService.getSourceCostsForChannel(channelId, maxAge);
|
||||
Coins rebalanceSupportFeesSent = rebalanceService.getSupportAsSourceCostsFromChannel(channelId, maxAge);
|
||||
Coins receivedViaPayments = settledInvoicesService.getAmountReceivedViaChannel(channelId, maxAge)
|
||||
.subtract(rebalanceReceived)
|
||||
.subtract(rebalanceSupportReceived)
|
||||
.maximum(Coins.NONE);
|
||||
Coins receivedViaPayments =
|
||||
settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(channelId, maxAge);
|
||||
|
||||
return new FlowReport(
|
||||
forwardedSent,
|
||||
|
||||
@@ -16,8 +16,8 @@ public class SettledInvoicesService {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
public Coins getAmountReceivedViaChannel(ChannelId channelId, Duration maxAge) {
|
||||
return dao.getInvoicesPaidVia(channelId, maxAge).stream()
|
||||
public Coins getAmountReceivedViaChannelWithoutSelfPayments(ChannelId channelId, Duration maxAge) {
|
||||
return dao.getInvoicesWithoutSelfPaymentsPaidVia(channelId, maxAge).stream()
|
||||
.map(SettledInvoice::receivedVia)
|
||||
.map(receivedVia -> receivedVia.getOrDefault(channelId, Coins.NONE))
|
||||
.reduce(Coins.NONE, Coins::add);
|
||||
|
||||
@@ -60,7 +60,8 @@ class FlowServiceTest {
|
||||
lenient().when(rebalanceService.getSourceCostsForChannel(any())).thenReturn(Coins.NONE);
|
||||
lenient().when(rebalanceService.getSourceCostsForChannel(any(), any())).thenReturn(Coins.NONE);
|
||||
lenient().when(rebalanceService.getSupportAsSourceCostsFromChannel(any(), any())).thenReturn(Coins.NONE);
|
||||
lenient().when(settledInvoicesService.getAmountReceivedViaChannel(any(), any())).thenReturn(Coins.NONE);
|
||||
lenient().when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(any(), any()))
|
||||
.thenReturn(Coins.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,9 +80,9 @@ class FlowServiceTest {
|
||||
mockRebalanceFromTo(DEFAULT_MAX_AGE, CHANNEL_ID_2, 3, 4);
|
||||
mockRebalanceSupportFromTo(DEFAULT_MAX_AGE, CHANNEL_ID, 555, 6);
|
||||
mockRebalanceSupportFromTo(DEFAULT_MAX_AGE, CHANNEL_ID_2, 7, 888);
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
.thenReturn(Coins.ofMilliSatoshis(1_500_000));
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID_2, DEFAULT_MAX_AGE))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID_2, DEFAULT_MAX_AGE))
|
||||
.thenReturn(Coins.ofMilliSatoshis(915_000));
|
||||
FlowReport flowReport = new FlowReport(
|
||||
Coins.ofSatoshis(1_000 + 50),
|
||||
@@ -93,7 +94,7 @@ class FlowServiceTest {
|
||||
Coins.ofSatoshis(555 + 7),
|
||||
Coins.ofMilliSatoshis(555 + 7),
|
||||
Coins.ofSatoshis(6 + 888),
|
||||
Coins.ofMilliSatoshis(1_500_000 + 915_000 - 2_000 - 4_000 - 6_000 - 888_000)
|
||||
Coins.ofMilliSatoshis(1_500_000 + 915_000)
|
||||
);
|
||||
assertThat(flowService.getFlowReportForPeer(PUBKEY)).isEqualTo(flowReport);
|
||||
verify(forwardingEventsService).getEventsWithOutgoingChannel(CHANNEL_ID, DEFAULT_MAX_AGE);
|
||||
@@ -113,9 +114,9 @@ class FlowServiceTest {
|
||||
mockRebalanceFromTo(maxAge, CHANNEL_ID_2, 333, 4);
|
||||
mockRebalanceSupportFromTo(maxAge, CHANNEL_ID, 5, 6);
|
||||
mockRebalanceSupportFromTo(maxAge, CHANNEL_ID_2, 7, 8);
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, maxAge))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, maxAge))
|
||||
.thenReturn(Coins.ofMilliSatoshis(1_500_000));
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID_2, maxAge))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID_2, maxAge))
|
||||
.thenReturn(Coins.ofMilliSatoshis(15_000));
|
||||
when(channelService.getAllChannelsWith(PUBKEY)).thenReturn(Set.of(LOCAL_OPEN_CHANNEL, CLOSED_CHANNEL_2));
|
||||
FlowReport flowReport = new FlowReport(
|
||||
@@ -128,7 +129,7 @@ class FlowServiceTest {
|
||||
Coins.ofSatoshis(5 + 7),
|
||||
Coins.ofMilliSatoshis(5 + 7),
|
||||
Coins.ofSatoshis(6 + 8),
|
||||
Coins.ofMilliSatoshis(1_500_000 + 15_000 - 2_000 - 4_000 - 6_000 - 8_000)
|
||||
Coins.ofMilliSatoshis(1_500_000 + 15_000)
|
||||
);
|
||||
assertThat(flowService.getFlowReportForPeer(PUBKEY, maxAge)).isEqualTo(flowReport);
|
||||
}
|
||||
@@ -139,7 +140,7 @@ class FlowServiceTest {
|
||||
mockReceived(DEFAULT_MAX_AGE, CHANNEL_ID, 9_001L);
|
||||
mockRebalanceFromTo(DEFAULT_MAX_AGE, CHANNEL_ID, 100, 200);
|
||||
mockRebalanceSupportFromTo(DEFAULT_MAX_AGE, CHANNEL_ID, 5, 6);
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
.thenReturn(Coins.ofMilliSatoshis(1_500_000));
|
||||
FlowReport flowReport = new FlowReport(
|
||||
Coins.ofSatoshis(1_050),
|
||||
@@ -151,7 +152,7 @@ class FlowServiceTest {
|
||||
Coins.ofSatoshis(5),
|
||||
Coins.ofMilliSatoshis(5),
|
||||
Coins.ofSatoshis(6),
|
||||
Coins.ofMilliSatoshis(1_500_000 - 200_000 - 6_000)
|
||||
Coins.ofMilliSatoshis(1_500_000)
|
||||
);
|
||||
assertThat(flowService.getFlowReportForChannel(CHANNEL_ID)).isEqualTo(flowReport);
|
||||
verify(forwardingEventsService).getEventsWithOutgoingChannel(CHANNEL_ID, DEFAULT_MAX_AGE);
|
||||
@@ -165,7 +166,7 @@ class FlowServiceTest {
|
||||
mockReceived(maxAge, CHANNEL_ID, 1_000L, 8_001L);
|
||||
mockRebalanceFromTo(maxAge, CHANNEL_ID, 101, 201);
|
||||
mockRebalanceSupportFromTo(maxAge, CHANNEL_ID, 5, 6);
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, maxAge))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, maxAge))
|
||||
.thenReturn(Coins.ofMilliSatoshis(1_500_000));
|
||||
FlowReport flowReport = new FlowReport(
|
||||
Coins.ofSatoshis(1_000 + 50),
|
||||
@@ -177,7 +178,7 @@ class FlowServiceTest {
|
||||
Coins.ofSatoshis(5),
|
||||
Coins.ofMilliSatoshis(5),
|
||||
Coins.ofSatoshis(6),
|
||||
Coins.ofMilliSatoshis(1_500_000 - 201_000 - 6_000)
|
||||
Coins.ofMilliSatoshis(1_500_000)
|
||||
);
|
||||
assertThat(flowService.getFlowReportForChannel(CHANNEL_ID, maxAge)).isEqualTo(flowReport);
|
||||
}
|
||||
@@ -185,7 +186,7 @@ class FlowServiceTest {
|
||||
@Test
|
||||
void getFlowReportForChannel_includes_receivedViaPayments() {
|
||||
Coins expectedReceivedViaPayments = Coins.ofMilliSatoshis(1);
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
.thenReturn(expectedReceivedViaPayments);
|
||||
assertThat(flowService.getFlowReportForChannel(CHANNEL_ID).receivedViaPayments())
|
||||
.isEqualTo(expectedReceivedViaPayments);
|
||||
@@ -196,24 +197,12 @@ class FlowServiceTest {
|
||||
Coins expectedReceivedViaPayments = Coins.NONE;
|
||||
Coins expectedRebalanceReceived = Coins.ofSatoshis(1);
|
||||
mockRebalanceFromTo(DEFAULT_MAX_AGE, CHANNEL_ID, 0, expectedRebalanceReceived.satoshis());
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
when(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
.thenReturn(expectedReceivedViaPayments);
|
||||
assertThat(flowService.getFlowReportForChannel(CHANNEL_ID).receivedViaPayments())
|
||||
.isEqualTo(expectedReceivedViaPayments);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getFlowReportForChannel_self_payments_do_not_count_for_receivedViaPayments() {
|
||||
Coins expectedReceivedViaPayments = Coins.ofSatoshis(3);
|
||||
Coins expectedRebalanceReceived = Coins.ofSatoshis(1);
|
||||
|
||||
mockRebalanceFromTo(DEFAULT_MAX_AGE, CHANNEL_ID, 0, expectedRebalanceReceived.satoshis());
|
||||
when(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, DEFAULT_MAX_AGE))
|
||||
.thenReturn(expectedReceivedViaPayments.add(expectedRebalanceReceived));
|
||||
assertThat(flowService.getFlowReportForChannel(CHANNEL_ID).receivedViaPayments())
|
||||
.isEqualTo(expectedReceivedViaPayments);
|
||||
}
|
||||
|
||||
private void mockReceived(Duration maxAge, ChannelId channelId, Long... amounts) {
|
||||
List<ForwardingEvent> events = createListOfEvents(channelId, CHANNEL_ID_2, amounts);
|
||||
when(forwardingEventsService.getEventsWithIncomingChannel(channelId, maxAge)).thenReturn(events);
|
||||
|
||||
@@ -31,30 +31,31 @@ class SettledInvoicesServiceTest {
|
||||
private SettledInvoicesDao dao;
|
||||
|
||||
@Test
|
||||
void getAmountReceivedViaChannel_no_settled_invoices() {
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, MAX_AGE))
|
||||
void getAmountReceivedViaChannelWithoutSelfPayments_no_settled_invoices() {
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, MAX_AGE))
|
||||
.isEqualTo(Coins.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAmountReceivedViaChannel() {
|
||||
void getAmountReceivedViaChannelWithoutSelfPayments() {
|
||||
Coins amountReceivedPerInvoice = Coins.ofMilliSatoshis(CHANNEL_ID.getShortChannelId());
|
||||
when(dao.getInvoicesPaidVia(CHANNEL_ID, MAX_AGE)).thenReturn(List.of(SETTLED_INVOICE));
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, MAX_AGE))
|
||||
when(dao.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID, MAX_AGE)).thenReturn(List.of(SETTLED_INVOICE));
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, MAX_AGE))
|
||||
.isEqualTo(amountReceivedPerInvoice);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAmountReceivedViaChannel_two_invoices() {
|
||||
void getAmountReceivedViaChannelWithoutSelfPayments_two_invoices() {
|
||||
Coins amountReceivedPerInvoice = Coins.ofMilliSatoshis(CHANNEL_ID.getShortChannelId());
|
||||
Coins expected = amountReceivedPerInvoice.add(amountReceivedPerInvoice);
|
||||
when(dao.getInvoicesPaidVia(CHANNEL_ID, MAX_AGE)).thenReturn(List.of(SETTLED_INVOICE, SETTLED_INVOICE_2));
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, MAX_AGE))
|
||||
when(dao.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID, MAX_AGE))
|
||||
.thenReturn(List.of(SETTLED_INVOICE, SETTLED_INVOICE_2));
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, MAX_AGE))
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAmountReceivedViaChannel_invoice_paid_via_two_channels() {
|
||||
void getAmountReceivedViaChannelWithoutSelfPayments_invoice_paid_via_two_channels() {
|
||||
Coins oneMilliSatoshi = Coins.ofMilliSatoshis(1);
|
||||
SettledInvoice invoice = new SettledInvoice(
|
||||
SETTLED_INVOICE.addIndex(),
|
||||
@@ -66,8 +67,8 @@ class SettledInvoicesServiceTest {
|
||||
SETTLED_INVOICE.keysendMessage(),
|
||||
Map.of(CHANNEL_ID, oneMilliSatoshi, CHANNEL_ID_2, AMOUNT_PAID.subtract(oneMilliSatoshi))
|
||||
);
|
||||
when(dao.getInvoicesPaidVia(CHANNEL_ID, MAX_AGE)).thenReturn(List.of(invoice));
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannel(CHANNEL_ID, MAX_AGE))
|
||||
when(dao.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID, MAX_AGE)).thenReturn(List.of(invoice));
|
||||
assertThat(settledInvoicesService.getAmountReceivedViaChannelWithoutSelfPayments(CHANNEL_ID, MAX_AGE))
|
||||
.isEqualTo(oneMilliSatoshi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ dependencies {
|
||||
implementation project(':model')
|
||||
implementation project(':caching')
|
||||
implementation project(':grpc-adapter')
|
||||
implementation project(':payments')
|
||||
testFixturesApi testFixtures(project(':model'))
|
||||
integrationTestRuntimeOnly 'com.h2database:h2'
|
||||
integrationTestImplementation project(':payments')
|
||||
integrationTestImplementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
integrationTestImplementation testFixtures(project(':model'))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ package de.cotto.lndmanagej.invoices.persistence;
|
||||
|
||||
import de.cotto.lndmanagej.model.ChannelId;
|
||||
import de.cotto.lndmanagej.model.Coins;
|
||||
import de.cotto.lndmanagej.model.Payment;
|
||||
import de.cotto.lndmanagej.model.SettledInvoice;
|
||||
import de.cotto.lndmanagej.payments.persistence.PaymentJpaDto;
|
||||
import de.cotto.lndmanagej.payments.persistence.PaymentsRepository;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
@@ -23,6 +27,9 @@ class SettledInvoicesRepositoryIT {
|
||||
@Autowired
|
||||
private SettledInvoicesRepository repository;
|
||||
|
||||
@Autowired
|
||||
private PaymentsRepository paymentsRepository;
|
||||
|
||||
@Test
|
||||
void getMaxSettledIndexWithoutGaps_no_invoice() {
|
||||
assertThat(repository.getMaxSettledIndexWithoutGaps()).isEqualTo(0);
|
||||
@@ -61,70 +68,87 @@ class SettledInvoicesRepositoryIT {
|
||||
assertThat(repository.getMaxAddIndex()).isEqualTo(3L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_no_invoice() {
|
||||
assertThat(repository.findAllByReceivedViaChannelIdAndSettleDateAfter(CHANNEL_ID.getShortChannelId(), 0))
|
||||
.isEmpty();
|
||||
}
|
||||
@Nested
|
||||
class GetInvoicesWithoutSelfPaymentsPaidVia {
|
||||
@Test
|
||||
void no_invoice() {
|
||||
assertThat(repository.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID.getShortChannelId(), 0))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_wrong_channel_id() {
|
||||
repository.save(invoice(1, 1, Map.of(CHANNEL_ID, Coins.ofSatoshis(100))));
|
||||
assertThat(repository.findAllByReceivedViaChannelIdAndSettleDateAfter(CHANNEL_ID_2.getShortChannelId(), 0))
|
||||
.isEmpty();
|
||||
}
|
||||
@Test
|
||||
void wrong_channel_id() {
|
||||
repository.save(invoice(1, 1, Map.of(CHANNEL_ID, Coins.ofSatoshis(100))));
|
||||
assertThat(repository.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID_2.getShortChannelId(), 0))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_expected_channel_id() {
|
||||
Map<ChannelId, Coins> expected = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
repository.save(invoice(1, 1, expected));
|
||||
assertReceivedVia(CHANNEL_ID, List.of(expected));
|
||||
}
|
||||
@Test
|
||||
void expected_channel_id() {
|
||||
Map<ChannelId, Coins> expected = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
repository.save(invoice(1, 1, expected));
|
||||
assertReceivedVia(CHANNEL_ID, List.of(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_just_within_max_age() {
|
||||
Map<ChannelId, Coins> tooOld = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
SettledInvoiceJpaDto invoice = invoice(1, 1, tooOld);
|
||||
repository.save(invoice);
|
||||
long timestamp = invoice.getSettleDate() - 1;
|
||||
List<SettledInvoiceJpaDto> invoices =
|
||||
repository.findAllByReceivedViaChannelIdAndSettleDateAfter(CHANNEL_ID.getShortChannelId(), timestamp);
|
||||
assertThat(invoices).isNotEmpty();
|
||||
}
|
||||
@Test
|
||||
void just_within_max_age() {
|
||||
Map<ChannelId, Coins> tooOld = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
SettledInvoiceJpaDto invoice = invoice(1, 1, tooOld);
|
||||
repository.save(invoice);
|
||||
long timestamp = invoice.getSettleDate() - 1;
|
||||
List<SettledInvoiceJpaDto> invoices =
|
||||
repository.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID.getShortChannelId(), timestamp);
|
||||
assertThat(invoices).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_expected_channel_id_but_too_old() {
|
||||
Map<ChannelId, Coins> tooOld = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
SettledInvoiceJpaDto invoice = invoice(1, 1, tooOld);
|
||||
repository.save(invoice);
|
||||
long timestamp = invoice.getSettleDate();
|
||||
List<SettledInvoiceJpaDto> invoices =
|
||||
repository.findAllByReceivedViaChannelIdAndSettleDateAfter(CHANNEL_ID.getShortChannelId(), timestamp);
|
||||
assertThat(invoices).isEmpty();
|
||||
}
|
||||
@Test
|
||||
void too_old() {
|
||||
Map<ChannelId, Coins> tooOld = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
SettledInvoiceJpaDto invoice = invoice(1, 1, tooOld);
|
||||
repository.save(invoice);
|
||||
long timestamp = invoice.getSettleDate();
|
||||
List<SettledInvoiceJpaDto> invoices =
|
||||
repository.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID.getShortChannelId(), timestamp);
|
||||
assertThat(invoices).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_one_of_two_channels_matches() {
|
||||
Map<ChannelId, Coins> expected = Map.of(CHANNEL_ID, Coins.ofSatoshis(100), CHANNEL_ID_2, Coins.ofSatoshis(50));
|
||||
repository.save(invoice(1, 1, expected));
|
||||
assertReceivedVia(CHANNEL_ID, List.of(expected));
|
||||
}
|
||||
@Test
|
||||
void self_payment() {
|
||||
Map<ChannelId, Coins> receivedVia = Map.of(CHANNEL_ID, Coins.ofSatoshis(100));
|
||||
SettledInvoiceJpaDto invoice = invoice(1, 1, receivedVia);
|
||||
paymentsRepository.save(payment(invoice.getHash()));
|
||||
repository.save(invoice);
|
||||
List<SettledInvoiceJpaDto> invoices =
|
||||
repository.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID.getShortChannelId(), 0);
|
||||
assertThat(invoices).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByReceivedViaChannelIdAndSettleDateAfter_several_invoices() {
|
||||
Map<ChannelId, Coins> expected1 = Map.of(CHANNEL_ID, Coins.ofSatoshis(100), CHANNEL_ID_2, Coins.ofSatoshis(50));
|
||||
Map<ChannelId, Coins> expected2 = Map.of(CHANNEL_ID_3, Coins.ofSatoshis(3), CHANNEL_ID_2, Coins.ofSatoshis(4));
|
||||
repository.save(invoice(1, 1, expected1));
|
||||
repository.save(invoice(2, 2, Map.of(CHANNEL_ID, Coins.ofSatoshis(1), CHANNEL_ID_3, Coins.ofSatoshis(2))));
|
||||
repository.save(invoice(3, 3, expected2));
|
||||
assertReceivedVia(CHANNEL_ID_2, List.of(expected1, expected2));
|
||||
}
|
||||
@Test
|
||||
void one_of_two_channels_matches() {
|
||||
Map<ChannelId, Coins> expected =
|
||||
Map.of(CHANNEL_ID, Coins.ofSatoshis(100), CHANNEL_ID_2, Coins.ofSatoshis(50));
|
||||
repository.save(invoice(1, 1, expected));
|
||||
assertReceivedVia(CHANNEL_ID, List.of(expected));
|
||||
}
|
||||
|
||||
private void assertReceivedVia(ChannelId channelId, List<Map<ChannelId, Coins>> expected) {
|
||||
assertThat(repository.findAllByReceivedViaChannelIdAndSettleDateAfter(channelId.getShortChannelId(), 0))
|
||||
.map(SettledInvoiceJpaDto::toModel)
|
||||
.map(SettledInvoice::receivedVia)
|
||||
.isEqualTo(expected);
|
||||
@Test
|
||||
void several_invoices() {
|
||||
Map<ChannelId, Coins> expected1 =
|
||||
Map.of(CHANNEL_ID, Coins.ofSatoshis(100), CHANNEL_ID_2, Coins.ofSatoshis(50));
|
||||
Map<ChannelId, Coins> expected2 =
|
||||
Map.of(CHANNEL_ID_3, Coins.ofSatoshis(3), CHANNEL_ID_2, Coins.ofSatoshis(4));
|
||||
repository.save(invoice(1, 1, expected1));
|
||||
repository.save(invoice(2, 2, Map.of(CHANNEL_ID, Coins.ofSatoshis(1), CHANNEL_ID_3, Coins.ofSatoshis(2))));
|
||||
repository.save(invoice(3, 3, expected2));
|
||||
assertReceivedVia(CHANNEL_ID_2, List.of(expected1, expected2));
|
||||
}
|
||||
|
||||
private void assertReceivedVia(ChannelId channelId, List<Map<ChannelId, Coins>> expected) {
|
||||
assertThat(repository.getInvoicesWithoutSelfPaymentsPaidVia(channelId.getShortChannelId(), 0))
|
||||
.map(SettledInvoiceJpaDto::toModel)
|
||||
.map(SettledInvoice::receivedVia)
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
private SettledInvoiceJpaDto invoice(int addIndex, int settleIndex) {
|
||||
@@ -144,4 +168,15 @@ class SettledInvoicesRepositoryIT {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private PaymentJpaDto payment(String hash) {
|
||||
return PaymentJpaDto.createFromModel(new Payment(
|
||||
0,
|
||||
hash,
|
||||
LocalDateTime.of(2020, 1, 2, 3, 4),
|
||||
Coins.NONE,
|
||||
Coins.NONE,
|
||||
List.of())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,5 @@ public interface SettledInvoicesDao {
|
||||
|
||||
long getSettleIndexOffset();
|
||||
|
||||
List<SettledInvoice> getInvoicesPaidVia(ChannelId channelId, Duration maxAge);
|
||||
List<SettledInvoice> getInvoicesWithoutSelfPaymentsPaidVia(ChannelId channelId, Duration maxAge);
|
||||
}
|
||||
|
||||
@@ -44,13 +44,11 @@ public class SettledInvoicesDaoImpl implements SettledInvoicesDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SettledInvoice> getInvoicesPaidVia(ChannelId channelId, Duration maxAge) {
|
||||
return repository.findAllByReceivedViaChannelIdAndSettleDateAfter(
|
||||
public List<SettledInvoice> getInvoicesWithoutSelfPaymentsPaidVia(ChannelId channelId, Duration maxAge) {
|
||||
return repository.getInvoicesWithoutSelfPaymentsPaidVia(
|
||||
channelId.getShortChannelId(),
|
||||
getAfterEpochMilliSeconds(maxAge)
|
||||
).stream()
|
||||
.map(SettledInvoiceJpaDto::toModel)
|
||||
.toList();
|
||||
).stream().map(SettledInvoiceJpaDto::toModel).toList();
|
||||
}
|
||||
|
||||
private long getAfterEpochMilliSeconds(Duration maxAge) {
|
||||
|
||||
@@ -13,5 +13,9 @@ public interface SettledInvoicesRepository extends JpaRepository<SettledInvoiceJ
|
||||
"i.settleIndex = (SELECT COUNT(j) FROM SettledInvoiceJpaDto j WHERE j.settleIndex <= i.settleIndex)")
|
||||
long getMaxSettledIndexWithoutGaps();
|
||||
|
||||
List<SettledInvoiceJpaDto> findAllByReceivedViaChannelIdAndSettleDateAfter(long channelId, long timestamp);
|
||||
@Query("SELECT s FROM SettledInvoiceJpaDto s " +
|
||||
"JOIN s.receivedVia v " +
|
||||
"LEFT JOIN PaymentJpaDto p ON (s.hash = p.hash) " +
|
||||
"WHERE s.settleDate > ?2 AND v.channelId = ?1 AND p IS NULL")
|
||||
List<SettledInvoiceJpaDto> getInvoicesWithoutSelfPaymentsPaidVia(long channelId, long timestamp);
|
||||
}
|
||||
|
||||
@@ -82,23 +82,23 @@ class SettledInvoicesDaoImplTest {
|
||||
|
||||
@Test
|
||||
void getInvoicesPaidVia_empty() {
|
||||
assertThat(dao.getInvoicesPaidVia(CHANNEL_ID, Duration.ofMinutes(1))).isEmpty();
|
||||
assertThat(dao.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID, Duration.ofMinutes(1))).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInvoicesPaidVia() {
|
||||
SettledInvoiceJpaDto jpaDto = SettledInvoiceJpaDto.createFromModel(SETTLED_INVOICE);
|
||||
when(repository.findAllByReceivedViaChannelIdAndSettleDateAfter(eq(CHANNEL_ID.getShortChannelId()), anyLong()))
|
||||
when(repository.getInvoicesWithoutSelfPaymentsPaidVia(eq(CHANNEL_ID.getShortChannelId()), anyLong()))
|
||||
.thenReturn(List.of(jpaDto));
|
||||
assertThat(dao.getInvoicesPaidVia(CHANNEL_ID, MAX_AGE)).containsExactly(SETTLED_INVOICE);
|
||||
assertThat(dao.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID, MAX_AGE)).containsExactly(SETTLED_INVOICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInvoicesPaidVia_with_max_age() {
|
||||
Duration maxAge = Duration.ofDays(17);
|
||||
long expectedTimestamp = Instant.now().minus(maxAge).getEpochSecond() * 1_000;
|
||||
dao.getInvoicesPaidVia(CHANNEL_ID, maxAge);
|
||||
verify(repository).findAllByReceivedViaChannelIdAndSettleDateAfter(
|
||||
dao.getInvoicesWithoutSelfPaymentsPaidVia(CHANNEL_ID, maxAge);
|
||||
verify(repository).getInvoicesWithoutSelfPaymentsPaidVia(
|
||||
anyLong(),
|
||||
longThat(timestamp -> Math.abs(expectedTimestamp - timestamp) <= 5_000)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user