diff --git a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java index 4f97b30e..8f449f21 100644 --- a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java +++ b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java @@ -147,7 +147,7 @@ class LegacyControllerIT { } @Test - void getAvailableLocalBalance() throws Exception { + void getAvailableLocalBalance_channel() throws Exception { Coins availableBalance = Coins.ofSatoshis(999); when(balanceService.getAvailableLocalBalance(CHANNEL_ID)).thenReturn(availableBalance); mockMvc.perform(get(CHANNEL_BASE + "/available-local-balance")) @@ -155,10 +155,26 @@ class LegacyControllerIT { } @Test - void getAvailableRemoteBalance() throws Exception { + void getAvailableRemoteBalance_channel() throws Exception { Coins availableBalance = Coins.ofSatoshis(999); when(balanceService.getAvailableRemoteBalance(CHANNEL_ID)).thenReturn(availableBalance); mockMvc.perform(get(CHANNEL_BASE + "/available-remote-balance")) .andExpect(content().string(String.valueOf(availableBalance.satoshis()))); } + + @Test + void getAvailableLocalBalance_peer() throws Exception { + Coins availableBalance = Coins.ofSatoshis(999); + when(balanceService.getAvailableLocalBalance(PUBKEY)).thenReturn(availableBalance); + mockMvc.perform(get(PUBKEY_BASE + "/available-local-balance")) + .andExpect(content().string(String.valueOf(availableBalance.satoshis()))); + } + + @Test + void getAvailableRemoteBalance_peer() throws Exception { + Coins availableBalance = Coins.ofSatoshis(999); + when(balanceService.getAvailableRemoteBalance(PUBKEY)).thenReturn(availableBalance); + mockMvc.perform(get(PUBKEY_BASE + "/available-remote-balance")) + .andExpect(content().string(String.valueOf(availableBalance.satoshis()))); + } } \ No newline at end of file diff --git a/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java b/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java index 7e286158..d973ce2d 100644 --- a/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java +++ b/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java @@ -136,17 +136,29 @@ public class LegacyController { } @GetMapping("/channel/{channelId}/available-local-balance") - public long getAvailableLocalBalance(@PathVariable ChannelId channelId) { - mark("getAvailableLocalBalance"); + public long getAvailableLocalBalanceForChannel(@PathVariable ChannelId channelId) { + mark("getAvailableLocalBalanceForChannel"); return balanceService.getAvailableLocalBalance(channelId).satoshis(); } @GetMapping("/channel/{channelId}/available-remote-balance") - public long getAvailableRemoteBalance(@PathVariable ChannelId channelId) { - mark("getAvailableRemoteBalance"); + public long getAvailableRemoteBalanceForChannel(@PathVariable ChannelId channelId) { + mark("getAvailableRemoteBalanceForChannel"); return balanceService.getAvailableRemoteBalance(channelId).satoshis(); } + @GetMapping("/node/{pubkey}/available-local-balance") + public long getAvailableLocalBalanceForPeer(@PathVariable Pubkey pubkey) { + mark("getAvailableLocalBalanceForPeer"); + return balanceService.getAvailableLocalBalance(pubkey).satoshis(); + } + + @GetMapping("/node/{pubkey}/available-remote-balance") + public long getAvailableRemoteBalanceForPeer(@PathVariable Pubkey pubkey) { + mark("getAvailableRemoteBalanceForPeer"); + return balanceService.getAvailableRemoteBalance(pubkey).satoshis(); + } + private Stream getOpenChannelIdsSorted() { return channelService.getOpenChannels().stream() .map(Channel::getId) diff --git a/application/src/main/java/de/cotto/lndmanagej/service/BalanceService.java b/application/src/main/java/de/cotto/lndmanagej/service/BalanceService.java index fab1aec0..9c13161b 100644 --- a/application/src/main/java/de/cotto/lndmanagej/service/BalanceService.java +++ b/application/src/main/java/de/cotto/lndmanagej/service/BalanceService.java @@ -5,27 +5,49 @@ import de.cotto.lndmanagej.model.BalanceInformation; import de.cotto.lndmanagej.model.ChannelId; import de.cotto.lndmanagej.model.Coins; import de.cotto.lndmanagej.model.LocalChannel; +import de.cotto.lndmanagej.model.Pubkey; import org.springframework.stereotype.Component; +import java.util.Optional; + @Component public class BalanceService { private final GrpcChannels grpcChannels; + private final ChannelService channelService; - public BalanceService(GrpcChannels grpcChannels) { + public BalanceService(GrpcChannels grpcChannels, ChannelService channelService) { this.grpcChannels = grpcChannels; + this.channelService = channelService; + } + + public Coins getAvailableLocalBalance(Pubkey peer) { + return channelService.getOpenChannelsWith(peer).stream() + .map(LocalChannel::getId) + .map(this::getAvailableLocalBalance) + .reduce(Coins.NONE, Coins::add); } public Coins getAvailableLocalBalance(ChannelId channelId) { - return grpcChannels.getChannel(channelId) - .map(LocalChannel::getBalanceInformation) + return getBalanceInformation(channelId) .map(BalanceInformation::availableLocalBalance) .orElse(Coins.NONE); } + public Coins getAvailableRemoteBalance(Pubkey peer) { + return channelService.getOpenChannelsWith(peer).stream() + .map(LocalChannel::getId) + .map(this::getAvailableRemoteBalance) + .reduce(Coins.NONE, Coins::add); + } + public Coins getAvailableRemoteBalance(ChannelId channelId) { - return grpcChannels.getChannel(channelId) - .map(LocalChannel::getBalanceInformation) + return getBalanceInformation(channelId) .map(BalanceInformation::availableRemoteBalance) .orElse(Coins.NONE); } + + private Optional getBalanceInformation(ChannelId channelId) { + return grpcChannels.getChannel(channelId) + .map(LocalChannel::getBalanceInformation); + } } diff --git a/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java b/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java index ee15525f..57a4b67e 100644 --- a/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java @@ -199,16 +199,30 @@ class LegacyControllerTest { } @Test - void getAvailableLocalBalance() { + void getAvailableLocalBalance_channel() { when(balanceService.getAvailableLocalBalance(CHANNEL_ID)).thenReturn(Coins.ofSatoshis(123L)); - assertThat(legacyController.getAvailableLocalBalance(CHANNEL_ID)).isEqualTo(123); - verify(metrics).mark(argThat(name -> name.endsWith(".getAvailableLocalBalance"))); + assertThat(legacyController.getAvailableLocalBalanceForChannel(CHANNEL_ID)).isEqualTo(123); + verify(metrics).mark(argThat(name -> name.endsWith(".getAvailableLocalBalanceForChannel"))); } @Test - void getAvailableRemoteBalance() { + void getAvailableRemoteBalance_channel() { when(balanceService.getAvailableRemoteBalance(CHANNEL_ID)).thenReturn(Coins.ofSatoshis(123L)); - assertThat(legacyController.getAvailableRemoteBalance(CHANNEL_ID)).isEqualTo(123); - verify(metrics).mark(argThat(name -> name.endsWith(".getAvailableRemoteBalance"))); + assertThat(legacyController.getAvailableRemoteBalanceForChannel(CHANNEL_ID)).isEqualTo(123); + verify(metrics).mark(argThat(name -> name.endsWith(".getAvailableRemoteBalanceForChannel"))); + } + + @Test + void getAvailableLocalBalance_peer() { + when(balanceService.getAvailableLocalBalance(PUBKEY)).thenReturn(Coins.ofSatoshis(246L)); + assertThat(legacyController.getAvailableLocalBalanceForPeer(PUBKEY)).isEqualTo(246); + verify(metrics).mark(argThat(name -> name.endsWith(".getAvailableLocalBalanceForPeer"))); + } + + @Test + void getAvailableRemoteBalance_peer() { + when(balanceService.getAvailableRemoteBalance(PUBKEY)).thenReturn(Coins.ofSatoshis(246L)); + assertThat(legacyController.getAvailableRemoteBalanceForPeer(PUBKEY)).isEqualTo(246); + verify(metrics).mark(argThat(name -> name.endsWith(".getAvailableRemoteBalanceForPeer"))); } } \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/service/BalanceServiceTest.java b/application/src/test/java/de/cotto/lndmanagej/service/BalanceServiceTest.java index cc714dde..1471b84d 100644 --- a/application/src/test/java/de/cotto/lndmanagej/service/BalanceServiceTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/service/BalanceServiceTest.java @@ -9,9 +9,13 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Optional; +import java.util.Set; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.LocalChannelFixtures.LOCAL_CHANNEL; +import static de.cotto.lndmanagej.model.LocalChannelFixtures.LOCAL_CHANNEL_2; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @@ -23,25 +27,56 @@ class BalanceServiceTest { @Mock private GrpcChannels grpcChannels; + @Mock + private ChannelService channelService; + @Test - void getAvailableLocalBalance() { + void getAvailableLocalBalance_channel() { when(grpcChannels.getChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_CHANNEL)); assertThat(balanceService.getAvailableLocalBalance(CHANNEL_ID)).isEqualTo(Coins.ofSatoshis(900)); } @Test - void getAvailableLocalBalance_empty() { + void getAvailableLocalBalance_channel_empty() { assertThat(balanceService.getAvailableLocalBalance(CHANNEL_ID)).isEqualTo(Coins.NONE); } @Test - void getAvailableRemoteBalance() { + void getAvailableRemoteBalance_channel() { when(grpcChannels.getChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_CHANNEL)); assertThat(balanceService.getAvailableRemoteBalance(CHANNEL_ID)).isEqualTo(Coins.ofSatoshis(113)); } @Test - void getAvailableRemoteBalance_empty() { + void getAvailableRemoteBalance_channel_empty() { assertThat(balanceService.getAvailableRemoteBalance(CHANNEL_ID)).isEqualTo(Coins.NONE); } + + @Test + void getAvailableLocalBalance_peer() { + mockChannels(); + assertThat(balanceService.getAvailableLocalBalance(PUBKEY)).isEqualTo(Coins.ofSatoshis(1_800)); + } + + @Test + void getAvailableLocalBalance_peer_empty() { + assertThat(balanceService.getAvailableLocalBalance(PUBKEY)).isEqualTo(Coins.NONE); + } + + @Test + void getAvailableRemoteBalance_peer() { + mockChannels(); + assertThat(balanceService.getAvailableRemoteBalance(PUBKEY)).isEqualTo(Coins.ofSatoshis(226)); + } + + @Test + void getAvailableRemoteBalance_peer_empty() { + assertThat(balanceService.getAvailableRemoteBalance(PUBKEY)).isEqualTo(Coins.NONE); + } + + private void mockChannels() { + when(grpcChannels.getChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_CHANNEL)); + when(grpcChannels.getChannel(CHANNEL_ID_2)).thenReturn(Optional.of(LOCAL_CHANNEL_2)); + when(channelService.getOpenChannelsWith(PUBKEY)).thenReturn(Set.of(LOCAL_CHANNEL, LOCAL_CHANNEL_2)); + } } \ No newline at end of file