Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

IrcClientTest.kt 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package com.dmdirc.ktirc
  2. import com.dmdirc.ktirc.events.IrcEvent
  3. import com.dmdirc.ktirc.events.ServerConnected
  4. import com.dmdirc.ktirc.events.ServerWelcome
  5. import com.dmdirc.ktirc.io.CaseMapping
  6. import com.dmdirc.ktirc.io.LineBufferedSocket
  7. import com.dmdirc.ktirc.model.Profile
  8. import com.dmdirc.ktirc.model.Server
  9. import com.dmdirc.ktirc.model.ServerFeature
  10. import com.dmdirc.ktirc.model.User
  11. import com.dmdirc.ktirc.util.currentTimeProvider
  12. import com.nhaarman.mockitokotlin2.*
  13. import kotlinx.coroutines.GlobalScope
  14. import kotlinx.coroutines.channels.Channel
  15. import kotlinx.coroutines.launch
  16. import kotlinx.coroutines.runBlocking
  17. import kotlinx.coroutines.withTimeoutOrNull
  18. import org.junit.jupiter.api.Assertions.*
  19. import org.junit.jupiter.api.BeforeEach
  20. import org.junit.jupiter.api.Test
  21. import org.junit.jupiter.api.assertThrows
  22. internal class IrcClientImplTest {
  23. companion object {
  24. private const val HOST = "thegibson.com"
  25. private const val PORT = 12345
  26. private const val NICK = "AcidBurn"
  27. private const val REAL_NAME = "Kate Libby"
  28. private const val USER_NAME = "acidb"
  29. private const val PASSWORD = "HackThePlanet"
  30. }
  31. private val readLineChannel = Channel<ByteArray>(10)
  32. private val mockSocket = mock<LineBufferedSocket> {
  33. on { readLines(any()) } doReturn readLineChannel
  34. }
  35. private val mockSocketFactory = mock<(String, Int, Boolean) -> LineBufferedSocket> {
  36. on { invoke(eq(HOST), eq(PORT), any()) } doReturn mockSocket
  37. }
  38. private val mockEventHandler = mock<(IrcEvent) -> Unit>()
  39. @BeforeEach
  40. fun setUp() {
  41. currentTimeProvider = { TestConstants.time }
  42. }
  43. @Test
  44. fun `IrcClientImpl uses socket factory to create a new socket on connect`() {
  45. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  46. client.socketFactory = mockSocketFactory
  47. client.connect()
  48. verify(mockSocketFactory, timeout(500)).invoke(HOST, PORT, false)
  49. }
  50. @Test
  51. fun `IrcClientImpl uses socket factory to create a new tls on connect`() {
  52. val client = IrcClientImpl(Server(HOST, PORT, true), Profile(NICK, REAL_NAME, USER_NAME))
  53. client.socketFactory = mockSocketFactory
  54. client.connect()
  55. verify(mockSocketFactory, timeout(500)).invoke(HOST, PORT, true)
  56. }
  57. @Test
  58. fun `IrcClientImpl throws if socket already exists`() {
  59. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  60. client.socketFactory = mockSocketFactory
  61. client.connect()
  62. assertThrows<IllegalStateException> {
  63. client.connect()
  64. }
  65. }
  66. @Test
  67. fun `IrcClientImpl emits connected event with local time`() = runBlocking {
  68. currentTimeProvider = { TestConstants.time }
  69. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  70. client.socketFactory = mockSocketFactory
  71. client.onEvent(mockEventHandler)
  72. client.connect()
  73. val captor = argumentCaptor<ServerConnected>()
  74. verify(mockEventHandler, timeout(500)).invoke(captor.capture())
  75. assertEquals(TestConstants.time, captor.firstValue.time)
  76. }
  77. @Test
  78. fun `IrcClientImpl sends basic connection strings`() = runBlocking {
  79. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  80. client.socketFactory = mockSocketFactory
  81. client.connect()
  82. with(inOrder(mockSocket).verify(mockSocket, timeout(500))) {
  83. sendLine("CAP LS 302")
  84. sendLine("NICK :$NICK")
  85. sendLine("USER $USER_NAME localhost $HOST :$REAL_NAME")
  86. }
  87. }
  88. @Test
  89. fun `IrcClientImpl sends password first, when present`() = runBlocking {
  90. val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
  91. client.socketFactory = mockSocketFactory
  92. client.connect()
  93. with(inOrder(mockSocket).verify(mockSocket, timeout(500))) {
  94. sendLine("CAP LS 302")
  95. sendLine("PASS :$PASSWORD")
  96. sendLine("NICK :$NICK")
  97. }
  98. }
  99. @Test
  100. fun `IrcClientImpl sends events to provided event handler`() {
  101. val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
  102. client.socketFactory = mockSocketFactory
  103. client.onEvent(mockEventHandler)
  104. GlobalScope.launch {
  105. readLineChannel.send(":the.gibson 001 acidBurn :Welcome to the IRC!".toByteArray())
  106. }
  107. client.connect()
  108. verify(mockEventHandler, timeout(500)).invoke(isA<ServerWelcome>())
  109. }
  110. @Test
  111. fun `IrcClient gets case mapping from server features`() {
  112. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  113. client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.RfcStrict
  114. assertEquals(CaseMapping.RfcStrict, client.caseMapping)
  115. }
  116. @Test
  117. fun `IrcClient indicates if user is local user or not`() {
  118. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  119. client.serverState.localNickname = "[acidBurn]"
  120. assertTrue(client.isLocalUser(User("{acidBurn}", "libby", "root.localhost")))
  121. assertFalse(client.isLocalUser(User("acid-Burn", "libby", "root.localhost")))
  122. }
  123. @Test
  124. fun `IrcClient uses current case mapping to check local user`() {
  125. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  126. client.serverState.localNickname = "[acidBurn]"
  127. client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.Ascii
  128. assertFalse(client.isLocalUser(User("{acidBurn}", "libby", "root.localhost")))
  129. }
  130. @Test
  131. fun `IrcClientImpl join blocks when socket is open`() {
  132. val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
  133. client.socketFactory = mockSocketFactory
  134. GlobalScope.launch {
  135. readLineChannel.send(":the.gibson 001 acidBurn :Welcome to the IRC!".toByteArray())
  136. }
  137. client.connect()
  138. runBlocking {
  139. assertNull(withTimeoutOrNull(100L) {
  140. client.join()
  141. true
  142. })
  143. }
  144. }
  145. @Test
  146. fun `IrcClientImpl join returns when socket is closed`() {
  147. val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
  148. client.socketFactory = mockSocketFactory
  149. GlobalScope.launch {
  150. readLineChannel.send(":the.gibson 001 acidBurn :Welcome to the IRC!".toByteArray())
  151. readLineChannel.close()
  152. }
  153. client.connect()
  154. runBlocking {
  155. assertEquals(true, withTimeoutOrNull(500L) {
  156. client.join()
  157. true
  158. })
  159. }
  160. }
  161. @Test
  162. fun `IrcClientImpl sends text to socket`() = runBlocking {
  163. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  164. client.socketFactory = mockSocketFactory
  165. client.connect()
  166. // Wait for it to connect
  167. verify(mockSocket, timeout(500)).sendLine("CAP LS 302")
  168. client.send("testing 123")
  169. verify(mockSocket, timeout(500)).sendLine("testing 123")
  170. }
  171. @Test
  172. fun `IrcClientImpl disconnects the socket`() = runBlocking {
  173. val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
  174. client.socketFactory = mockSocketFactory
  175. client.connect()
  176. // Wait for it to connect
  177. verify(mockSocket, timeout(500)).sendLine("CAP LS 302")
  178. client.disconnect()
  179. verify(mockSocket, timeout(500)).disconnect()
  180. }
  181. }