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 f4bdc4b4..81563e45 100644 --- a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java +++ b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java @@ -1,6 +1,7 @@ package de.cotto.lndmanagej.controller; import de.cotto.lndmanagej.service.ChannelService; +import de.cotto.lndmanagej.service.FeeService; import de.cotto.lndmanagej.service.NodeService; import de.cotto.lndmanagej.service.OwnNodeService; import org.junit.jupiter.api.Test; @@ -39,6 +40,9 @@ class LegacyControllerIT { @MockBean private OwnNodeService ownNodeService; + @MockBean + private FeeService feeService; + @Test void getAlias() throws Exception { when(nodeService.getAlias(PUBKEY)).thenReturn(ALIAS); @@ -75,4 +79,18 @@ class LegacyControllerIT { mockMvc.perform(get("/legacy/peer-pubkeys")) .andExpect(content().string(PUBKEY_2 + "\n" + PUBKEY_3)); } + + @Test + void getIncomingFeeRate() throws Exception { + when(feeService.getIncomingFeeRate(CHANNEL_ID)).thenReturn(123L); + mockMvc.perform(get("/legacy/channel/" + CHANNEL_ID + "/incoming-fee-rate")) + .andExpect(content().string("123")); + } + + @Test + void getOutgoingFeeRate() throws Exception { + when(feeService.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(123L); + mockMvc.perform(get("/legacy/channel/" + CHANNEL_ID + "/outgoing-fee-rate")) + .andExpect(content().string("123")); + } } \ No newline at end of file diff --git a/application/src/main/java/de/cotto/lndmanagej/controller/ChannelIdConverter.java b/application/src/main/java/de/cotto/lndmanagej/controller/ChannelIdConverter.java new file mode 100644 index 00000000..1ba40705 --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/controller/ChannelIdConverter.java @@ -0,0 +1,24 @@ +package de.cotto.lndmanagej.controller; + +import de.cotto.lndmanagej.model.ChannelId; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; + +@Component +public class ChannelIdConverter implements Converter { + public ChannelIdConverter() { + // default constructor + } + + @Override + public ChannelId convert(@Nonnull String source) { + try { + long shortChannelId = Long.parseLong(source); + return ChannelId.fromShortChannelId(shortChannelId); + } catch (NumberFormatException numberFormatException) { + return ChannelId.fromCompactForm(source); + } + } +} 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 56e3aa13..f9853b40 100644 --- a/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java +++ b/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java @@ -5,6 +5,7 @@ import de.cotto.lndmanagej.model.ChannelId; import de.cotto.lndmanagej.model.LocalChannel; import de.cotto.lndmanagej.model.Pubkey; import de.cotto.lndmanagej.service.ChannelService; +import de.cotto.lndmanagej.service.FeeService; import de.cotto.lndmanagej.service.NodeService; import de.cotto.lndmanagej.service.OwnNodeService; import org.springframework.web.bind.annotation.GetMapping; @@ -21,11 +22,18 @@ public class LegacyController { private final NodeService nodeService; private final ChannelService channelService; private final OwnNodeService ownNodeService; + private final FeeService feeService; - public LegacyController(NodeService nodeService, ChannelService channelService, OwnNodeService ownNodeService) { + public LegacyController( + NodeService nodeService, + ChannelService channelService, + OwnNodeService ownNodeService, + FeeService feeService + ) { this.nodeService = nodeService; this.channelService = channelService; this.ownNodeService = ownNodeService; + this.feeService = feeService; } @GetMapping("/node/{pubkey}/alias") @@ -56,4 +64,14 @@ public class LegacyController { public boolean syncedToChain() { return ownNodeService.isSyncedToChain(); } + + @GetMapping("/channel/{channelId}/incoming-fee-rate") + public long getIncomingFeeRate(@PathVariable ChannelId channelId) { + return feeService.getIncomingFeeRate(channelId); + } + + @GetMapping("/channel/{channelId}/outgoing-fee-rate") + public long getOutgoingFeeRate(@PathVariable ChannelId channelId) { + return feeService.getOutgoingFeeRate(channelId); + } } diff --git a/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java b/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java new file mode 100644 index 00000000..e2510a68 --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java @@ -0,0 +1,22 @@ +package de.cotto.lndmanagej.service; + +import de.cotto.lndmanagej.grpc.GrpcFees; +import de.cotto.lndmanagej.model.ChannelId; +import org.springframework.stereotype.Component; + +@Component +public class FeeService { + private final GrpcFees grpcFees; + + public FeeService(GrpcFees grpcFees) { + this.grpcFees = grpcFees; + } + + public long getIncomingFeeRate(ChannelId channelId) { + return grpcFees.getIncomingFeeRate(channelId).orElseThrow(IllegalStateException::new); + } + + public long getOutgoingFeeRate(ChannelId channelId) { + return grpcFees.getOutgoingFeeRate(channelId).orElseThrow(IllegalStateException::new); + } +} diff --git a/application/src/test/java/de/cotto/lndmanagej/controller/ChannelIdConverterTest.java b/application/src/test/java/de/cotto/lndmanagej/controller/ChannelIdConverterTest.java new file mode 100644 index 00000000..8a4c6477 --- /dev/null +++ b/application/src/test/java/de/cotto/lndmanagej/controller/ChannelIdConverterTest.java @@ -0,0 +1,23 @@ +package de.cotto.lndmanagej.controller; + +import org.junit.jupiter.api.Test; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.assertj.core.api.Assertions.assertThat; + +class ChannelIdConverterTest { + @Test + void convert() { + assertThat(new ChannelIdConverter().convert(CHANNEL_ID.toString())).isEqualTo(CHANNEL_ID); + } + + @Test + void convert_from_compact_form_with_x() { + assertThat(new ChannelIdConverter().convert("712345x123x1")).isEqualTo(CHANNEL_ID); + } + + @Test + void convert_from_compact_form() { + assertThat(new ChannelIdConverter().convert("712345:123:1")).isEqualTo(CHANNEL_ID); + } +} \ No newline at end of file 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 5b27a048..5205aa83 100644 --- a/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java @@ -3,6 +3,7 @@ package de.cotto.lndmanagej.controller; import de.cotto.lndmanagej.model.ChannelFixtures; import de.cotto.lndmanagej.model.LocalChannel; import de.cotto.lndmanagej.service.ChannelService; +import de.cotto.lndmanagej.service.FeeService; import de.cotto.lndmanagej.service.NodeService; import de.cotto.lndmanagej.service.OwnNodeService; import org.junit.jupiter.api.Test; @@ -40,6 +41,9 @@ class LegacyControllerTest { @Mock private OwnNodeService ownNodeService; + @Mock + private FeeService feeService; + @Test void getAlias() { when(nodeService.getAlias(PUBKEY)).thenReturn(ALIAS); @@ -93,4 +97,16 @@ class LegacyControllerTest { when(channelService.getOpenChannels()).thenReturn(Set.of(LOCAL_CHANNEL, LOCAL_CHANNEL_2)); assertThat(legacyController.getPeerPubkeys()).isEqualTo(PUBKEY_2.toString()); } + + @Test + void getIncomingFeeRate() { + when(feeService.getIncomingFeeRate(CHANNEL_ID)).thenReturn(123L); + assertThat(legacyController.getIncomingFeeRate(CHANNEL_ID)).isEqualTo(123); + } + + @Test + void getOutgoingFeeRate() { + when(feeService.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(123L); + assertThat(legacyController.getOutgoingFeeRate(CHANNEL_ID)).isEqualTo(123); + } } \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java b/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java new file mode 100644 index 00000000..409340f1 --- /dev/null +++ b/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java @@ -0,0 +1,48 @@ +package de.cotto.lndmanagej.service; + +import de.cotto.lndmanagej.grpc.GrpcFees; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class FeeServiceTest { + @InjectMocks + private FeeService feeService; + + @Mock + private GrpcFees grpcFees; + + @Test + void getIncomingFeeRate() { + when(grpcFees.getIncomingFeeRate(CHANNEL_ID)).thenReturn(Optional.of(123L)); + assertThat(feeService.getIncomingFeeRate(CHANNEL_ID)).isEqualTo(123); + } + + @Test + void getIncomingFeeRate_empty() { + when(grpcFees.getIncomingFeeRate(CHANNEL_ID)).thenReturn(Optional.empty()); + assertThatIllegalStateException().isThrownBy(() -> feeService.getIncomingFeeRate(CHANNEL_ID)); + } + + @Test + void getOutgoingFeeRate() { + when(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(Optional.of(123L)); + assertThat(feeService.getOutgoingFeeRate(CHANNEL_ID)).isEqualTo(123); + } + + @Test + void getOutgoingFeeRate_empty() { + when(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(Optional.empty()); + assertThatIllegalStateException().isThrownBy(() -> feeService.getOutgoingFeeRate(CHANNEL_ID)); + } +} \ No newline at end of file diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java new file mode 100644 index 00000000..cd68b74f --- /dev/null +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java @@ -0,0 +1,49 @@ +package de.cotto.lndmanagej.grpc; + +import de.cotto.lndmanagej.model.ChannelId; +import lnrpc.RoutingPolicy; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class GrpcChannelPolicy { + private final GrpcService grpcService; + private final GrpcGetInfo grpcGetInfo; + + public GrpcChannelPolicy(GrpcService grpcService, GrpcGetInfo grpcGetInfo) { + this.grpcService = grpcService; + this.grpcGetInfo = grpcGetInfo; + } + + public Optional getLocalPolicy(ChannelId channelId) { + String ownPubkey = grpcGetInfo.getPubkey().toString(); + return grpcService.getChannelEdge(channelId).map( + channelEdge -> { + if (ownPubkey.equals(channelEdge.getNode1Pub())) { + return channelEdge.getNode1Policy(); + } else if (ownPubkey.equals(channelEdge.getNode2Pub())) { + return channelEdge.getNode2Policy(); + } else { + return null; + } + } + ); + } + + public Optional getRemotePolicy(ChannelId channelId) { + String ownPubkey = grpcGetInfo.getPubkey().toString(); + return grpcService.getChannelEdge(channelId).map( + channelEdge -> { + if (ownPubkey.equals(channelEdge.getNode2Pub())) { + return channelEdge.getNode1Policy(); + } else if (ownPubkey.equals(channelEdge.getNode1Pub())) { + return channelEdge.getNode2Policy(); + } else { + return null; + } + } + ); + } + +} diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java index 7fd4de0d..d813b3c4 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java @@ -25,7 +25,7 @@ public class GrpcChannels { } public Set getChannels() { - Pubkey ownPubkey = grpcGetInfo.getPubkey().orElseThrow(); + Pubkey ownPubkey = grpcGetInfo.getPubkey(); return grpcService.getChannels().stream() .map(lndChannel -> toChannel(lndChannel, ownPubkey)) .collect(toSet()); diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java new file mode 100644 index 00000000..97e4bf03 --- /dev/null +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java @@ -0,0 +1,24 @@ +package de.cotto.lndmanagej.grpc; + +import de.cotto.lndmanagej.model.ChannelId; +import lnrpc.RoutingPolicy; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class GrpcFees { + private final GrpcChannelPolicy grpcChannelPolicy; + + public GrpcFees(GrpcChannelPolicy grpcChannelPolicy) { + this.grpcChannelPolicy = grpcChannelPolicy; + } + + public Optional getOutgoingFeeRate(ChannelId channelId) { + return grpcChannelPolicy.getLocalPolicy(channelId).map(RoutingPolicy::getFeeRateMilliMsat); + } + + public Optional getIncomingFeeRate(ChannelId channelId) { + return grpcChannelPolicy.getRemotePolicy(channelId).map(RoutingPolicy::getFeeRateMilliMsat); + } +} diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java index 18602f76..a922830e 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java @@ -4,19 +4,27 @@ import de.cotto.lndmanagej.model.Pubkey; import lnrpc.GetInfoResponse; import org.springframework.stereotype.Component; +import javax.annotation.Nullable; import java.time.Instant; +import java.util.Objects; import java.util.Optional; @Component public class GrpcGetInfo { private final GrpcService grpcService; + @Nullable + private Pubkey pubkey; + public GrpcGetInfo(GrpcService grpcService) { this.grpcService = grpcService; } - public Optional getPubkey() { - return grpcService.getInfo().map(GetInfoResponse::getIdentityPubkey).map(Pubkey::create); + public Pubkey getPubkey() { + if (pubkey == null) { + pubkey = grpcService.getInfo().map(GetInfoResponse::getIdentityPubkey).map(Pubkey::create).orElseThrow(); + } + return Objects.requireNonNull(pubkey); } public Optional getAlias() { diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java index d0ba0f1b..949ff522 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java @@ -1,9 +1,12 @@ package de.cotto.lndmanagej.grpc; import de.cotto.lndmanagej.LndConfiguration; +import de.cotto.lndmanagej.model.ChannelId; import de.cotto.lndmanagej.model.Pubkey; import io.grpc.StatusRuntimeException; +import lnrpc.ChanInfoRequest; import lnrpc.Channel; +import lnrpc.ChannelEdge; import lnrpc.GetInfoResponse; import lnrpc.LightningGrpc; import lnrpc.ListChannelsRequest; @@ -74,4 +77,9 @@ public class GrpcService { public Optional getNodeInfo(Pubkey pubkey) { return get(() -> lightningStub.getNodeInfo(NodeInfoRequest.newBuilder().setPubKey(pubkey.toString()).build())); } + + public Optional getChannelEdge(ChannelId channelId) { + ChanInfoRequest build = ChanInfoRequest.newBuilder().setChanId(channelId.shortChannelId()).build(); + return get(() -> lightningStub.getChanInfo(build)); + } } diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java new file mode 100644 index 00000000..8c558f25 --- /dev/null +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java @@ -0,0 +1,99 @@ +package de.cotto.lndmanagej.grpc; + +import de.cotto.lndmanagej.model.Pubkey; +import lnrpc.ChannelEdge; +import lnrpc.RoutingPolicy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class GrpcChannelPolicyTest { + private static final int FEE_RATE_FIRST = 123; + private static final int FEE_RATE_SECOND = 456; + + @InjectMocks + private GrpcChannelPolicy grpcChannelPolicy; + + @Mock + private GrpcService grpcService; + + @Mock + private GrpcGetInfo grpcGetInfo; + + @BeforeEach + void setUp() { + when(grpcGetInfo.getPubkey()).thenReturn(PUBKEY); + } + + @Test + void getLocalPolicy_local_first() { + when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2))); + assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).contains(routingPolicy(FEE_RATE_FIRST)); + } + + @Test + void getLocalPolicy_local_second() { + when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY))); + assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).contains(routingPolicy(FEE_RATE_SECOND)); + } + + @Test + void getLocalPolicy_not_local() { + when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); + assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).isEmpty(); + } + + @Test + void getLocalPolicy_empty() { + assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).isEmpty(); + } + + @Test + void getRemotePolicy_local_first() { + when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2))); + assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).contains(routingPolicy(FEE_RATE_SECOND)); + } + + @Test + void getRemotePolicy_local_second() { + when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY))); + assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).contains(routingPolicy(FEE_RATE_FIRST)); + } + + @Test + void getRemotePolicy_not_local() { + when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); + assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).isEmpty(); + } + + @Test + void getRemotePolicy_empty() { + assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).isEmpty(); + } + + private ChannelEdge channelEdge(Pubkey firstPubkey, Pubkey secondPubkey) { + return ChannelEdge.newBuilder() + .setNode1Pub(firstPubkey.toString()) + .setNode2Pub(secondPubkey.toString()) + .setNode1Policy(routingPolicy(FEE_RATE_FIRST)) + .setNode2Policy(routingPolicy(FEE_RATE_SECOND)) + .build(); + } + + private RoutingPolicy routingPolicy(int feeRate) { + return RoutingPolicy.newBuilder().setFeeRateMilliMsat(feeRate).build(); + } +} \ No newline at end of file diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java index 7e980619..ff24980a 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java @@ -9,7 +9,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.List; -import java.util.Optional; import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; @@ -32,7 +31,7 @@ class GrpcChannelsTest { @BeforeEach void setUp() { - when(grpcGetInfo.getPubkey()).thenReturn(Optional.of(PUBKEY)); + when(grpcGetInfo.getPubkey()).thenReturn(PUBKEY); } @Test diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java new file mode 100644 index 00000000..8661fac6 --- /dev/null +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java @@ -0,0 +1,50 @@ +package de.cotto.lndmanagej.grpc; + +import lnrpc.RoutingPolicy; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class GrpcFeesTest { + @InjectMocks + private GrpcFees grpcFees; + + @Mock + private GrpcChannelPolicy grpcChannelPolicy; + + @Test + void getOutgoingFeeRate() { + when(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).thenReturn(Optional.of(routingPolicy())); + assertThat(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).contains(123L); + } + + @Test + void getOutgoingFeeRate_empty() { + assertThat(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).isEmpty(); + } + + @Test + void getIncomingFeeRate() { + when(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).thenReturn(Optional.of(routingPolicy())); + assertThat(grpcFees.getIncomingFeeRate(CHANNEL_ID)).contains(123L); + } + + @Test + void getIncomingFeeRate_empty() { + assertThat(grpcFees.getIncomingFeeRate(CHANNEL_ID)).isEmpty(); + } + + private RoutingPolicy routingPolicy() { + return RoutingPolicy.newBuilder().setFeeRateMilliMsat(123).build(); + } + +} \ No newline at end of file diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java index f1cfc67c..3e7e0025 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java @@ -55,7 +55,14 @@ class GrpcGetInfoTest { @Test void getPubkey() { - assertThat(grpcGetInfo.getPubkey()).contains(PUBKEY); + assertThat(grpcGetInfo.getPubkey()).isEqualTo(PUBKEY); + } + + @Test + void getPubkey_cached() { + grpcGetInfo.getPubkey(); + when(grpcService.getInfo()).thenReturn(Optional.empty()); + assertThat(grpcGetInfo.getPubkey()).isEqualTo(PUBKEY); } @Test