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