Skip to content

Commit 7093157

Browse files
authored
Merge pull request #541 from synonymdev/fix/peer-unreachable
fix: add peer detail full address handling
2 parents f7d4523 + f01139a commit 7093157

File tree

2 files changed

+286
-1
lines changed

2 files changed

+286
-1
lines changed

app/src/main/java/to/bitkit/ext/PeerDetails.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,16 @@ fun PeerDetails.Companion.from(nodeId: String, host: String, port: String) = Pee
3838
)
3939

4040
fun ILspNode.toPeerDetails(): PeerDetails? {
41-
val address = connectionStrings.firstOrNull() ?: return null
41+
val connectionString = connectionStrings.firstOrNull() ?: return null
42+
43+
// Connection string can be either "host:port" or "nodeId@host:port"
44+
// Extract just the "host:port" part
45+
val address = if (connectionString.contains("@")) {
46+
connectionString.substringAfter("@")
47+
} else {
48+
connectionString
49+
}
50+
4251
return PeerDetails(
4352
nodeId = pubkey,
4453
address = address,
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
package to.bitkit.ext
2+
3+
import com.synonym.bitkitcore.ILspNode
4+
import org.junit.Test
5+
import org.lightningdevkit.ldknode.PeerDetails
6+
import to.bitkit.test.BaseUnitTest
7+
import kotlin.test.assertEquals
8+
import kotlin.test.assertFailsWith
9+
import kotlin.test.assertFalse
10+
import kotlin.test.assertNotNull
11+
import kotlin.test.assertNull
12+
import kotlin.test.assertTrue
13+
14+
class PeerDetailsTest : BaseUnitTest() {
15+
16+
@Test
17+
fun `host extension returns host part of address`() {
18+
val peer = PeerDetails(
19+
nodeId = "node123",
20+
address = "example.com:9735",
21+
isConnected = false,
22+
isPersisted = false,
23+
)
24+
25+
assertEquals("example.com", peer.host)
26+
}
27+
28+
@Test
29+
fun `port extension returns port part of address`() {
30+
val peer = PeerDetails(
31+
nodeId = "node123",
32+
address = "example.com:9735",
33+
isConnected = false,
34+
isPersisted = false,
35+
)
36+
37+
assertEquals("9735", peer.port)
38+
}
39+
40+
@Test
41+
fun `uri extension returns full connection string`() {
42+
val peer = PeerDetails(
43+
nodeId = "node123",
44+
address = "example.com:9735",
45+
isConnected = false,
46+
isPersisted = false,
47+
)
48+
49+
assertEquals("[email protected]:9735", peer.uri)
50+
}
51+
52+
@Test
53+
fun `parse correctly parses full connection string`() {
54+
val uri = "028a8910b0048630d4eb17af25668cdd7ea6f2d8ae20956e7a06e2ae46ebcb69fc@34.65.86.104:9400"
55+
56+
val peer = PeerDetails.parse(uri)
57+
58+
assertEquals("028a8910b0048630d4eb17af25668cdd7ea6f2d8ae20956e7a06e2ae46ebcb69fc", peer.nodeId)
59+
assertEquals("34.65.86.104:9400", peer.address)
60+
assertFalse(peer.isConnected)
61+
assertFalse(peer.isPersisted)
62+
}
63+
64+
@Test
65+
fun `parse throws on invalid uri without at symbol`() {
66+
val invalidUri = "node123example.com:9735"
67+
68+
val exception = assertFailsWith<IllegalArgumentException> {
69+
PeerDetails.parse(invalidUri)
70+
}
71+
72+
assertTrue(exception.message!!.contains("Invalid uri format"))
73+
}
74+
75+
@Test
76+
fun `parse throws on invalid uri without port`() {
77+
val invalidUri = "[email protected]"
78+
79+
val exception = assertFailsWith<IllegalArgumentException> {
80+
PeerDetails.parse(invalidUri)
81+
}
82+
83+
assertTrue(exception.message!!.contains("Invalid uri format"))
84+
}
85+
86+
@Test
87+
fun `from creates PeerDetails with correct values`() {
88+
val peer = PeerDetails.from(
89+
nodeId = "node123",
90+
host = "example.com",
91+
port = "9735",
92+
)
93+
94+
assertEquals("node123", peer.nodeId)
95+
assertEquals("example.com:9735", peer.address)
96+
assertFalse(peer.isConnected)
97+
assertFalse(peer.isPersisted)
98+
}
99+
100+
@Test
101+
fun `toPeerDetails handles connection string with host and port only`() {
102+
val lspNode = ILspNode(
103+
alias = "LSP1",
104+
pubkey = "node1pubkey",
105+
connectionStrings = listOf("node1.example.com:9735"),
106+
readonly = null,
107+
)
108+
109+
val peer = lspNode.toPeerDetails()
110+
111+
assertNotNull(peer)
112+
assertEquals("node1pubkey", peer.nodeId)
113+
assertEquals("node1.example.com:9735", peer.address)
114+
assertFalse(peer.isConnected)
115+
assertFalse(peer.isPersisted)
116+
}
117+
118+
@Test
119+
fun `toPeerDetails handles connection string with full uri format`() {
120+
val lspNode = ILspNode(
121+
alias = "LSP1",
122+
pubkey = "028a8910b0048630d4eb17af25668cdd7ea6f2d8ae20956e7a06e2ae46ebcb69fc",
123+
connectionStrings = listOf(
124+
"028a8910b0048630d4eb17af25668cdd7ea6f2d8ae20956e7a06e2ae46ebcb69fc@34.65.86.104:9400",
125+
),
126+
readonly = null,
127+
)
128+
129+
val peer = lspNode.toPeerDetails()
130+
131+
assertNotNull(peer)
132+
assertEquals("028a8910b0048630d4eb17af25668cdd7ea6f2d8ae20956e7a06e2ae46ebcb69fc", peer.nodeId)
133+
assertEquals("34.65.86.104:9400", peer.address)
134+
assertFalse(peer.isConnected)
135+
assertFalse(peer.isPersisted)
136+
}
137+
138+
@Test
139+
fun `toPeerDetails returns null when connectionStrings is empty`() {
140+
val lspNode = ILspNode(
141+
alias = "LSP1",
142+
pubkey = "node1pubkey",
143+
connectionStrings = emptyList(),
144+
readonly = null,
145+
)
146+
147+
val peer = lspNode.toPeerDetails()
148+
149+
assertNull(peer)
150+
}
151+
152+
@Test
153+
fun `toPeerDetails handles IP address in connection string`() {
154+
val lspNode = ILspNode(
155+
alias = "LSP1",
156+
pubkey = "node1pubkey",
157+
connectionStrings = listOf("127.0.0.1:9735"),
158+
readonly = null,
159+
)
160+
161+
val peer = lspNode.toPeerDetails()
162+
163+
assertNotNull(peer)
164+
assertEquals("node1pubkey", peer.nodeId)
165+
assertEquals("127.0.0.1:9735", peer.address)
166+
}
167+
168+
@Test
169+
fun `toPeerDetails handles IPv6 address in connection string`() {
170+
val lspNode = ILspNode(
171+
alias = "LSP1",
172+
pubkey = "node1pubkey",
173+
connectionStrings = listOf("[2001:db8::1]:9735"),
174+
readonly = null,
175+
)
176+
177+
val peer = lspNode.toPeerDetails()
178+
179+
assertNotNull(peer)
180+
assertEquals("node1pubkey", peer.nodeId)
181+
assertEquals("[2001:db8::1]:9735", peer.address)
182+
}
183+
184+
@Test
185+
fun `toPeerDetails takes first connection string when multiple provided`() {
186+
val lspNode = ILspNode(
187+
alias = "LSP1",
188+
pubkey = "node1pubkey",
189+
connectionStrings = listOf("primary.example.com:9735", "backup.example.com:9735"),
190+
readonly = null,
191+
)
192+
193+
val peer = lspNode.toPeerDetails()
194+
195+
assertNotNull(peer)
196+
assertEquals("primary.example.com:9735", peer.address)
197+
}
198+
199+
@Test
200+
fun `toPeerDetailsList converts all valid nodes`() {
201+
val lspNodes = listOf(
202+
ILspNode(
203+
alias = "LSP1",
204+
pubkey = "node1pubkey",
205+
connectionStrings = listOf("node1.example.com:9735"),
206+
readonly = null,
207+
),
208+
ILspNode(
209+
alias = "LSP2",
210+
pubkey = "node2pubkey",
211+
connectionStrings = listOf("[email protected]:9735"),
212+
readonly = null,
213+
),
214+
)
215+
216+
val peers = lspNodes.toPeerDetailsList()
217+
218+
assertEquals(2, peers.size)
219+
assertEquals("node1pubkey", peers[0].nodeId)
220+
assertEquals("node1.example.com:9735", peers[0].address)
221+
assertEquals("node2pubkey", peers[1].nodeId)
222+
assertEquals("node2.example.com:9735", peers[1].address)
223+
}
224+
225+
@Test
226+
fun `toPeerDetailsList filters out nodes with empty connectionStrings`() {
227+
val lspNodes = listOf(
228+
ILspNode(
229+
alias = "LSP1",
230+
pubkey = "node1pubkey",
231+
connectionStrings = listOf("node1.example.com:9735"),
232+
readonly = null,
233+
),
234+
ILspNode(
235+
alias = "LSP2",
236+
pubkey = "node2pubkey",
237+
connectionStrings = emptyList(),
238+
readonly = null,
239+
),
240+
ILspNode(
241+
alias = "LSP3",
242+
pubkey = "node3pubkey",
243+
connectionStrings = listOf("node3.example.com:9735"),
244+
readonly = null,
245+
),
246+
)
247+
248+
val peers = lspNodes.toPeerDetailsList()
249+
250+
assertEquals(2, peers.size)
251+
assertEquals("node1pubkey", peers[0].nodeId)
252+
assertEquals("node3pubkey", peers[1].nodeId)
253+
}
254+
255+
@Test
256+
fun `toPeerDetailsList returns empty list when all nodes have empty connectionStrings`() {
257+
val lspNodes = listOf(
258+
ILspNode(
259+
alias = "LSP1",
260+
pubkey = "node1pubkey",
261+
connectionStrings = emptyList(),
262+
readonly = null,
263+
),
264+
ILspNode(
265+
alias = "LSP2",
266+
pubkey = "node2pubkey",
267+
connectionStrings = emptyList(),
268+
readonly = null,
269+
),
270+
)
271+
272+
val peers = lspNodes.toPeerDetailsList()
273+
274+
assertTrue(peers.isEmpty())
275+
}
276+
}

0 commit comments

Comments
 (0)