|
@@ -17,6 +17,7 @@ import org.junit.jupiter.api.Assertions.*
|
17
|
17
|
import org.junit.jupiter.api.BeforeEach
|
18
|
18
|
import org.junit.jupiter.api.Test
|
19
|
19
|
import org.junit.jupiter.api.assertThrows
|
|
20
|
+import java.net.UnknownHostException
|
20
|
21
|
import java.nio.channels.UnresolvedAddressException
|
21
|
22
|
import java.security.cert.CertificateException
|
22
|
23
|
import java.util.concurrent.atomic.AtomicReference
|
|
@@ -26,6 +27,8 @@ internal class IrcClientImplTest {
|
26
|
27
|
|
27
|
28
|
companion object {
|
28
|
29
|
private const val HOST = "thegibson.com"
|
|
30
|
+ private const val HOST2 = "irc.thegibson.com"
|
|
31
|
+ private const val IP = "127.0.13.37"
|
29
|
32
|
private const val PORT = 12345
|
30
|
33
|
private const val NICK = "AcidBurn"
|
31
|
34
|
private const val REAL_NAME = "Kate Libby"
|
|
@@ -41,8 +44,13 @@ internal class IrcClientImplTest {
|
41
|
44
|
every { sendChannel } returns sendLineChannel
|
42
|
45
|
}
|
43
|
46
|
|
44
|
|
- private val mockSocketFactory = mockk<(CoroutineScope, String, Int, Boolean) -> LineBufferedSocket> {
|
45
|
|
- every { this@mockk.invoke(any(), eq(HOST), eq(PORT), any()) } returns mockSocket
|
|
47
|
+ private val mockSocketFactory = mockk<(CoroutineScope, String, String, Int, Boolean) -> LineBufferedSocket> {
|
|
48
|
+ every { this@mockk.invoke(any(), eq(HOST), eq(IP), eq(PORT), any()) } returns mockSocket
|
|
49
|
+ every { this@mockk.invoke(any(), eq(HOST2), any(), eq(PORT), any()) } returns mockSocket
|
|
50
|
+ }
|
|
51
|
+
|
|
52
|
+ private val mockResolver = mockk<(String) -> Collection<ResolveResult>> {
|
|
53
|
+ every { this@mockk.invoke(HOST) } returns listOf(ResolveResult(IP, false))
|
46
|
54
|
}
|
47
|
55
|
|
48
|
56
|
private val mockEventHandler = mockk<(IrcEvent) -> Unit> {
|
|
@@ -58,6 +66,7 @@ internal class IrcClientImplTest {
|
58
|
66
|
private val serverConfig = ServerConfig().apply {
|
59
|
67
|
host = HOST
|
60
|
68
|
port = PORT
|
|
69
|
+ useTls = false
|
61
|
70
|
}
|
62
|
71
|
|
63
|
72
|
private val normalConfig = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig(), null)
|
|
@@ -71,9 +80,10 @@ internal class IrcClientImplTest {
|
71
|
80
|
fun `uses socket factory to create a new socket on connect`() {
|
72
|
81
|
val client = IrcClientImpl(normalConfig)
|
73
|
82
|
client.socketFactory = mockSocketFactory
|
|
83
|
+ client.resolver = mockResolver
|
74
|
84
|
client.connect()
|
75
|
85
|
|
76
|
|
- verify(timeout = 500) { mockSocketFactory(client, HOST, PORT, false) }
|
|
86
|
+ verify(timeout = 500) { mockSocketFactory(client, HOST, IP, PORT, false) }
|
77
|
87
|
}
|
78
|
88
|
|
79
|
89
|
@Test
|
|
@@ -84,15 +94,111 @@ internal class IrcClientImplTest {
|
84
|
94
|
useTls = true
|
85
|
95
|
}, profileConfig, BehaviourConfig(), null))
|
86
|
96
|
client.socketFactory = mockSocketFactory
|
|
97
|
+ client.resolver = mockResolver
|
|
98
|
+ client.connect()
|
|
99
|
+
|
|
100
|
+ verify(timeout = 500) { mockSocketFactory(client, HOST, IP, PORT, true) }
|
|
101
|
+ }
|
|
102
|
+
|
|
103
|
+ @Test
|
|
104
|
+ fun `prefers ipv6 addresses if behaviour is enabled`() {
|
|
105
|
+ val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
|
|
106
|
+ host = HOST2
|
|
107
|
+ port = PORT
|
|
108
|
+ }, profileConfig, BehaviourConfig().apply { preferIPv6 = true }, null))
|
|
109
|
+
|
|
110
|
+ every { mockResolver(HOST2) } returns listOf(
|
|
111
|
+ ResolveResult(IP, false),
|
|
112
|
+ ResolveResult("::13:37", true),
|
|
113
|
+ ResolveResult("0.0.0.0", false)
|
|
114
|
+ )
|
|
115
|
+
|
|
116
|
+ client.socketFactory = mockSocketFactory
|
|
117
|
+ client.resolver = mockResolver
|
|
118
|
+ client.connect()
|
|
119
|
+
|
|
120
|
+ verify(timeout = 500) { mockSocketFactory(client, HOST2, "::13:37", PORT, true) }
|
|
121
|
+ }
|
|
122
|
+
|
|
123
|
+ @Test
|
|
124
|
+ fun `falls back to ipv4 if no ipv6 addresses are available`() {
|
|
125
|
+ val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
|
|
126
|
+ host = HOST2
|
|
127
|
+ port = PORT
|
|
128
|
+ }, profileConfig, BehaviourConfig().apply { preferIPv6 = true }, null))
|
|
129
|
+
|
|
130
|
+ every { mockResolver(HOST2) } returns listOf(
|
|
131
|
+ ResolveResult("0.0.0.0", false)
|
|
132
|
+ )
|
|
133
|
+
|
|
134
|
+ client.socketFactory = mockSocketFactory
|
|
135
|
+ client.resolver = mockResolver
|
87
|
136
|
client.connect()
|
88
|
137
|
|
89
|
|
- verify(timeout = 500) { mockSocketFactory(client, HOST, PORT, true) }
|
|
138
|
+ verify(timeout = 500) { mockSocketFactory(client, HOST2, "0.0.0.0", PORT, true) }
|
|
139
|
+ }
|
|
140
|
+
|
|
141
|
+ @Test
|
|
142
|
+ fun `prefers ipv4 addresses if ipv6 behaviour is disabled`() {
|
|
143
|
+ val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
|
|
144
|
+ host = HOST2
|
|
145
|
+ port = PORT
|
|
146
|
+ }, profileConfig, BehaviourConfig().apply { preferIPv6 = false }, null))
|
|
147
|
+
|
|
148
|
+ every { mockResolver(HOST2) } returns listOf(
|
|
149
|
+ ResolveResult("::13:37", true),
|
|
150
|
+ ResolveResult("::313:37", true),
|
|
151
|
+ ResolveResult("0.0.0.0", false)
|
|
152
|
+ )
|
|
153
|
+
|
|
154
|
+ client.socketFactory = mockSocketFactory
|
|
155
|
+ client.resolver = mockResolver
|
|
156
|
+ client.connect()
|
|
157
|
+
|
|
158
|
+ verify(timeout = 500) { mockSocketFactory(client, HOST2, "0.0.0.0", PORT, true) }
|
|
159
|
+ }
|
|
160
|
+
|
|
161
|
+ @Test
|
|
162
|
+ fun `falls back to ipv6 if no ipv4 addresses available`() {
|
|
163
|
+ val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
|
|
164
|
+ host = HOST2
|
|
165
|
+ port = PORT
|
|
166
|
+ }, profileConfig, BehaviourConfig().apply { preferIPv6 = false }, null))
|
|
167
|
+
|
|
168
|
+ every { mockResolver(HOST2) } returns listOf(
|
|
169
|
+ ResolveResult("::13:37", true)
|
|
170
|
+ )
|
|
171
|
+
|
|
172
|
+ client.socketFactory = mockSocketFactory
|
|
173
|
+ client.resolver = mockResolver
|
|
174
|
+ client.connect()
|
|
175
|
+
|
|
176
|
+ verify(timeout = 500) { mockSocketFactory(client, HOST2, "::13:37", PORT, true) }
|
|
177
|
+ }
|
|
178
|
+
|
|
179
|
+ @Test
|
|
180
|
+ fun `raises error if dns fails`() {
|
|
181
|
+ val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
|
|
182
|
+ host = HOST2
|
|
183
|
+ }, profileConfig, BehaviourConfig().apply { preferIPv6 = true }, null))
|
|
184
|
+
|
|
185
|
+ every { mockResolver(HOST2) } throws UnknownHostException("oops")
|
|
186
|
+
|
|
187
|
+ client.socketFactory = mockSocketFactory
|
|
188
|
+ client.resolver = mockResolver
|
|
189
|
+ client.onEvent(mockEventHandler)
|
|
190
|
+ client.connect()
|
|
191
|
+
|
|
192
|
+ verify(timeout = 500) {
|
|
193
|
+ mockEventHandler(match { it is ServerConnectionError && it.error == ConnectionError.UnresolvableAddress })
|
|
194
|
+ }
|
90
|
195
|
}
|
91
|
196
|
|
92
|
197
|
@Test
|
93
|
198
|
fun `throws if socket already exists`() {
|
94
|
199
|
val client = IrcClientImpl(normalConfig)
|
95
|
200
|
client.socketFactory = mockSocketFactory
|
|
201
|
+ client.resolver = mockResolver
|
96
|
202
|
client.connect()
|
97
|
203
|
|
98
|
204
|
assertThrows<IllegalStateException> {
|
|
@@ -112,6 +218,7 @@ internal class IrcClientImplTest {
|
112
|
218
|
|
113
|
219
|
val client = IrcClientImpl(normalConfig)
|
114
|
220
|
client.socketFactory = mockSocketFactory
|
|
221
|
+ client.resolver = mockResolver
|
115
|
222
|
client.onEvent(mockEventHandler)
|
116
|
223
|
client.connect()
|
117
|
224
|
|
|
@@ -128,6 +235,7 @@ internal class IrcClientImplTest {
|
128
|
235
|
fun `sends basic connection strings`() = runBlocking {
|
129
|
236
|
val client = IrcClientImpl(normalConfig)
|
130
|
237
|
client.socketFactory = mockSocketFactory
|
|
238
|
+ client.resolver = mockResolver
|
131
|
239
|
client.connect()
|
132
|
240
|
|
133
|
241
|
assertEquals("CAP LS 302", String(sendLineChannel.receive()))
|
|
@@ -143,6 +251,7 @@ internal class IrcClientImplTest {
|
143
|
251
|
password = PASSWORD
|
144
|
252
|
}, profileConfig, BehaviourConfig(), null))
|
145
|
253
|
client.socketFactory = mockSocketFactory
|
|
254
|
+ client.resolver = mockResolver
|
146
|
255
|
client.connect()
|
147
|
256
|
|
148
|
257
|
assertEquals("CAP LS 302", String(sendLineChannel.receive()))
|
|
@@ -153,6 +262,7 @@ internal class IrcClientImplTest {
|
153
|
262
|
fun `sends events to provided event handler`() {
|
154
|
263
|
val client = IrcClientImpl(normalConfig)
|
155
|
264
|
client.socketFactory = mockSocketFactory
|
|
265
|
+ client.resolver = mockResolver
|
156
|
266
|
client.onEvent(mockEventHandler)
|
157
|
267
|
|
158
|
268
|
GlobalScope.launch {
|
|
@@ -204,6 +314,7 @@ internal class IrcClientImplTest {
|
204
|
314
|
fun `sends text to socket`() = runBlocking {
|
205
|
315
|
val client = IrcClientImpl(normalConfig)
|
206
|
316
|
client.socketFactory = mockSocketFactory
|
|
317
|
+ client.resolver = mockResolver
|
207
|
318
|
client.connect()
|
208
|
319
|
|
209
|
320
|
client.send("testing 123")
|
|
@@ -215,6 +326,7 @@ internal class IrcClientImplTest {
|
215
|
326
|
fun `sends structured text to socket`() = runBlocking {
|
216
|
327
|
val client = IrcClientImpl(normalConfig)
|
217
|
328
|
client.socketFactory = mockSocketFactory
|
|
329
|
+ client.resolver = mockResolver
|
218
|
330
|
client.connect()
|
219
|
331
|
|
220
|
332
|
client.send("testing", "123", "456")
|
|
@@ -227,6 +339,7 @@ internal class IrcClientImplTest {
|
227
|
339
|
val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = true }, null)
|
228
|
340
|
val client = IrcClientImpl(config)
|
229
|
341
|
client.socketFactory = mockSocketFactory
|
|
342
|
+ client.resolver = mockResolver
|
230
|
343
|
|
231
|
344
|
val slot = slot<MessageReceived>()
|
232
|
345
|
val mockkEventHandler = mockk<(IrcEvent) -> Unit>(relaxed = true)
|
|
@@ -250,6 +363,7 @@ internal class IrcClientImplTest {
|
250
|
363
|
val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = true }, null)
|
251
|
364
|
val client = IrcClientImpl(config)
|
252
|
365
|
client.socketFactory = mockSocketFactory
|
|
366
|
+ client.resolver = mockResolver
|
253
|
367
|
client.serverState.capabilities.enabledCapabilities[Capability.EchoMessages] = ""
|
254
|
368
|
client.connect()
|
255
|
369
|
|
|
@@ -266,6 +380,7 @@ internal class IrcClientImplTest {
|
266
|
380
|
val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = false }, null)
|
267
|
381
|
val client = IrcClientImpl(config)
|
268
|
382
|
client.socketFactory = mockSocketFactory
|
|
383
|
+ client.resolver = mockResolver
|
269
|
384
|
client.connect()
|
270
|
385
|
|
271
|
386
|
client.onEvent(mockEventHandler)
|
|
@@ -273,12 +388,14 @@ internal class IrcClientImplTest {
|
273
|
388
|
|
274
|
389
|
verify(inverse = true) {
|
275
|
390
|
mockEventHandler(ofType<MessageReceived>())
|
276
|
|
- } }
|
|
391
|
+ }
|
|
392
|
+ }
|
277
|
393
|
|
278
|
394
|
@Test
|
279
|
395
|
fun `sends structured text to socket with tags`() = runBlocking {
|
280
|
396
|
val client = IrcClientImpl(normalConfig)
|
281
|
397
|
client.socketFactory = mockSocketFactory
|
|
398
|
+ client.resolver = mockResolver
|
282
|
399
|
client.connect()
|
283
|
400
|
|
284
|
401
|
client.send(tagMap(MessageTag.AccountName to "acidB"), "testing", "123", "456")
|
|
@@ -290,6 +407,8 @@ internal class IrcClientImplTest {
|
290
|
407
|
fun `asynchronously sends text to socket without label if cap is missing`() = runBlocking {
|
291
|
408
|
val client = IrcClientImpl(normalConfig)
|
292
|
409
|
client.socketFactory = mockSocketFactory
|
|
410
|
+ client.resolver = mockResolver
|
|
411
|
+
|
293
|
412
|
client.connect()
|
294
|
413
|
|
295
|
414
|
client.sendAsync(tagMap(), "testing", arrayOf("123")) { false }
|
|
@@ -302,6 +421,8 @@ internal class IrcClientImplTest {
|
302
|
421
|
generateLabel = { "abc123" }
|
303
|
422
|
val client = IrcClientImpl(normalConfig)
|
304
|
423
|
client.socketFactory = mockSocketFactory
|
|
424
|
+ client.resolver = mockResolver
|
|
425
|
+
|
305
|
426
|
client.serverState.capabilities.enabledCapabilities[Capability.LabeledResponse] = ""
|
306
|
427
|
client.connect()
|
307
|
428
|
|
|
@@ -315,6 +436,8 @@ internal class IrcClientImplTest {
|
315
|
436
|
generateLabel = { "abc123" }
|
316
|
437
|
val client = IrcClientImpl(normalConfig)
|
317
|
438
|
client.socketFactory = mockSocketFactory
|
|
439
|
+ client.resolver = mockResolver
|
|
440
|
+
|
318
|
441
|
client.serverState.capabilities.enabledCapabilities[Capability.LabeledResponse] = ""
|
319
|
442
|
client.connect()
|
320
|
443
|
|
|
@@ -327,6 +450,7 @@ internal class IrcClientImplTest {
|
327
|
450
|
fun `disconnects the socket`() = runBlocking {
|
328
|
451
|
val client = IrcClientImpl(normalConfig)
|
329
|
452
|
client.socketFactory = mockSocketFactory
|
|
453
|
+ client.resolver = mockResolver
|
330
|
454
|
client.connect()
|
331
|
455
|
|
332
|
456
|
client.disconnect()
|
|
@@ -341,6 +465,7 @@ internal class IrcClientImplTest {
|
341
|
465
|
fun `sends messages in order`() = runBlocking {
|
342
|
466
|
val client = IrcClientImpl(normalConfig)
|
343
|
467
|
client.socketFactory = mockSocketFactory
|
|
468
|
+ client.resolver = mockResolver
|
344
|
469
|
client.connect()
|
345
|
470
|
|
346
|
471
|
(0..100).forEach { client.send("TEST", "$it") }
|
|
@@ -399,6 +524,7 @@ internal class IrcClientImplTest {
|
399
|
524
|
every { mockSocket.connect() } throws UnresolvedAddressException()
|
400
|
525
|
with(IrcClientImpl(normalConfig)) {
|
401
|
526
|
socketFactory = mockSocketFactory
|
|
527
|
+ resolver = mockResolver
|
402
|
528
|
withTimeout(500) {
|
403
|
529
|
launch {
|
404
|
530
|
delay(50)
|
|
@@ -415,6 +541,7 @@ internal class IrcClientImplTest {
|
415
|
541
|
every { mockSocket.connect() } throws CertificateException("Boooo")
|
416
|
542
|
with(IrcClientImpl(normalConfig)) {
|
417
|
543
|
socketFactory = mockSocketFactory
|
|
544
|
+ resolver = mockResolver
|
418
|
545
|
withTimeout(500) {
|
419
|
546
|
launch {
|
420
|
547
|
delay(50)
|