From 760a81b5ce1a81cf0e041a06828062c3787abaff Mon Sep 17 00:00:00 2001 From: Daniel Rees Date: Wed, 19 Jun 2019 11:38:48 -0400 Subject: [PATCH 1/2] Converted all tests to Junit5 --- build.gradle | 3 - .../org/phoenixframework/ChannelTest.kt | 1536 +++++++++-------- .../org/phoenixframework/ExampleTest.kt | 25 - .../org/phoenixframework/MessageTest.kt | 28 +- .../org/phoenixframework/PresenceTest.kt | 749 ++++---- .../kotlin/org/phoenixframework/SocketTest.kt | 1095 ++++++------ .../org/phoenixframework/TimeoutTimerTest.kt | 23 +- .../WebSocketTransportTest.kt | 220 +-- .../{ => utilities}/TestUtilities.kt | 9 +- 9 files changed, 1929 insertions(+), 1759 deletions(-) delete mode 100644 src/test/kotlin/org/phoenixframework/ExampleTest.kt rename src/test/kotlin/org/phoenixframework/{ => utilities}/TestUtilities.kt (91%) diff --git a/build.gradle b/build.gradle index ebacded..30d99c5 100644 --- a/build.gradle +++ b/build.gradle @@ -29,9 +29,6 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' - testCompileOnly 'junit:junit:4.12' - testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.3.1' - testCompile group: 'com.google.truth', name: 'truth', version: '0.44' testCompile group: 'org.mockito', name: 'mockito-core', version: '2.27.0' testCompile group: 'com.nhaarman.mockitokotlin2', name: 'mockito-kotlin', version: '2.1.0' diff --git a/src/test/kotlin/org/phoenixframework/ChannelTest.kt b/src/test/kotlin/org/phoenixframework/ChannelTest.kt index 3803abd..80dd2e8 100644 --- a/src/test/kotlin/org/phoenixframework/ChannelTest.kt +++ b/src/test/kotlin/org/phoenixframework/ChannelTest.kt @@ -9,12 +9,16 @@ import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyZeroInteractions import com.nhaarman.mockitokotlin2.whenever -import org.junit.After -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.stubbing.Answer +import org.phoenixframework.utilities.ManualDispatchQueue +import org.phoenixframework.utilities.getBindings class ChannelTest { @@ -36,8 +40,8 @@ class ChannelTest { mutableRef.toString() } - @Before - fun setUp() { + @BeforeEach + internal fun setUp() { MockitoAnnotations.initMocks(this) mutableRef = 0 @@ -51,988 +55,1030 @@ class ChannelTest { channel = Channel("topic", kDefaultPayload, socket) } - @After - fun tearDown() { + @AfterEach + internal fun tearDown() { fakeClock.reset() } - //------------------------------------------------------------------------------ - // Channel.Event - //------------------------------------------------------------------------------ - @Test - fun `isLifecycleEvent returns true for lifecycle events`() { - assertThat(Channel.Event.isLifecycleEvent(Channel.Event.HEARTBEAT.value)).isFalse() - assertThat(Channel.Event.isLifecycleEvent(Channel.Event.JOIN.value)).isTrue() - assertThat(Channel.Event.isLifecycleEvent(Channel.Event.LEAVE.value)).isTrue() - assertThat(Channel.Event.isLifecycleEvent(Channel.Event.REPLY.value)).isTrue() - assertThat(Channel.Event.isLifecycleEvent(Channel.Event.ERROR.value)).isTrue() - assertThat(Channel.Event.isLifecycleEvent(Channel.Event.CLOSE.value)).isTrue() - assertThat(Channel.Event.isLifecycleEvent("random")).isFalse() + @Nested + @DisplayName("ChannelEvent") + inner class ChannelEvent { + @Test + internal fun `isLifecycleEvent returns true for lifecycle events`() { + assertThat(Channel.Event.isLifecycleEvent(Channel.Event.HEARTBEAT.value)).isFalse() + assertThat(Channel.Event.isLifecycleEvent(Channel.Event.JOIN.value)).isTrue() + assertThat(Channel.Event.isLifecycleEvent(Channel.Event.LEAVE.value)).isTrue() + assertThat(Channel.Event.isLifecycleEvent(Channel.Event.REPLY.value)).isTrue() + assertThat(Channel.Event.isLifecycleEvent(Channel.Event.ERROR.value)).isTrue() + assertThat(Channel.Event.isLifecycleEvent(Channel.Event.CLOSE.value)).isTrue() + assertThat(Channel.Event.isLifecycleEvent("random")).isFalse() + } - } - //------------------------------------------------------------------------------ - // Channel Class - //------------------------------------------------------------------------------ - - /* constructor */ - @Test - fun `constructor() sets defaults`() { - assertThat(channel.state).isEqualTo(Channel.State.CLOSED) - assertThat(channel.topic).isEqualTo("topic") - assertThat(channel.params["one"]).isEqualTo("two") - assertThat(channel.socket).isEqualTo(socket) - assertThat(channel.timeout).isEqualTo(10_000L) - assertThat(channel.joinedOnce).isFalse() - assertThat(channel.joinPush).isNotNull() - assertThat(channel.pushBuffer).isEmpty() - } + /* End ChannelEvent */ + } + + @Nested + @DisplayName("constructor") + inner class Constructor { + @Test + internal fun `sets defaults`() { + assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + assertThat(channel.topic).isEqualTo("topic") + assertThat(channel.params["one"]).isEqualTo("two") + assertThat(channel.socket).isEqualTo(socket) + assertThat(channel.timeout).isEqualTo(10_000L) + assertThat(channel.joinedOnce).isFalse() + assertThat(channel.joinPush).isNotNull() + assertThat(channel.pushBuffer).isEmpty() + } - @Test - fun `constructor() sets up joinPush with literal params`() { - val joinPush = channel.joinPush + @Test + internal fun `sets up joinPush with literal params`() { + val joinPush = channel.joinPush - assertThat(joinPush.channel).isEqualTo(channel) - assertThat(joinPush.payload["one"]).isEqualTo("two") - assertThat(joinPush.event).isEqualTo("phx_join") - assertThat(joinPush.timeout).isEqualTo(10_000L) - } + assertThat(joinPush.channel).isEqualTo(channel) + assertThat(joinPush.payload["one"]).isEqualTo("two") + assertThat(joinPush.event).isEqualTo("phx_join") + assertThat(joinPush.timeout).isEqualTo(10_000L) + } - /* onMessage */ - @Test - fun `onMessage() returns message by default`() { - val message = channel.onMessage.invoke(Message(ref = "original")) - assertThat(message.ref).isEqualTo("original") + /* End Constructor */ } - @Test - fun `onMessage() can be overidden`() { - channel.onMessage { Message(ref = "changed") } + @Nested + @DisplayName("onMessage") + inner class OnMessage { + @Test + internal fun `returns message by default`() { + val message = channel.onMessage.invoke(Message(ref = "original")) + assertThat(message.ref).isEqualTo("original") + } - val message = channel.onMessage.invoke(Message(ref = "original")) - assertThat(message.ref).isEqualTo("changed") - } + @Test + internal fun `can be overidden`() { + channel.onMessage { Message(ref = "changed") } - /* join params */ - @Test - fun `updating join params`() { - val params = mapOf("value" to 1) - val change = mapOf("value" to 2) - - channel = Channel("topic", params, socket) - val joinPush = channel.joinPush - - assertThat(joinPush.channel).isEqualTo(channel) - assertThat(joinPush.payload["value"]).isEqualTo(1) - assertThat(joinPush.event).isEqualTo("phx_join") - assertThat(joinPush.timeout).isEqualTo(10_000L) - - channel.params = change - assertThat(joinPush.channel).isEqualTo(channel) - assertThat(joinPush.payload["value"]).isEqualTo(2) - assertThat(channel.params["value"]).isEqualTo(2) - assertThat(joinPush.event).isEqualTo("phx_join") - assertThat(joinPush.timeout).isEqualTo(10_000L) - } + val message = channel.onMessage.invoke(Message(ref = "original")) + assertThat(message.ref).isEqualTo("changed") + } - /* join */ - @Test - fun `join() sets state to joining"`() { - channel.join() - assertThat(channel.state).isEqualTo(Channel.State.JOINING) + /* End OnMessage */ } - @Test - fun `join() sets joinedOnce to true`() { - assertThat(channel.joinedOnce).isFalse() + @Nested + @DisplayName("join params") + inner class JoinParams { + @Test + internal fun `updating join params`() { + val params = mapOf("value" to 1) + val change = mapOf("value" to 2) - channel.join() - assertThat(channel.joinedOnce).isTrue() - } + channel = Channel("topic", params, socket) + val joinPush = channel.joinPush - @Test - fun `join() throws if attempting to join multiple times`() { - var exceptionThrown = false - try { - channel.join() - channel.join() - } catch (e: Exception) { - exceptionThrown = true - assertThat(e).isInstanceOf(IllegalStateException::class.java) - assertThat(e.message).isEqualTo( - "Tried to join channel multiple times. `join()` can only be called once per channel") + assertThat(joinPush.channel).isEqualTo(channel) + assertThat(joinPush.payload["value"]).isEqualTo(1) + assertThat(joinPush.event).isEqualTo("phx_join") + assertThat(joinPush.timeout).isEqualTo(10_000L) + + channel.params = change + assertThat(joinPush.channel).isEqualTo(channel) + assertThat(joinPush.payload["value"]).isEqualTo(2) + assertThat(channel.params["value"]).isEqualTo(2) + assertThat(joinPush.event).isEqualTo("phx_join") + assertThat(joinPush.timeout).isEqualTo(10_000L) } - assertThat(exceptionThrown).isTrue() + /* End JoinParams */ } - @Test - fun `join() triggers socket push with channel params`() { - channel.join() - verify(socket).push("topic", "phx_join", kDefaultPayload, kDefaultRef, channel.joinRef) - } + @Nested + @DisplayName("join") + inner class Join { + @Test + internal fun `sets state to joining`() { + channel.join() + assertThat(channel.state).isEqualTo(Channel.State.JOINING) + } - @Test - fun `join() can set timeout on joinPush`() { - val newTimeout = 20_000L - val joinPush = channel.joinPush + @Test + internal fun `sets joinedOnce to true`() { + assertThat(channel.joinedOnce).isFalse() - assertThat(joinPush.timeout).isEqualTo(kDefaultTimeout) - channel.join(newTimeout) - assertThat(joinPush.timeout).isEqualTo(newTimeout) - } + channel.join() + assertThat(channel.joinedOnce).isTrue() + } - /* timeout behavior */ - @Test - fun `succeeds before timeout`() { - val joinPush = channel.joinPush - val timeout = channel.timeout + @Test + internal fun `throws if attempting to join multiple times`() { + var exceptionThrown = false + try { + channel.join() + channel.join() + } catch (e: Exception) { + exceptionThrown = true + assertThat(e).isInstanceOf(IllegalStateException::class.java) + assertThat(e.message).isEqualTo( + "Tried to join channel multiple times. `join()` can only be called once per channel") + } + + assertThat(exceptionThrown).isTrue() + } - channel.join() - verify(socket).push(any(), any(), any(), any(), any()) + @Test + internal fun `triggers socket push with channel params`() { + channel.join() + verify(socket).push("topic", "phx_join", kDefaultPayload, kDefaultRef, channel.joinRef) + } - fakeClock.tick(timeout / 2) + @Test + internal fun `can set timeout on joinPush`() { + val newTimeout = 20_000L + val joinPush = channel.joinPush - joinPush.trigger("ok", kEmptyPayload) - assertThat(channel.state).isEqualTo(Channel.State.JOINED) + assertThat(joinPush.timeout).isEqualTo(kDefaultTimeout) + channel.join(newTimeout) + assertThat(joinPush.timeout).isEqualTo(newTimeout) + } - fakeClock.tick(timeout) - verify(socket, times(1)).push(any(), any(), any(), any(), any()) + /* End Join */ } - @Test - fun `retries with backoff after timeout`() { - var ref = 0 - whenever(socket.isConnected).thenReturn(true) - whenever(socket.makeRef()).thenAnswer { - ref += 1 - ref.toString() - } + @Nested + @DisplayName("timeout behavior") + inner class TimeoutBehavior { + @Test + internal fun `succeeds before timeout`() { + val joinPush = channel.joinPush + val timeout = channel.timeout - val joinPush = channel.joinPush - val timeout = channel.timeout + channel.join() + verify(socket).push(any(), any(), any(), any(), any()) - channel.join() - verify(socket, times(1)).push(any(), any(), any(), any(), any()) + fakeClock.tick(timeout / 2) - fakeClock.tick(timeout) // leave push sent to the server - verify(socket, times(2)).push(any(), any(), any(), any(), any()) + joinPush.trigger("ok", kEmptyPayload) + assertThat(channel.state).isEqualTo(Channel.State.JOINED) - fakeClock.tick(1_000) // begin stepped backoff - verify(socket, times(3)).push(any(), any(), any(), any(), any()) + fakeClock.tick(timeout) + verify(socket, times(1)).push(any(), any(), any(), any(), any()) + } - fakeClock.tick(2_000) - verify(socket, times(4)).push(any(), any(), any(), any(), any()) + @Test + internal fun `retries with backoff after timeout`() { + var ref = 0 + whenever(socket.isConnected).thenReturn(true) + whenever(socket.makeRef()).thenAnswer { + ref += 1 + ref.toString() + } - fakeClock.tick(5_000) - verify(socket, times(5)).push(any(), any(), any(), any(), any()) + val joinPush = channel.joinPush + val timeout = channel.timeout - fakeClock.tick(10_000) - verify(socket, times(6)).push(any(), any(), any(), any(), any()) + channel.join() + verify(socket, times(1)).push(any(), any(), any(), any(), any()) - joinPush.trigger("ok", kEmptyPayload) - assertThat(channel.state).isEqualTo(Channel.State.JOINED) + fakeClock.tick(timeout) // leave push sent to the server + verify(socket, times(2)).push(any(), any(), any(), any(), any()) - fakeClock.tick(10_000) - verify(socket, times(6)).push(any(), any(), any(), any(), any()) - assertThat(channel.state).isEqualTo(Channel.State.JOINED) - } + fakeClock.tick(1_000) // begin stepped backoff + verify(socket, times(3)).push(any(), any(), any(), any(), any()) - @Test - fun `with socket and join delay`() { - whenever(socket.isConnected).thenReturn(false) - val joinPush = channel.joinPush + fakeClock.tick(2_000) + verify(socket, times(4)).push(any(), any(), any(), any(), any()) - channel.join() - verify(socket, times(1)).push(any(), any(), any(), any(), any()) + fakeClock.tick(5_000) + verify(socket, times(5)).push(any(), any(), any(), any(), any()) - // Open the socket after a delay - fakeClock.tick(9_000) - verify(socket, times(1)).push(any(), any(), any(), any(), any()) + fakeClock.tick(10_000) + verify(socket, times(6)).push(any(), any(), any(), any(), any()) - // join request returns between timeouts - fakeClock.tick(1_000) + joinPush.trigger("ok", kEmptyPayload) + assertThat(channel.state).isEqualTo(Channel.State.JOINED) - whenever(socket.isConnected).thenReturn(true) - joinPush.trigger("ok", kEmptyPayload) + fakeClock.tick(10_000) + verify(socket, times(6)).push(any(), any(), any(), any(), any()) + assertThat(channel.state).isEqualTo(Channel.State.JOINED) + } - assertThat(channel.state).isEqualTo(Channel.State.ERRORED) + @Test + internal fun `with socket and join delay`() { + whenever(socket.isConnected).thenReturn(false) + val joinPush = channel.joinPush - fakeClock.tick(1_000) - assertThat(channel.state).isEqualTo(Channel.State.JOINING) + channel.join() + verify(socket, times(1)).push(any(), any(), any(), any(), any()) - joinPush.trigger("ok", kEmptyPayload) - assertThat(channel.state).isEqualTo(Channel.State.JOINED) + // Open the socket after a delay + fakeClock.tick(9_000) + verify(socket, times(1)).push(any(), any(), any(), any(), any()) - verify(socket, times(3)).push(any(), any(), any(), any(), any()) - } + // join request returns between timeouts + fakeClock.tick(1_000) - @Test - fun `with socket delay only`() { - whenever(socket.isConnected).thenReturn(false) - val joinPush = channel.joinPush + whenever(socket.isConnected).thenReturn(true) + joinPush.trigger("ok", kEmptyPayload) - channel.join() + assertThat(channel.state).isEqualTo(Channel.State.ERRORED) - // connect socket after a delay - fakeClock.tick(6_000) - whenever(socket.isConnected).thenReturn(true) + fakeClock.tick(1_000) + assertThat(channel.state).isEqualTo(Channel.State.JOINING) - fakeClock.tick(5_000) - joinPush.trigger("ok", kEmptyPayload) + joinPush.trigger("ok", kEmptyPayload) + assertThat(channel.state).isEqualTo(Channel.State.JOINED) - fakeClock.tick(2_000) - assertThat(channel.state).isEqualTo(Channel.State.JOINING) + verify(socket, times(3)).push(any(), any(), any(), any(), any()) + } - joinPush.trigger("ok", kEmptyPayload) - assertThat(channel.state).isEqualTo(Channel.State.JOINED) - } + @Test + internal fun `with socket delay only`() { + whenever(socket.isConnected).thenReturn(false) + val joinPush = channel.joinPush - /* Join Push */ - private fun setupJoinPushTests() { - whenever(socket.isConnected).thenReturn(true) - whenever(socket.makeRef()).thenAnswer(mutableRefAnswer) - channel.join() - } + channel.join() - private fun receivesOk(joinPush: Push) { - fakeClock.tick(joinPush.timeout / 2) - joinPush.trigger("ok", mapOf("a" to "b")) - } + // connect socket after a delay + fakeClock.tick(6_000) + whenever(socket.isConnected).thenReturn(true) - private fun receivesTimeout(joinPush: Push) { - fakeClock.tick(joinPush.timeout * 2) - } + fakeClock.tick(5_000) + joinPush.trigger("ok", kEmptyPayload) - private fun receivesError(joinPush: Push) { - fakeClock.tick(joinPush.timeout / 2) - joinPush.trigger("error", mapOf("a" to "b")) - } - - /* receives 'ok' */ - @Test - fun `joinPush - receivesOk - sets channel state to joined`() { - setupJoinPushTests() - val joinPush = channel.joinPush + fakeClock.tick(2_000) + assertThat(channel.state).isEqualTo(Channel.State.JOINING) - assertThat(channel.state).isNotEqualTo(Channel.State.JOINED) + joinPush.trigger("ok", kEmptyPayload) + assertThat(channel.state).isEqualTo(Channel.State.JOINED) + } - receivesOk(joinPush) - assertThat(channel.state).isEqualTo(Channel.State.JOINED) + /* End TimeoutBehavior */ } - @Test - fun `joinPush - receivesOk - triggers receive(ok) callback after ok response`() { - setupJoinPushTests() - val joinPush = channel.joinPush + @Nested + @DisplayName("joinPush") + inner class JoinPush { - val mockCallback = mock<(Message) -> Unit>() - joinPush.receive("ok", mockCallback) + /* setup */ + @BeforeEach + internal fun setUp() { + whenever(socket.isConnected).thenReturn(true) + whenever(socket.makeRef()).thenAnswer(mutableRefAnswer) + channel.join() + } - receivesOk(joinPush) - verify(mockCallback, times(1)).invoke(any()) - } + /* helper methods */ + private fun receivesOk(joinPush: Push) { + fakeClock.tick(joinPush.timeout / 2) + joinPush.trigger("ok", mapOf("a" to "b")) + } - @Test - fun `joinPush - receivesOk - triggers receive('ok') callback if ok response already received`() { - setupJoinPushTests() - val joinPush = channel.joinPush + private fun receivesTimeout(joinPush: Push) { + fakeClock.tick(joinPush.timeout * 2) + } - receivesOk(joinPush) + private fun receivesError(joinPush: Push) { + fakeClock.tick(joinPush.timeout / 2) + joinPush.trigger("error", mapOf("a" to "b")) + } - val mockCallback = mock<(Message) -> Unit>() - joinPush.receive("ok", mockCallback) + @Nested + @DisplayName("receives 'ok'") + inner class ReceivesOk { + @Test + internal fun `sets channel state to joined`() { + val joinPush = channel.joinPush - verify(mockCallback, times(1)).invoke(any()) - } + assertThat(channel.state).isNotEqualTo(Channel.State.JOINED) - @Test - fun `joinPush - receivesOk - does not trigger other receive callbacks after ok response`() { - setupJoinPushTests() - val joinPush = channel.joinPush + receivesOk(joinPush) + assertThat(channel.state).isEqualTo(Channel.State.JOINED) + } - val mockCallback = mock<(Message) -> Unit>() - joinPush - .receive("error", mockCallback) - .receive("timeout", mockCallback) + @Test + internal fun `triggers receive(ok) callback after ok response`() { + val joinPush = channel.joinPush - receivesOk(joinPush) - receivesTimeout(joinPush) - verify(mockCallback, times(0)).invoke(any()) - } + val mockCallback = mock<(Message) -> Unit>() + joinPush.receive("ok", mockCallback) - @Test - fun `joinPush - receivesOk - clears timeoutTimer workItem`() { - setupJoinPushTests() - val joinPush = channel.joinPush + receivesOk(joinPush) + verify(mockCallback, times(1)).invoke(any()) + } - assertThat(joinPush.timeoutTask).isNotNull() + @Test + internal fun `triggers receive('ok') callback if ok response already received`() { + val joinPush = channel.joinPush - val mockTimeoutTask = mock() - joinPush.timeoutTask = mockTimeoutTask + receivesOk(joinPush) - receivesOk(joinPush) - verify(mockTimeoutTask).cancel() - assertThat(joinPush.timeoutTask).isNull() - } + val mockCallback = mock<(Message) -> Unit>() + joinPush.receive("ok", mockCallback) - @Test - fun `joinPush - receivesOk - sets receivedMessage`() { - setupJoinPushTests() - val joinPush = channel.joinPush + verify(mockCallback, times(1)).invoke(any()) + } - assertThat(joinPush.receivedMessage).isNull() + @Test + internal fun `does not trigger other receive callbacks after ok response`() { + val joinPush = channel.joinPush - receivesOk(joinPush) - assertThat(joinPush.receivedMessage?.payload).isEqualTo(mapOf("status" to "ok", "a" to "b")) - assertThat(joinPush.receivedMessage?.status).isEqualTo("ok") - } + val mockCallback = mock<(Message) -> Unit>() + joinPush + .receive("error", mockCallback) + .receive("timeout", mockCallback) - @Test - fun `joinPush - receivesOk - removes channel binding`() { - setupJoinPushTests() - val joinPush = channel.joinPush + receivesOk(joinPush) + receivesTimeout(joinPush) + verify(mockCallback, times(0)).invoke(any()) + } - var bindings = channel.getBindings("chan_reply_1") - assertThat(bindings).hasSize(1) + @Test + internal fun `clears timeoutTimer workItem`() { + val joinPush = channel.joinPush - receivesOk(joinPush) - bindings = channel.getBindings("chan_reply_1") - assertThat(bindings).isEmpty() - } + assertThat(joinPush.timeoutTask).isNotNull() - @Test - fun `joinPush - receivesOk - resets channel rejoinTimer`() { - setupJoinPushTests() - val joinPush = channel.joinPush + val mockTimeoutTask = mock() + joinPush.timeoutTask = mockTimeoutTask - val mockRejoinTimer = mock() - channel.rejoinTimer = mockRejoinTimer + receivesOk(joinPush) + verify(mockTimeoutTask).cancel() + assertThat(joinPush.timeoutTask).isNull() + } - receivesOk(joinPush) - verify(mockRejoinTimer, times(1)).reset() - } + @Test + internal fun `sets receivedMessage`() { + val joinPush = channel.joinPush - @Test - fun `joinPush - receivesOk - sends and empties channel's buffered pushEvents`() { - setupJoinPushTests() - val joinPush = channel.joinPush + assertThat(joinPush.receivedMessage).isNull() - val mockPush = mock() - channel.pushBuffer.add(mockPush) + receivesOk(joinPush) + assertThat(joinPush.receivedMessage?.payload).isEqualTo(mapOf("status" to "ok", "a" to "b")) + assertThat(joinPush.receivedMessage?.status).isEqualTo("ok") + } - receivesOk(joinPush) - verify(mockPush).send() - assertThat(channel.pushBuffer).isEmpty() - } + @Test + internal fun `removes channel binding`() { + val joinPush = channel.joinPush - /* receives 'timeout' */ - @Test - fun `joinPush - receives 'timeout' - sets channel state to errored`() { - setupJoinPushTests() - val joinPush = channel.joinPush + var bindings = channel.getBindings("chan_reply_1") + assertThat(bindings).hasSize(1) - receivesTimeout(joinPush) - assertThat(channel.state).isEqualTo(Channel.State.ERRORED) - } + receivesOk(joinPush) + bindings = channel.getBindings("chan_reply_1") + assertThat(bindings).isEmpty() + } - @Test - fun `joinPush - receives 'timeout' - triggers receive('timeout') callback after ok response`() { - setupJoinPushTests() - val joinPush = channel.joinPush + @Test + internal fun `resets channel rejoinTimer`() { + val joinPush = channel.joinPush - val mockCallback = mock<(Message) -> Unit>() - joinPush.receive("timeout", mockCallback) + val mockRejoinTimer = mock() + channel.rejoinTimer = mockRejoinTimer - receivesTimeout(joinPush) - verify(mockCallback).invoke(any()) - } + receivesOk(joinPush) + verify(mockRejoinTimer, times(1)).reset() + } - @Test - fun `joinPush - receives 'timeout' - does not trigger other receive callbacks after timeout response`() { - setupJoinPushTests() - val joinPush = channel.joinPush - - val mockOk = mock<(Message) -> Unit>() - val mockError = mock<(Message) -> Unit>() - val mockTimeout = mock<(Message) -> Unit>() - joinPush - .receive("ok", mockOk) - .receive("error", mockError) - .receive("timeout", mockTimeout) - - receivesTimeout(joinPush) - joinPush.trigger("ok", emptyMap()) - - verifyZeroInteractions(mockOk) - verifyZeroInteractions(mockError) - verify(mockTimeout).invoke(any()) - } + @Test + internal fun `sends and empties channel's buffered pushEvents`() { + val joinPush = channel.joinPush - @Test - fun `joinPush - receives 'timeout' - schedules rejoinTimer timeout`() { - setupJoinPushTests() - val joinPush = channel.joinPush + val mockPush = mock() + channel.pushBuffer.add(mockPush) - val mockTimer = mock() - channel.rejoinTimer = mockTimer + receivesOk(joinPush) + verify(mockPush).send() + assertThat(channel.pushBuffer).isEmpty() + } - receivesTimeout(joinPush) - verify(mockTimer).scheduleTimeout() - } + /* End ReceivesOk */ + } - /* receives 'error' */ - @Test - fun `joinPush - receives 'error' - triggers receive('error') callback after error response`() { - setupJoinPushTests() - val joinPush = channel.joinPush + @Nested + @DisplayName("receives 'timeout'") + inner class ReceivesTimeout { + @Test + internal fun `sets channel state to errored`() { + val joinPush = channel.joinPush - val mockCallback = mock<(Message) -> Unit>() - joinPush.receive("error", mockCallback) + receivesTimeout(joinPush) + assertThat(channel.state).isEqualTo(Channel.State.ERRORED) + } - receivesError(joinPush) - verify(mockCallback).invoke(any()) - } + @Test + internal fun `triggers receive('timeout') callback after ok response`() { + val joinPush = channel.joinPush - @Test - fun `joinPush - receives 'error' - triggers receive('error') callback if error response already received"`() { - setupJoinPushTests() - val joinPush = channel.joinPush + val mockCallback = mock<(Message) -> Unit>() + joinPush.receive("timeout", mockCallback) - receivesError(joinPush) + receivesTimeout(joinPush) + verify(mockCallback).invoke(any()) + } - val mockCallback = mock<(Message) -> Unit>() - joinPush.receive("error", mockCallback) + @Test + internal fun `does not trigger other receive callbacks after timeout response`() { + val joinPush = channel.joinPush - verify(mockCallback).invoke(any()) - } + val mockOk = mock<(Message) -> Unit>() + val mockError = mock<(Message) -> Unit>() + val mockTimeout = mock<(Message) -> Unit>() + joinPush + .receive("ok", mockOk) + .receive("error", mockError) + .receive("timeout", mockTimeout) - @Test - fun `joinPush - receives 'error' - does not trigger other receive callbacks after ok response`() { - setupJoinPushTests() - val joinPush = channel.joinPush + receivesTimeout(joinPush) + joinPush.trigger("ok", emptyMap()) - val mockCallback = mock<(Message) -> Unit>() - joinPush - .receive("ok", mockCallback) - .receive("timeout", mockCallback) + verifyZeroInteractions(mockOk) + verifyZeroInteractions(mockError) + verify(mockTimeout).invoke(any()) + } - receivesError(joinPush) - receivesTimeout(joinPush) - verifyZeroInteractions(mockCallback) - } + @Test + internal fun `schedules rejoinTimer timeout`() { + val joinPush = channel.joinPush - @Test - fun `joinPush - receives 'error' - clears timeoutTimer workItem`() { - setupJoinPushTests() - val joinPush = channel.joinPush + val mockTimer = mock() + channel.rejoinTimer = mockTimer - val mockTask = mock() - assertThat(joinPush.timeoutTask).isNotNull() + receivesTimeout(joinPush) + verify(mockTimer).scheduleTimeout() + } - joinPush.timeoutTask = mockTask - receivesError(joinPush) + /* End ReceivesTimeout */ + } - verify(mockTask).cancel() - assertThat(joinPush.timeoutTask).isNull() - } + @Nested + @DisplayName("receives 'error'") + inner class ReceivesError { + @Test + internal fun `triggers receive('error') callback after error response`() { + val joinPush = channel.joinPush - @Test - fun `joinPush - receives 'error' - sets receivedMessage`() { - setupJoinPushTests() - val joinPush = channel.joinPush + val mockCallback = mock<(Message) -> Unit>() + joinPush.receive("error", mockCallback) - assertThat(joinPush.receivedMessage).isNull() + receivesError(joinPush) + verify(mockCallback).invoke(any()) + } - receivesError(joinPush) - assertThat(joinPush.receivedMessage).isNotNull() - assertThat(joinPush.receivedMessage?.status).isEqualTo("error") - assertThat(joinPush.receivedMessage?.payload?.get("a")).isEqualTo("b") - } + @Test + internal fun `triggers receive('error') callback if error response already received`() { + val joinPush = channel.joinPush - @Test - fun `joinPush - receives 'error' - removes channel binding`() { - setupJoinPushTests() - val joinPush = channel.joinPush + receivesError(joinPush) - var bindings = channel.getBindings("chan_reply_1") - assertThat(bindings).hasSize(1) + val mockCallback = mock<(Message) -> Unit>() + joinPush.receive("error", mockCallback) - receivesError(joinPush) - bindings = channel.getBindings("chan_reply_1") - assertThat(bindings).isEmpty() - } + verify(mockCallback).invoke(any()) + } - @Test - fun `joinPush - receives 'error' - does not sets channel state to joined`() { - setupJoinPushTests() - val joinPush = channel.joinPush + @Test + internal fun `does not trigger other receive callbacks after ok response`() { + val joinPush = channel.joinPush - receivesError(joinPush) - assertThat(channel.state).isNotEqualTo(Channel.State.JOINED) - } + val mockCallback = mock<(Message) -> Unit>() + joinPush + .receive("ok", mockCallback) + .receive("timeout", mockCallback) - @Test - fun `joinPush - receives 'error' - does not trigger channel's buffered pushEvents`() { - setupJoinPushTests() - val joinPush = channel.joinPush + receivesError(joinPush) + receivesTimeout(joinPush) + verifyZeroInteractions(mockCallback) + } - val mockPush = mock() - channel.pushBuffer.add(mockPush) + @Test + internal fun `clears timeoutTimer workItem`() { + val joinPush = channel.joinPush - receivesError(joinPush) - verifyZeroInteractions(mockPush) - assertThat(channel.pushBuffer).hasSize(1) - } + val mockTask = mock() + assertThat(joinPush.timeoutTask).isNotNull() - /* onError */ - private fun setupCallbackTests() { - whenever(socket.isConnected).thenReturn(true) - channel.join() - } + joinPush.timeoutTask = mockTask + receivesError(joinPush) - @Test - fun `onError sets channel state to errored`() { - setupCallbackTests() + verify(mockTask).cancel() + assertThat(joinPush.timeoutTask).isNull() + } - assertThat(channel.state).isNotEqualTo(Channel.State.ERRORED) + @Test + internal fun `sets receivedMessage`() { + val joinPush = channel.joinPush - channel.trigger(Channel.Event.ERROR) - assertThat(channel.state).isEqualTo(Channel.State.ERRORED) - } + assertThat(joinPush.receivedMessage).isNull() - @Test - fun `onError tries to rejoin with backoff`() { - setupCallbackTests() + receivesError(joinPush) + assertThat(joinPush.receivedMessage).isNotNull() + assertThat(joinPush.receivedMessage?.status).isEqualTo("error") + assertThat(joinPush.receivedMessage?.payload?.get("a")).isEqualTo("b") + } - val mockTimer = mock() - channel.rejoinTimer = mockTimer + @Test + internal fun `removes channel binding`() { + val joinPush = channel.joinPush - channel.trigger(Channel.Event.ERROR) - verify(mockTimer).scheduleTimeout() - } + var bindings = channel.getBindings("chan_reply_1") + assertThat(bindings).hasSize(1) - @Test - fun `onError does not rejoin if leaving channel`() { - setupCallbackTests() + receivesError(joinPush) + bindings = channel.getBindings("chan_reply_1") + assertThat(bindings).isEmpty() + } - channel.state = Channel.State.LEAVING + @Test + internal fun `does not sets channel state to joined`() { + val joinPush = channel.joinPush - val mockPush = mock() - channel.joinPush = mockPush + receivesError(joinPush) + assertThat(channel.state).isNotEqualTo(Channel.State.JOINED) + } - channel.trigger(Channel.Event.ERROR) + @Test + internal fun `does not trigger channel's buffered pushEvents`() { + val joinPush = channel.joinPush - fakeClock.tick(1_000) - verify(mockPush, never()).send() + val mockPush = mock() + channel.pushBuffer.add(mockPush) - fakeClock.tick(2_000) - verify(mockPush, never()).send() + receivesError(joinPush) + verifyZeroInteractions(mockPush) + assertThat(channel.pushBuffer).hasSize(1) + } - assertThat(channel.state).isEqualTo(Channel.State.LEAVING) - } + /* End ReceivesError */ + } - @Test - fun `onError does nothing if channel is closed`() { - setupCallbackTests() + /* End JoinPush */ + } - channel.state = Channel.State.CLOSED + @Nested + @DisplayName("onError") + inner class OnError { + @BeforeEach + internal fun setUp() { + whenever(socket.isConnected).thenReturn(true) + channel.join() + } - val mockPush = mock() - channel.joinPush = mockPush + @Test + internal fun `sets channel state to errored`() { + assertThat(channel.state).isNotEqualTo(Channel.State.ERRORED) - channel.trigger(Channel.Event.ERROR) + channel.trigger(Channel.Event.ERROR) + assertThat(channel.state).isEqualTo(Channel.State.ERRORED) + } - fakeClock.tick(1_000) - verify(mockPush, never()).send() + @Test + internal fun `tries to rejoin with backoff`() { + val mockTimer = mock() + channel.rejoinTimer = mockTimer - fakeClock.tick(2_000) - verify(mockPush, never()).send() + channel.trigger(Channel.Event.ERROR) + verify(mockTimer).scheduleTimeout() + } - assertThat(channel.state).isEqualTo(Channel.State.CLOSED) - } + @Test + internal fun `does not rejoin if leaving channel`() { + channel.state = Channel.State.LEAVING - @Test - fun `onError triggers additional callbacks`() { - setupCallbackTests() + val mockPush = mock() + channel.joinPush = mockPush - val mockCallback = mock<(Message) -> Unit>() - channel.onError(mockCallback) + channel.trigger(Channel.Event.ERROR) - channel.trigger(Channel.Event.ERROR) - verify(mockCallback).invoke(any()) - } + fakeClock.tick(1_000) + verify(mockPush, never()).send() - /* onClose */ - @Test - fun `onClose sets state to closed`() { - setupCallbackTests() + fakeClock.tick(2_000) + verify(mockPush, never()).send() - assertThat(channel.state).isNotEqualTo(Channel.State.CLOSED) + assertThat(channel.state).isEqualTo(Channel.State.LEAVING) + } - channel.trigger(Channel.Event.CLOSE) - assertThat(channel.state).isEqualTo(Channel.State.CLOSED) - } + @Test + internal fun `does nothing if channel is closed`() { + channel.state = Channel.State.CLOSED - @Test - fun `onClose does not rejoin`() { - setupCallbackTests() + val mockPush = mock() + channel.joinPush = mockPush - val mockPush = mock() - channel.joinPush = mockPush + channel.trigger(Channel.Event.ERROR) - channel.trigger(Channel.Event.CLOSE) - verify(mockPush, never()).send() - } + fakeClock.tick(1_000) + verify(mockPush, never()).send() - @Test - fun `onClose resets the rejoin timer`() { - setupCallbackTests() + fakeClock.tick(2_000) + verify(mockPush, never()).send() - val mockTimer = mock() - channel.rejoinTimer = mockTimer + assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + } - channel.trigger(Channel.Event.CLOSE) - verify(mockTimer).reset() - } + @Test + internal fun `triggers additional callbacks`() { + val mockCallback = mock<(Message) -> Unit>() + channel.onError(mockCallback) - @Test - fun `onClose removes self from socket`() { - setupCallbackTests() + channel.trigger(Channel.Event.ERROR) + verify(mockCallback).invoke(any()) + } - channel.trigger(Channel.Event.CLOSE) - verify(socket).remove(channel) + /* End OnError */ } - @Test - fun `onClose triggers additional callbacks`() { - setupCallbackTests() + @Nested + @DisplayName("onClose") + inner class OnClose { + @BeforeEach + internal fun setUp() { + whenever(socket.isConnected).thenReturn(true) + channel.join() + } - val mockCallback = mock<(Message) -> Unit>() - channel.onClose(mockCallback) + @Test + internal fun `sets state to closed`() { + assertThat(channel.state).isNotEqualTo(Channel.State.CLOSED) - channel.trigger(Channel.Event.CLOSE) - verify(mockCallback).invoke(any()) - } + channel.trigger(Channel.Event.CLOSE) + assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + } - /* canPush */ - @Test - fun `canPush returns true when socket connected and channel joined`() { - channel.state = Channel.State.JOINED - whenever(socket.isConnected).thenReturn(true) + @Test + internal fun `does not rejoin`() { + val mockPush = mock() + channel.joinPush = mockPush - assertThat(channel.canPush).isTrue() - } + channel.trigger(Channel.Event.CLOSE) + verify(mockPush, never()).send() + } - @Test - fun `canPush otherwise returns false`() { - channel.state = Channel.State.JOINED - whenever(socket.isConnected).thenReturn(false) - assertThat(channel.canPush).isFalse() + @Test + internal fun `resets the rejoin timer`() { + val mockTimer = mock() + channel.rejoinTimer = mockTimer - channel.state = Channel.State.JOINING - whenever(socket.isConnected).thenReturn(true) - assertThat(channel.canPush).isFalse() + channel.trigger(Channel.Event.CLOSE) + verify(mockTimer).reset() + } - channel.state = Channel.State.JOINING - whenever(socket.isConnected).thenReturn(false) - assertThat(channel.canPush).isFalse() - } + @Test + internal fun `removes self from socket`() { + channel.trigger(Channel.Event.CLOSE) + verify(socket).remove(channel) + } + + @Test + internal fun `triggers additional callbacks`() { + val mockCallback = mock<(Message) -> Unit>() + channel.onClose(mockCallback) - /* on(event:, callback:) */ - @Test - fun `on() sets up callback for event`() { - channel.trigger(event = "event", ref = kDefaultRef) + channel.trigger(Channel.Event.CLOSE) + verify(mockCallback).invoke(any()) + } - channel.on("event", mockCallback) - channel.trigger(event = "event", ref = kDefaultRef) - verify(mockCallback, times(1)).invoke(any()) + /* End OnClose */ } - @Test - fun `on() other event callbacks are ignored`() { - val mockIgnoredCallback = mock<(Message) -> Unit>() + @Nested + @DisplayName("canPush") + inner class CanPush { + @Test + internal fun `returns true when socket connected and channel joined`() { + channel.state = Channel.State.JOINED + whenever(socket.isConnected).thenReturn(true) - channel.on("ignored_event", mockIgnoredCallback) - channel.trigger(event = "event", ref = kDefaultRef) + assertThat(channel.canPush).isTrue() + } - channel.on("event", mockCallback) - channel.trigger(event = "event", ref = kDefaultRef) + @Test + internal fun `otherwise returns false`() { + channel.state = Channel.State.JOINED + whenever(socket.isConnected).thenReturn(false) + assertThat(channel.canPush).isFalse() - verify(mockIgnoredCallback, never()).invoke(any()) - } + channel.state = Channel.State.JOINING + whenever(socket.isConnected).thenReturn(true) + assertThat(channel.canPush).isFalse() - @Test - fun `on() generates unique refs for callbacks`() { - val ref1 = channel.on("event1") {} - val ref2 = channel.on("event2") {} + channel.state = Channel.State.JOINING + whenever(socket.isConnected).thenReturn(false) + assertThat(channel.canPush).isFalse() + } - assertThat(ref1).isNotEqualTo(ref2) - assertThat(ref1 + 1).isEqualTo(ref2) + /* End CanPush */ } - /* off */ - @Test - fun `off removes all callbacks for event`() { - val callback1 = mock<(Message) -> Unit>() - val callback2 = mock<(Message) -> Unit>() - val callback3 = mock<(Message) -> Unit>() + @Nested + @DisplayName("on(event, callback)") + inner class OnEventCallback { + @Test + internal fun `sets up callback for event`() { + channel.trigger(event = "event", ref = kDefaultRef) - channel.on("event", callback1) - channel.on("event", callback2) - channel.on("other", callback3) + channel.on("event", mockCallback) + channel.trigger(event = "event", ref = kDefaultRef) + verify(mockCallback, times(1)).invoke(any()) + } - channel.off("event") - channel.trigger(event = "event", ref = kDefaultRef) - channel.trigger(event = "other", ref = kDefaultRef) + @Test + internal fun `other event callbacks are ignored`() { + val mockIgnoredCallback = mock<(Message) -> Unit>() - verifyZeroInteractions(callback1) - verifyZeroInteractions(callback2) - verify(callback3, times(1)).invoke(any()) - } + channel.on("ignored_event", mockIgnoredCallback) + channel.trigger(event = "event", ref = kDefaultRef) - @Test - fun `off removes callback by ref`() { - val callback1 = mock<(Message) -> Unit>() - val callback2 = mock<(Message) -> Unit>() + channel.on("event", mockCallback) + channel.trigger(event = "event", ref = kDefaultRef) - val ref1 = channel.on("event", callback1) - channel.on("event", callback2) + verify(mockIgnoredCallback, never()).invoke(any()) + } - channel.off("event", ref1) - channel.trigger(event = "event", ref = kDefaultRef) + @Test + internal fun `generates unique refs for callbacks`() { + val ref1 = channel.on("event1") {} + val ref2 = channel.on("event2") {} - verifyZeroInteractions(callback1) - verify(callback2, times(1)).invoke(any()) + assertThat(ref1).isNotEqualTo(ref2) + assertThat(ref1 + 1).isEqualTo(ref2) + } + + /* End OnEventCallback */ } - /* push */ - @Test - fun `push sends push event when successfully joined`() { - whenever(socket.isConnected).thenReturn(true) - channel.join().trigger("ok", kEmptyPayload) - channel.push("event", mapOf("foo" to "bar")) + @Nested + @DisplayName("off") + inner class Off { + @Test + internal fun `removes all callbacks for event`() { + val callback1 = mock<(Message) -> Unit>() + val callback2 = mock<(Message) -> Unit>() + val callback3 = mock<(Message) -> Unit>() - verify(socket).push("topic", "event", mapOf("foo" to "bar"), channel.joinRef, kDefaultRef) - } + channel.on("event", callback1) + channel.on("event", callback2) + channel.on("other", callback3) - @Test - fun `push enqueues push event to be sent once join has succeeded`() { - whenever(socket.isConnected).thenReturn(true) + channel.off("event") + channel.trigger(event = "event", ref = kDefaultRef) + channel.trigger(event = "other", ref = kDefaultRef) - val joinPush = channel.join() - channel.push("event", mapOf("foo" to "bar")) + verifyZeroInteractions(callback1) + verifyZeroInteractions(callback2) + verify(callback3, times(1)).invoke(any()) + } - verify(socket, never()).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) + @Test + internal fun `removes callback by ref`() { + val callback1 = mock<(Message) -> Unit>() + val callback2 = mock<(Message) -> Unit>() - fakeClock.tick(channel.timeout / 2) - joinPush.trigger("ok", kEmptyPayload) + val ref1 = channel.on("event", callback1) + channel.on("event", callback2) - verify(socket).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) - } + channel.off("event", ref1) + channel.trigger(event = "event", ref = kDefaultRef) - @Test - fun `push does not push if channel join times out`() { - whenever(socket.isConnected).thenReturn(true) + verifyZeroInteractions(callback1) + verify(callback2, times(1)).invoke(any()) + } - val joinPush = channel.join() - channel.push("event", mapOf("foo" to "bar")) + /* End Off */ + } - verify(socket, never()).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) + @Nested + @DisplayName("push") + inner class PushFunction { - fakeClock.tick(channel.timeout * 2) - joinPush.trigger("ok", kEmptyPayload) + @BeforeEach + internal fun setUp() { + whenever(socket.isConnected).thenReturn(true) + } - verify(socket, never()).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) - } + @Test + internal fun `sends push event when successfully joined`() { + channel.join().trigger("ok", kEmptyPayload) + channel.push("event", mapOf("foo" to "bar")) - @Test - fun `push uses channel timeout by default`() { - whenever(socket.isConnected).thenReturn(true) + verify(socket).push("topic", "event", mapOf("foo" to "bar"), channel.joinRef, kDefaultRef) + } - channel.join().trigger("ok", kEmptyPayload) - channel - .push("event", mapOf("foo" to "bar")) - .receive("timeout", mockCallback) + @Test + internal fun `enqueues push event to be sent once join has succeeded`() { + val joinPush = channel.join() + channel.push("event", mapOf("foo" to "bar")) - fakeClock.tick(channel.timeout / 2) - verifyZeroInteractions(mockCallback) + verify(socket, never()).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) - fakeClock.tick(channel.timeout) - verify(mockCallback).invoke(any()) - } + fakeClock.tick(channel.timeout / 2) + joinPush.trigger("ok", kEmptyPayload) - @Test - fun `push accepts timeout arg`() { - whenever(socket.isConnected).thenReturn(true) + verify(socket).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) + } - channel.join().trigger("ok", kEmptyPayload) - channel - .push("event", mapOf("foo" to "bar"), channel.timeout * 2) - .receive("timeout", mockCallback) + @Test + internal fun `does not push if channel join times out`() { + val joinPush = channel.join() + channel.push("event", mapOf("foo" to "bar")) - fakeClock.tick(channel.timeout) - verifyZeroInteractions(mockCallback) + verify(socket, never()).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) - fakeClock.tick(channel.timeout * 2) - verify(mockCallback).invoke(any()) - } + fakeClock.tick(channel.timeout * 2) + joinPush.trigger("ok", kEmptyPayload) - @Test - fun `push does not time out after receiving 'ok'`() { - whenever(socket.isConnected).thenReturn(true) + verify(socket, never()).push(any(), any(), eq(mapOf("foo" to "bar")), any(), any()) + } - channel.join().trigger("ok", kEmptyPayload) - val push = channel - .push("event", mapOf("foo" to "bar"), channel.timeout * 2) - .receive("timeout", mockCallback) + @Test + internal fun `uses channel timeout by default`() { + channel.join().trigger("ok", kEmptyPayload) + channel + .push("event", mapOf("foo" to "bar")) + .receive("timeout", mockCallback) - fakeClock.tick(channel.timeout / 2) - verifyZeroInteractions(mockCallback) + fakeClock.tick(channel.timeout / 2) + verifyZeroInteractions(mockCallback) - push.trigger("ok", kEmptyPayload) + fakeClock.tick(channel.timeout) + verify(mockCallback).invoke(any()) + } - fakeClock.tick(channel.timeout) - verifyZeroInteractions(mockCallback) - } + @Test + internal fun `accepts timeout arg`() { + channel.join().trigger("ok", kEmptyPayload) + channel + .push("event", mapOf("foo" to "bar"), channel.timeout * 2) + .receive("timeout", mockCallback) - @Test - fun `push throws if channel has not been joined`() { - whenever(socket.isConnected).thenReturn(true) + fakeClock.tick(channel.timeout) + verifyZeroInteractions(mockCallback) - var exceptionThrown = false - try { - channel.push("event", kEmptyPayload) - } catch (e: Exception) { - exceptionThrown = true - assertThat(e.message).isEqualTo( - "Tried to push event to topic before joining. Use channel.join() before pushing events") + fakeClock.tick(channel.timeout * 2) + verify(mockCallback).invoke(any()) } - assertThat(exceptionThrown).isTrue() - } + @Test + internal fun `does not time out after receiving 'ok'`() { + channel.join().trigger("ok", kEmptyPayload) + val push = channel + .push("event", mapOf("foo" to "bar"), channel.timeout * 2) + .receive("timeout", mockCallback) - /* leave */ - private fun setupLeaveTests() { - whenever(socket.isConnected).thenReturn(true) - channel.join().trigger("ok", kEmptyPayload) - } + fakeClock.tick(channel.timeout / 2) + verifyZeroInteractions(mockCallback) - @Test - fun `leave unsubscribes from server events`() { - this.setupLeaveTests() + push.trigger("ok", kEmptyPayload) - val joinRef = channel.joinRef - channel.leave() + fakeClock.tick(channel.timeout) + verifyZeroInteractions(mockCallback) + } - verify(socket).push("topic", "phx_leave", emptyMap(), joinRef, kDefaultRef) + @Test + internal fun `throws if channel has not been joined`() { + var exceptionThrown = false + try { + channel.push("event", kEmptyPayload) + } catch (e: Exception) { + exceptionThrown = true + assertThat(e.message).isEqualTo( + "Tried to push event to topic before joining. Use channel.join() before pushing events") + } + + assertThat(exceptionThrown).isTrue() + } + + /* End PushFunction */ } - @Test - fun `leave closes channel on 'ok' from server`() { - this.setupLeaveTests() + @Nested + @DisplayName("leave") + inner class Leave { + @BeforeEach + internal fun setUp() { + whenever(socket.isConnected).thenReturn(true) + channel.join().trigger("ok", kEmptyPayload) + } - channel.leave().trigger("ok", kEmptyPayload) - verify(socket).remove(channel) - } + @Test + internal fun `unsubscribes from server events`() { + val joinRef = channel.joinRef + channel.leave() - @Test - fun `leave sets state to closed on 'ok' event`() { - this.setupLeaveTests() + verify(socket).push("topic", "phx_leave", emptyMap(), joinRef, kDefaultRef) + } - channel.leave().trigger("ok", kEmptyPayload) - assertThat(channel.state).isEqualTo(Channel.State.CLOSED) - } + @Test + internal fun `closes channel on 'ok' from server`() { + channel.leave().trigger("ok", kEmptyPayload) + verify(socket).remove(channel) + } - @Test - fun `leave sets state to leaving initially`() { - this.setupLeaveTests() + @Test + internal fun `sets state to closed on 'ok' event`() { + channel.leave().trigger("ok", kEmptyPayload) + assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + } - channel.leave() - assertThat(channel.state).isEqualTo(Channel.State.LEAVING) - } + @Test + internal fun `sets state to leaving initially`() { + channel.leave() + assertThat(channel.state).isEqualTo(Channel.State.LEAVING) + } - @Test - fun `leave closes channel on timeout`() { - this.setupLeaveTests() + @Test + internal fun `closes channel on timeout`() { + channel.leave() + assertThat(channel.state).isEqualTo(Channel.State.LEAVING) - channel.leave() - assertThat(channel.state).isEqualTo(Channel.State.LEAVING) + fakeClock.tick(channel.timeout) + assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + } - fakeClock.tick(channel.timeout) - assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + @Test + internal fun `triggers immediately if cannot push`() { + whenever(socket.isConnected).thenReturn(false) + channel.leave() + assertThat(channel.state).isEqualTo(Channel.State.CLOSED) + } + /* End Leave */ } - @Test - fun `leave triggers immediately if cannot push`() { - whenever(socket.isConnected).thenReturn(false) + @Nested + @DisplayName("state accessors") + inner class StateAccessors { + @Test + fun `isClosed returns true if state is CLOSED`() { + channel.state = Channel.State.JOINED + assertThat(channel.isClosed).isFalse() - channel.leave() - assertThat(channel.state).isEqualTo(Channel.State.CLOSED) - } + channel.state = Channel.State.CLOSED + assertThat(channel.isClosed).isTrue() + } - /* State Accessors */ - @Test - fun `isClosed returns true if state is CLOSED`() { - channel.state = Channel.State.JOINED - assertThat(channel.isClosed).isFalse() + @Test + fun `isErrored returns true if state is ERRORED`() { + channel.state = Channel.State.JOINED + assertThat(channel.isErrored).isFalse() - channel.state = Channel.State.CLOSED - assertThat(channel.isClosed).isTrue() - } + channel.state = Channel.State.ERRORED + assertThat(channel.isErrored).isTrue() + } - @Test - fun `isErrored returns true if state is ERRORED`() { - channel.state = Channel.State.JOINED - assertThat(channel.isErrored).isFalse() + @Test + fun `isJoined returns true if state is JOINED`() { + channel.state = Channel.State.JOINING + assertThat(channel.isJoined).isFalse() - channel.state = Channel.State.ERRORED - assertThat(channel.isErrored).isTrue() - } + channel.state = Channel.State.JOINED + assertThat(channel.isJoined).isTrue() + } - @Test - fun `isJoined returns true if state is JOINED`() { - channel.state = Channel.State.JOINING - assertThat(channel.isJoined).isFalse() + @Test + fun `isJoining returns true if state is JOINING`() { + channel.state = Channel.State.JOINED + assertThat(channel.isJoining).isFalse() - channel.state = Channel.State.JOINED - assertThat(channel.isJoined).isTrue() - } + channel.state = Channel.State.JOINING + assertThat(channel.isJoining).isTrue() + } - @Test - fun `isJoining returns true if state is JOINING`() { - channel.state = Channel.State.JOINED - assertThat(channel.isJoining).isFalse() + @Test + fun `isLeaving returns true if state is LEAVING`() { + channel.state = Channel.State.JOINED + assertThat(channel.isLeaving).isFalse() - channel.state = Channel.State.JOINING - assertThat(channel.isJoining).isTrue() + channel.state = Channel.State.LEAVING + assertThat(channel.isLeaving).isTrue() + } + /* End StateAccessors */ } - @Test - fun `isLeaving returns true if state is LEAVING`() { - channel.state = Channel.State.JOINED - assertThat(channel.isLeaving).isFalse() + @Nested + @DisplayName("isMember") + inner class IsMember { - channel.state = Channel.State.LEAVING - assertThat(channel.isLeaving).isTrue() - } + @Test + fun `returns false if topics are different`() { + val message = Message(topic = "other-topic") + assertThat(channel.isMember(message)).isFalse() + } - /* isMember */ - @Test - fun `isMember returns false if topics are different`() { - val message = Message(topic = "other-topic") - assertThat(channel.isMember(message)).isFalse() - } + @Test + fun `drops outdated messages`() { + channel.joinPush.ref = "9" + val message = Message(topic = "topic", event = Channel.Event.LEAVE.value, joinRef = "7") + assertThat(channel.isMember(message)).isFalse() + } - @Test - fun `isMember drops outdated messages`() { - channel.joinPush.ref = "9" - val message = Message(topic = "topic", event = Channel.Event.LEAVE.value, joinRef = "7") - assertThat(channel.isMember(message)).isFalse() - } + @Test + fun `returns true if message belongs to channel`() { + val message = Message(topic = "topic", event = "msg:new") + assertThat(channel.isMember(message)).isTrue() + } - @Test - fun `isMember returns true if message belongs to channel`() { - val message = Message(topic = "topic", event = "msg:new") - assertThat(channel.isMember(message)).isTrue() + /* End IsMember */ } + } \ No newline at end of file diff --git a/src/test/kotlin/org/phoenixframework/ExampleTest.kt b/src/test/kotlin/org/phoenixframework/ExampleTest.kt deleted file mode 100644 index 7689162..0000000 --- a/src/test/kotlin/org/phoenixframework/ExampleTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.phoenixframework - -import com.google.common.truth.Truth.assertThat -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -class ExampleTest { - @Test - internal fun `should fail`() { -// assertThat(true).isFalse() - assertThat(true).isTrue() - } - - @Nested - @DisplayName("nested test") - inner class NestedTest { - - @Test - internal fun `does fail`() { - assertThat(false).isFalse() - } - } -} - diff --git a/src/test/kotlin/org/phoenixframework/MessageTest.kt b/src/test/kotlin/org/phoenixframework/MessageTest.kt index bae47a2..f949b70 100644 --- a/src/test/kotlin/org/phoenixframework/MessageTest.kt +++ b/src/test/kotlin/org/phoenixframework/MessageTest.kt @@ -1,21 +1,27 @@ package org.phoenixframework import com.google.common.truth.Truth.assertThat -import org.junit.Test +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test class MessageTest { - @Test - fun `status returns the status from payload`() { + @Nested + @DisplayName("status") + inner class Status { - val payload = mapOf("one" to "two", "status" to "ok") - val message = Message("ref", "topic", "event", payload, null) + @Test + internal fun `returns the status from the payload`() { + val payload = mapOf("one" to "two", "status" to "ok") + val message = Message("ref", "topic", "event", payload, null) - assertThat(message.ref).isEqualTo("ref") - assertThat(message.topic).isEqualTo("topic") - assertThat(message.event).isEqualTo("event") - assertThat(message.payload).isEqualTo(payload) - assertThat(message.joinRef).isNull() - assertThat(message.status).isEqualTo("ok") + assertThat(message.ref).isEqualTo("ref") + assertThat(message.topic).isEqualTo("topic") + assertThat(message.event).isEqualTo("event") + assertThat(message.payload).isEqualTo(payload) + assertThat(message.joinRef).isNull() + assertThat(message.status).isEqualTo("ok") + } } } \ No newline at end of file diff --git a/src/test/kotlin/org/phoenixframework/PresenceTest.kt b/src/test/kotlin/org/phoenixframework/PresenceTest.kt index 2c1d291..4c41780 100644 --- a/src/test/kotlin/org/phoenixframework/PresenceTest.kt +++ b/src/test/kotlin/org/phoenixframework/PresenceTest.kt @@ -3,10 +3,13 @@ package org.phoenixframework import com.google.common.truth.Truth.assertThat import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.mockito.Mock import org.mockito.MockitoAnnotations +import org.phoenixframework.utilities.getBindings class PresenceTest { @@ -28,8 +31,8 @@ class PresenceTest { lateinit var channel: Channel lateinit var presence: Presence - @Before - fun setUp() { + @BeforeEach + internal fun setUp() { MockitoAnnotations.initMocks(this) whenever(socket.timeout).thenReturn(Defaults.TIMEOUT) @@ -43,407 +46,431 @@ class PresenceTest { presence = Presence(channel) } - /* constructor */ - @Test - fun `constructor() sets defaults`() { - assertThat(presence.state).isEmpty() - assertThat(presence.pendingDiffs).isEmpty() - assertThat(presence.channel).isEqualTo(channel) - assertThat(presence.joinRef).isNull() - } - - @Test - fun `constructor() binds to channel with default arguments`() { - assertThat(presence.channel.getBindings("presence_state")).hasSize(1) - assertThat(presence.channel.getBindings("presence_diff")).hasSize(1) - } - - @Test - fun `constructor() binds to channel with custom options`() { - val channel = Channel("topic", mapOf(), socket) - val customOptions = Presence.Options(mapOf( - Presence.Events.STATE to "custom_state", - Presence.Events.DIFF to "custom_diff")) - - val p = Presence(channel, customOptions) - assertThat(p.channel.getBindings("presence_state")).isEmpty() - assertThat(p.channel.getBindings("presence_diff")).isEmpty() - assertThat(p.channel.getBindings("custom_state")).hasSize(1) - assertThat(p.channel.getBindings("custom_diff")).hasSize(1) - } - - @Test - fun `constructor() syncs state and diffs`() { - val user1: PresenceMap = mutableMapOf("metas" to mutableListOf( - mapOf("id" to 1, "phx_ref" to "1"))) - val user2: PresenceMap = mutableMapOf("metas" to mutableListOf( - mapOf("id" to 2, "phx_ref" to "2"))) - val newState: PresenceState = mutableMapOf("u1" to user1, "u2" to user2) - - channel.trigger("presence_state", newState, "1") - val s = presence.listBy(listByFirst) - assertThat(s).hasSize(2) - assertThat(s[0]["id"]).isEqualTo(1) - assertThat(s[0]["phx_ref"]).isEqualTo("1") - - assertThat(s[1]["id"]).isEqualTo(2) - assertThat(s[1]["phx_ref"]).isEqualTo("2") - - channel.trigger("presence_diff", mapOf("joins" to emptyMap(), "leaves" to mapOf("u1" to user1))) - val l = presence.listBy(listByFirst) - assertThat(l).hasSize(1) - assertThat(l[0]["id"]).isEqualTo(2) - assertThat(l[0]["phx_ref"]).isEqualTo("2") - } - - @Test - fun `constructor() applies pending diff if state is not yet synced`() { - val onJoins = mutableListOf>() - val onLeaves = mutableListOf>() - - presence.onJoin { key, current, new -> onJoins.add(Triple(key, current, new)) } - presence.onLeave { key, current, left -> onLeaves.add(Triple(key, current, left)) } - - val user1 = mutableMapOf("metas" to mutableListOf(mutableMapOf("id" to 1, "phx_ref" to "1"))) - val user2 = mutableMapOf("metas" to mutableListOf(mutableMapOf("id" to 2, "phx_ref" to "2"))) - val user3 = mutableMapOf("metas" to mutableListOf(mutableMapOf("id" to 3, "phx_ref" to "3"))) - - val newState = mutableMapOf("u1" to user1, "u2" to user2) - val leaves = mapOf("u2" to user2) - - val payload1 = mapOf("joins" to emptyMap(), "leaves" to leaves) - channel.trigger("presence_diff", payload1, "") - - // There is no state - assertThat(presence.listBy(listByFirst)).isEmpty() - - // pending diffs 1 - assertThat(presence.pendingDiffs).hasSize(1) - assertThat(presence.pendingDiffs[0]["joins"]).isEmpty() - assertThat(presence.pendingDiffs[0]["leaves"]).isEqualTo(leaves) - - channel.trigger("presence_state", newState, "") - assertThat(onLeaves).hasSize(1) - assertThat(onLeaves[0].first).isEqualTo("u2") - assertThat(onLeaves[0].second["metas"]).isEmpty() - assertThat(onLeaves[0].third["metas"]!![0]["id"]).isEqualTo(2) - - val s = presence.listBy(listByFirst) - assertThat(s).hasSize(1) - assertThat(s[0]["id"]).isEqualTo(1) - assertThat(s[0]["phx_ref"]).isEqualTo("1") - assertThat(presence.pendingDiffs).isEmpty() - - assertThat(onJoins).hasSize(2) - assertThat(onJoins[0].first).isEqualTo("u1") - assertThat(onJoins[0].second).isNull() - assertThat(onJoins[0].third["metas"]!![0]["id"]).isEqualTo(1) - - assertThat(onJoins[1].first).isEqualTo("u2") - assertThat(onJoins[1].second).isNull() - assertThat(onJoins[1].third["metas"]!![0]["id"]).isEqualTo(2) - - // disconnect then reconnect - assertThat(presence.isPendingSyncState).isFalse() - channel.joinPush.ref = "2" - assertThat(presence.isPendingSyncState).isTrue() - - - channel.trigger("presence_diff", - mapOf("joins" to mapOf(), "leaves" to mapOf("u1" to user1))) - val d = presence.listBy(listByFirst) - assertThat(d).hasSize(1) - assertThat(d[0]["id"]).isEqualTo(1) - assertThat(d[0]["phx_ref"]).isEqualTo("1") - - - channel.trigger("presence_state", - mapOf("u1" to user1, "u3" to user3)) - val s2 = presence.listBy(listByFirst) - assertThat(s2).hasSize(1) - assertThat(s2[0]["id"]).isEqualTo(3) - assertThat(s2[0]["phx_ref"]).isEqualTo("3") - } + @Nested + @DisplayName("constructor") + inner class Constructor { - /* sync state */ - @Test - fun `syncState() syncs empty state`() { - val newState: PresenceState = mutableMapOf( - "u1" to mutableMapOf("metas" to listOf(mapOf("id" to 1, "phx_ref" to "1")))) - var state: PresenceState = mutableMapOf() - val stateBefore = state - - Presence.syncState(state, newState) - assertThat(state).isEqualTo(stateBefore) - - state = Presence.syncState(state, newState) - assertThat(state).isEqualTo(newState) - } - - @Test - fun `syncState() onJoins new presences and onLeaves left presences`() { - val newState = fixState - var state = mutableMapOf( - "u4" to mutableMapOf("metas" to listOf(mapOf("id" to 4, "phx_ref" to "4")))) - - val joined: PresenceDiff = mutableMapOf() - val left: PresenceDiff = mutableMapOf() + @Test + internal fun `sets defaults`() { + assertThat(presence.state).isEmpty() + assertThat(presence.pendingDiffs).isEmpty() + assertThat(presence.channel).isEqualTo(channel) + assertThat(presence.joinRef).isNull() + } - val onJoin: OnJoin = { key, current, newPres -> - val joinState: PresenceState = mutableMapOf("newPres" to newPres) - current?.let { c -> joinState["current"] = c } + @Test + internal fun `binds to channel with default arguments`() { + assertThat(presence.channel.getBindings("presence_state")).hasSize(1) + assertThat(presence.channel.getBindings("presence_diff")).hasSize(1) + } - joined[key] = joinState + @Test + internal fun `binds to channel with custom options`() { + val channel = Channel("topic", mapOf(), socket) + val customOptions = Presence.Options(mapOf( + Presence.Events.STATE to "custom_state", + Presence.Events.DIFF to "custom_diff")) + + val p = Presence(channel, customOptions) + assertThat(p.channel.getBindings("presence_state")).isEmpty() + assertThat(p.channel.getBindings("presence_diff")).isEmpty() + assertThat(p.channel.getBindings("custom_state")).hasSize(1) + assertThat(p.channel.getBindings("custom_diff")).hasSize(1) } - val onLeave: OnLeave = { key, current, leftPres -> - left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + @Test + internal fun `syncs state and diffs`() { + val user1: PresenceMap = mutableMapOf("metas" to mutableListOf( + mapOf("id" to 1, "phx_ref" to "1"))) + val user2: PresenceMap = mutableMapOf("metas" to mutableListOf( + mapOf("id" to 2, "phx_ref" to "2"))) + val newState: PresenceState = mutableMapOf("u1" to user1, "u2" to user2) + + channel.trigger("presence_state", newState, "1") + val s = presence.listBy(listByFirst) + assertThat(s).hasSize(2) + assertThat(s[0]["id"]).isEqualTo(1) + assertThat(s[0]["phx_ref"]).isEqualTo("1") + + assertThat(s[1]["id"]).isEqualTo(2) + assertThat(s[1]["phx_ref"]).isEqualTo("2") + + channel.trigger("presence_diff", + mapOf("joins" to emptyMap(), "leaves" to mapOf("u1" to user1))) + val l = presence.listBy(listByFirst) + assertThat(l).hasSize(1) + assertThat(l[0]["id"]).isEqualTo(2) + assertThat(l[0]["phx_ref"]).isEqualTo("2") } - val stateBefore = state - Presence.syncState(state, newState, onJoin, onLeave) - assertThat(state).isEqualTo(stateBefore) - - state = Presence.syncState(state, newState, onJoin, onLeave) - assertThat(state).isEqualTo(newState) - - // asset equality in joined - val joinedExpectation: PresenceDiff = mutableMapOf( - "u1" to mutableMapOf("newPres" to mutableMapOf( - "metas" to listOf(mapOf("id" to 1, "phx_ref" to "1")))), - "u2" to mutableMapOf("newPres" to mutableMapOf( - "metas" to listOf(mapOf("id" to 2, "phx_ref" to "2")))), - "u3" to mutableMapOf("newPres" to mutableMapOf( - "metas" to listOf(mapOf("id" to 3, "phx_ref" to "3")))) - ) - - assertThat(joined).isEqualTo(joinedExpectation) - - // assert equality in left - val leftExpectation: PresenceDiff = mutableMapOf( - "u4" to mutableMapOf( - "current" to mutableMapOf( - "metas" to mutableListOf()), - "leftPres" to mutableMapOf( - "metas" to listOf(mapOf("id" to 4, "phx_ref" to "4")))) - ) - assertThat(left).isEqualTo(leftExpectation) + @Test + internal fun `applies pending diff if state is not yet synced`() { + val onJoins = mutableListOf>() + val onLeaves = mutableListOf>() + + presence.onJoin { key, current, new -> onJoins.add(Triple(key, current, new)) } + presence.onLeave { key, current, left -> onLeaves.add(Triple(key, current, left)) } + + val user1 = mutableMapOf("metas" to mutableListOf(mutableMapOf("id" to 1, "phx_ref" to "1"))) + val user2 = mutableMapOf("metas" to mutableListOf(mutableMapOf("id" to 2, "phx_ref" to "2"))) + val user3 = mutableMapOf("metas" to mutableListOf(mutableMapOf("id" to 3, "phx_ref" to "3"))) + + val newState = mutableMapOf("u1" to user1, "u2" to user2) + val leaves = mapOf("u2" to user2) + + val payload1 = mapOf("joins" to emptyMap(), "leaves" to leaves) + channel.trigger("presence_diff", payload1, "") + + // There is no state + assertThat(presence.listBy(listByFirst)).isEmpty() + + // pending diffs 1 + assertThat(presence.pendingDiffs).hasSize(1) + assertThat(presence.pendingDiffs[0]["joins"]).isEmpty() + assertThat(presence.pendingDiffs[0]["leaves"]).isEqualTo(leaves) + + channel.trigger("presence_state", newState, "") + assertThat(onLeaves).hasSize(1) + assertThat(onLeaves[0].first).isEqualTo("u2") + assertThat(onLeaves[0].second["metas"]).isEmpty() + assertThat(onLeaves[0].third["metas"]!![0]["id"]).isEqualTo(2) + + val s = presence.listBy(listByFirst) + assertThat(s).hasSize(1) + assertThat(s[0]["id"]).isEqualTo(1) + assertThat(s[0]["phx_ref"]).isEqualTo("1") + assertThat(presence.pendingDiffs).isEmpty() + + assertThat(onJoins).hasSize(2) + assertThat(onJoins[0].first).isEqualTo("u1") + assertThat(onJoins[0].second).isNull() + assertThat(onJoins[0].third["metas"]!![0]["id"]).isEqualTo(1) + + assertThat(onJoins[1].first).isEqualTo("u2") + assertThat(onJoins[1].second).isNull() + assertThat(onJoins[1].third["metas"]!![0]["id"]).isEqualTo(2) + + // disconnect then reconnect + assertThat(presence.isPendingSyncState).isFalse() + channel.joinPush.ref = "2" + assertThat(presence.isPendingSyncState).isTrue() + + + channel.trigger("presence_diff", + mapOf("joins" to mapOf(), "leaves" to mapOf("u1" to user1))) + val d = presence.listBy(listByFirst) + assertThat(d).hasSize(1) + assertThat(d[0]["id"]).isEqualTo(1) + assertThat(d[0]["phx_ref"]).isEqualTo("1") + + + channel.trigger("presence_state", + mapOf("u1" to user1, "u3" to user3)) + val s2 = presence.listBy(listByFirst) + assertThat(s2).hasSize(1) + assertThat(s2[0]["id"]).isEqualTo(3) + assertThat(s2[0]["phx_ref"]).isEqualTo("3") + } + /* End Constructor */ } - @Test - fun `syncState() onJoins only newly added metas`() { - var state = mutableMapOf( - "u3" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3")))) - val newState = mutableMapOf( - "u3" to mutableMapOf("metas" to listOf( - mapOf("id" to 3, "phx_ref" to "3"), - mapOf("id" to 3, "phx_ref" to "3.new") - ))) + @Nested + @DisplayName("syncState") + inner class SyncState { - val joined: PresenceDiff = mutableMapOf() - val left: PresenceDiff = mutableMapOf() + @Test + internal fun `syncs empty state`() { + val newState: PresenceState = mutableMapOf( + "u1" to mutableMapOf("metas" to listOf(mapOf("id" to 1, "phx_ref" to "1")))) + var state: PresenceState = mutableMapOf() + val stateBefore = state - val onJoin: OnJoin = { key, current, newPres -> - val joinState: PresenceState = mutableMapOf("newPres" to newPres) - current?.let { c -> joinState["current"] = c } + Presence.syncState(state, newState) + assertThat(state).isEqualTo(stateBefore) - joined[key] = joinState + state = Presence.syncState(state, newState) + assertThat(state).isEqualTo(newState) } - val onLeave: OnLeave = { key, current, leftPres -> - left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + @Test + internal fun `onJoins new presences and onLeaves left presences`() { + val newState = fixState + var state = mutableMapOf( + "u4" to mutableMapOf("metas" to listOf(mapOf("id" to 4, "phx_ref" to "4")))) + + val joined: PresenceDiff = mutableMapOf() + val left: PresenceDiff = mutableMapOf() + + val onJoin: OnJoin = { key, current, newPres -> + val joinState: PresenceState = mutableMapOf("newPres" to newPres) + current?.let { c -> joinState["current"] = c } + + joined[key] = joinState + } + + val onLeave: OnLeave = { key, current, leftPres -> + left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + } + + val stateBefore = state + Presence.syncState(state, newState, onJoin, onLeave) + assertThat(state).isEqualTo(stateBefore) + + state = Presence.syncState(state, newState, onJoin, onLeave) + assertThat(state).isEqualTo(newState) + + // asset equality in joined + val joinedExpectation: PresenceDiff = mutableMapOf( + "u1" to mutableMapOf("newPres" to mutableMapOf( + "metas" to listOf(mapOf("id" to 1, "phx_ref" to "1")))), + "u2" to mutableMapOf("newPres" to mutableMapOf( + "metas" to listOf(mapOf("id" to 2, "phx_ref" to "2")))), + "u3" to mutableMapOf("newPres" to mutableMapOf( + "metas" to listOf(mapOf("id" to 3, "phx_ref" to "3")))) + ) + + assertThat(joined).isEqualTo(joinedExpectation) + + // assert equality in left + val leftExpectation: PresenceDiff = mutableMapOf( + "u4" to mutableMapOf( + "current" to mutableMapOf( + "metas" to mutableListOf()), + "leftPres" to mutableMapOf( + "metas" to listOf(mapOf("id" to 4, "phx_ref" to "4")))) + ) + assertThat(left).isEqualTo(leftExpectation) } - state = Presence.syncState(state, newState, onJoin, onLeave) - assertThat(state).isEqualTo(newState) + @Test + internal fun `onJoins only newly added metas`() { + var state = mutableMapOf( + "u3" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3")))) + val newState = mutableMapOf( + "u3" to mutableMapOf("metas" to listOf( + mapOf("id" to 3, "phx_ref" to "3"), + mapOf("id" to 3, "phx_ref" to "3.new") + ))) - // asset equality in joined - val joinedExpectation: PresenceDiff = mutableMapOf( - "u3" to mutableMapOf( - "newPres" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.new"))), - "current" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3"))) + val joined: PresenceDiff = mutableMapOf() + val left: PresenceDiff = mutableMapOf() - )) - assertThat(joined).isEqualTo(joinedExpectation) + val onJoin: OnJoin = { key, current, newPres -> + val joinState: PresenceState = mutableMapOf("newPres" to newPres) + current?.let { c -> joinState["current"] = c } - // assert equality in left - assertThat(left).isEmpty() - } + joined[key] = joinState + } - @Test - fun `syncState() onLeaves only newly removed metas`() { - val newState = mutableMapOf( - "u3" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3")))) - var state = mutableMapOf( - "u3" to mutableMapOf("metas" to listOf( - mapOf("id" to 3, "phx_ref" to "3"), - mapOf("id" to 3, "phx_ref" to "3.left") - ))) + val onLeave: OnLeave = { key, current, leftPres -> + left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + } - val joined: PresenceDiff = mutableMapOf() - val left: PresenceDiff = mutableMapOf() + state = Presence.syncState(state, newState, onJoin, onLeave) + assertThat(state).isEqualTo(newState) - val onJoin: OnJoin = { key, current, newPres -> - val joinState: PresenceState = mutableMapOf("newPres" to newPres) - current?.let { c -> joinState["current"] = c } + // asset equality in joined + val joinedExpectation: PresenceDiff = mutableMapOf( + "u3" to mutableMapOf( + "newPres" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.new"))), + "current" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3"))) - joined[key] = joinState - } + )) + assertThat(joined).isEqualTo(joinedExpectation) - val onLeave: OnLeave = { key, current, leftPres -> - left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + // assert equality in left + assertThat(left).isEmpty() } - state = Presence.syncState(state, newState, onJoin, onLeave) - assertThat(state).isEqualTo(newState) - - // asset equality in joined - val leftExpectation: PresenceDiff = mutableMapOf( - "u3" to mutableMapOf( - "leftPres" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.left"))), - "current" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3"))) + @Test + internal fun `onLeaves only newly removed metas`() { + val newState = mutableMapOf( + "u3" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3")))) + var state = mutableMapOf( + "u3" to mutableMapOf("metas" to listOf( + mapOf("id" to 3, "phx_ref" to "3"), + mapOf("id" to 3, "phx_ref" to "3.left") + ))) + + val joined: PresenceDiff = mutableMapOf() + val left: PresenceDiff = mutableMapOf() + + val onJoin: OnJoin = { key, current, newPres -> + val joinState: PresenceState = mutableMapOf("newPres" to newPres) + current?.let { c -> joinState["current"] = c } + + joined[key] = joinState + } + + val onLeave: OnLeave = { key, current, leftPres -> + left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + } + + state = Presence.syncState(state, newState, onJoin, onLeave) + assertThat(state).isEqualTo(newState) + + // asset equality in joined + val leftExpectation: PresenceDiff = mutableMapOf( + "u3" to mutableMapOf( + "leftPres" to mutableMapOf( + "metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.left"))), + "current" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3"))) + + )) + assertThat(left).isEqualTo(leftExpectation) + + // assert equality in left + assertThat(joined).isEmpty() + } - )) - assertThat(left).isEqualTo(leftExpectation) + @Test + internal fun `syncs both joined and left metas`() { + val newState = mutableMapOf( + "u3" to mutableMapOf("metas" to listOf( + mapOf("id" to 3, "phx_ref" to "3"), + mapOf("id" to 3, "phx_ref" to "3.new") + ))) + + var state = mutableMapOf( + "u3" to mutableMapOf("metas" to listOf( + mapOf("id" to 3, "phx_ref" to "3"), + mapOf("id" to 3, "phx_ref" to "3.left") + ))) + + val joined: PresenceDiff = mutableMapOf() + val left: PresenceDiff = mutableMapOf() + + val onJoin: OnJoin = { key, current, newPres -> + val joinState: PresenceState = mutableMapOf("newPres" to newPres) + current?.let { c -> joinState["current"] = c } + + joined[key] = joinState + } + + val onLeave: OnLeave = { key, current, leftPres -> + left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + } + + state = Presence.syncState(state, newState, onJoin, onLeave) + assertThat(state).isEqualTo(newState) + + // asset equality in joined + val joinedExpectation: PresenceDiff = mutableMapOf( + "u3" to mutableMapOf( + "newPres" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.new"))), + "current" to mutableMapOf("metas" to listOf( + mapOf("id" to 3, "phx_ref" to "3"), + mapOf("id" to 3, "phx_ref" to "3.left"))) + )) + assertThat(joined).isEqualTo(joinedExpectation) + + // assert equality in left + val leftExpectation: PresenceDiff = mutableMapOf( + "u3" to mutableMapOf( + "leftPres" to mutableMapOf( + "metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.left"))), + "current" to mutableMapOf("metas" to listOf( + mapOf("id" to 3, "phx_ref" to "3"), + mapOf("id" to 3, "phx_ref" to "3.new"))) + )) + assertThat(left).isEqualTo(leftExpectation) + } - // assert equality in left - assertThat(joined).isEmpty() + /* end SyncState */ } - @Test - fun `syncState() syncs both joined and left metas`() { - - val newState = mutableMapOf( - "u3" to mutableMapOf("metas" to listOf( - mapOf("id" to 3, "phx_ref" to "3"), - mapOf("id" to 3, "phx_ref" to "3.new") - ))) + @Nested + @DisplayName("syncDiff") + inner class SyncDiff { - var state = mutableMapOf( - "u3" to mutableMapOf("metas" to listOf( - mapOf("id" to 3, "phx_ref" to "3"), - mapOf("id" to 3, "phx_ref" to "3.left") - ))) + @Test + internal fun `syncs empty state`() { + val joins: PresenceState = mutableMapOf( + "u1" to mutableMapOf("metas" to + listOf(mapOf("id" to 1, "phx_ref" to "1")))) + var state: PresenceState = mutableMapOf() - val joined: PresenceDiff = mutableMapOf() - val left: PresenceDiff = mutableMapOf() + Presence.syncDiff(state, mutableMapOf("joins" to joins, "leaves" to mutableMapOf())) + assertThat(state).isEmpty() - val onJoin: OnJoin = { key, current, newPres -> - val joinState: PresenceState = mutableMapOf("newPres" to newPres) - current?.let { c -> joinState["current"] = c } - - joined[key] = joinState + state = Presence.syncDiff(state, mutableMapOf("joins" to joins, "leaves" to mutableMapOf())) + assertThat(state).isEqualTo(joins) } - val onLeave: OnLeave = { key, current, leftPres -> - left[key] = mutableMapOf("current" to current, "leftPres" to leftPres) + @Test + internal fun `removes presence when meta is empty and adds additional meta`() { + var state = fixState + val diff: PresenceDiff = mutableMapOf("joins" to fixJoins, "leaves" to fixLeaves) + state = Presence.syncDiff(state, diff) + + val expectation: PresenceState = mutableMapOf( + "u1" to mutableMapOf("metas" to + listOf( + mapOf("id" to 1, "phx_ref" to "1"), + mapOf("id" to 1, "phx_ref" to "1.2") + ) + ), + "u3" to mutableMapOf("metas" to + listOf(mapOf("id" to 3, "phx_ref" to "3")) + ) + ) + + assertThat(state).isEqualTo(expectation) } - state = Presence.syncState(state, newState, onJoin, onLeave) - assertThat(state).isEqualTo(newState) - - // asset equality in joined - val joinedExpectation: PresenceDiff = mutableMapOf( - "u3" to mutableMapOf( - "newPres" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.new"))), - "current" to mutableMapOf("metas" to listOf( - mapOf("id" to 3, "phx_ref" to "3"), - mapOf("id" to 3, "phx_ref" to "3.left"))) - )) - assertThat(joined).isEqualTo(joinedExpectation) - - // assert equality in left - val leftExpectation: PresenceDiff = mutableMapOf( - "u3" to mutableMapOf( - "leftPres" to mutableMapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3.left"))), - "current" to mutableMapOf("metas" to listOf( - mapOf("id" to 3, "phx_ref" to "3"), - mapOf("id" to 3, "phx_ref" to "3.new"))) - )) - assertThat(left).isEqualTo(leftExpectation) - } - - /* syncDiff */ - @Test - fun `syncDiff() syncs empty state`() { - val joins: PresenceState = mutableMapOf( - "u1" to mutableMapOf("metas" to - listOf(mapOf("id" to 1, "phx_ref" to "1")))) - var state: PresenceState = mutableMapOf() - - Presence.syncDiff(state, mutableMapOf("joins" to joins, "leaves" to mutableMapOf())) - assertThat(state).isEmpty() + @Test + internal fun `removes meta while leaving key if other metas exist`() { + var state = mutableMapOf( + "u1" to mutableMapOf("metas" to listOf( + mapOf("id" to 1, "phx_ref" to "1"), + mapOf("id" to 1, "phx_ref" to "1.2") + ))) + + val leaves = mutableMapOf( + "u1" to mutableMapOf("metas" to listOf( + mapOf("id" to 1, "phx_ref" to "1") + ))) + val diff: PresenceDiff = mutableMapOf("joins" to mutableMapOf(), "leaves" to leaves) + state = Presence.syncDiff(state, diff) + + val expectedState = mutableMapOf( + "u1" to mutableMapOf("metas" to listOf(mapOf("id" to 1, "phx_ref" to "1.2")))) + assertThat(state).isEqualTo(expectedState) + } - state = Presence.syncDiff(state, mutableMapOf("joins" to joins, "leaves" to mutableMapOf())) - assertThat(state).isEqualTo(joins) + /* End SyncDiff */ } - @Test - fun `syncDiff() removes presence when meta is empty and adds additional meta`() { - var state = fixState - val diff: PresenceDiff = mutableMapOf("joins" to fixJoins, "leaves" to fixLeaves) - state = Presence.syncDiff(state, diff) - - val expectation: PresenceState = mutableMapOf( - "u1" to mutableMapOf("metas" to - listOf( - mapOf("id" to 1, "phx_ref" to "1"), - mapOf("id" to 1, "phx_ref" to "1.2") - ) - ), - "u3" to mutableMapOf("metas" to - listOf(mapOf("id" to 3, "phx_ref" to "3")) - ) - ) - - assertThat(state).isEqualTo(expectation) - } + @Nested + @DisplayName("listBy") + inner class ListBy { - @Test - fun `syncDiff() removes meta while leaving key if other metas exist`() { - var state = mutableMapOf( - "u1" to mutableMapOf("metas" to listOf( - mapOf("id" to 1, "phx_ref" to "1"), - mapOf("id" to 1, "phx_ref" to "1.2") - ))) - - val leaves = mutableMapOf( - "u1" to mutableMapOf("metas" to listOf( - mapOf("id" to 1, "phx_ref" to "1") - ))) - val diff: PresenceDiff = mutableMapOf("joins" to mutableMapOf(), "leaves" to leaves) - state = Presence.syncDiff(state, diff) - - val expectedState = mutableMapOf( - "u1" to mutableMapOf("metas" to listOf(mapOf("id" to 1, "phx_ref" to "1.2")))) - assertThat(state).isEqualTo(expectedState) - } + @Test + internal fun `lists full presence by default`() { + presence.state = fixState - /* listBy */ - @Test - fun `listBy() lists full presence by default`() { - presence.state = fixState + val listExpectation = listOf( + mapOf("metas" to listOf(mapOf("id" to 1, "phx_ref" to "1"))), + mapOf("metas" to listOf(mapOf("id" to 2, "phx_ref" to "2"))), + mapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3"))) + ) - val listExpectation = listOf( - mapOf("metas" to listOf(mapOf("id" to 1, "phx_ref" to "1"))), - mapOf("metas" to listOf(mapOf("id" to 2, "phx_ref" to "2"))), - mapOf("metas" to listOf(mapOf("id" to 3, "phx_ref" to "3"))) - ) + assertThat(presence.list()).isEqualTo(listExpectation) + } - assertThat(presence.list()).isEqualTo(listExpectation) + @Test + internal fun `lists with custom function`() { + val state: PresenceState = mutableMapOf( + "u1" to mutableMapOf("metas" to listOf( + mapOf("id" to 1, "phx_ref" to "1.first"), + mapOf("id" to 1, "phx_ref" to "1.second")) + ) + ) + + presence.state = state + val listBy = presence.listBy { it.value["metas"]!!.first() } + assertThat(listBy).isEqualTo(listOf(mapOf("id" to 1, "phx_ref" to "1.first"))) + } } - @Test - fun `listBy() lists with custom function`() { - val state: PresenceState = mutableMapOf( - "u1" to mutableMapOf("metas" to listOf( - mapOf("id" to 1, "phx_ref" to "1.first"), - mapOf("id" to 1, "phx_ref" to "1.second")) - ) - ) - - presence.state = state - val listBy = presence.listBy { it.value["metas"]!!.first() } - assertThat(listBy).isEqualTo(listOf(mapOf("id" to 1, "phx_ref" to "1.first"))) - } } \ No newline at end of file diff --git a/src/test/kotlin/org/phoenixframework/SocketTest.kt b/src/test/kotlin/org/phoenixframework/SocketTest.kt index 620d501..50385ab 100644 --- a/src/test/kotlin/org/phoenixframework/SocketTest.kt +++ b/src/test/kotlin/org/phoenixframework/SocketTest.kt @@ -13,8 +13,10 @@ import com.nhaarman.mockitokotlin2.verifyZeroInteractions import com.nhaarman.mockitokotlin2.whenever import okhttp3.OkHttpClient import okhttp3.Response -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.mockito.Mock import org.mockito.MockitoAnnotations import java.net.URL @@ -28,8 +30,8 @@ class SocketTest { lateinit var connection: Transport lateinit var socket: Socket - @Before - fun setUp() { + @BeforeEach + internal fun setUp() { MockitoAnnotations.initMocks(this) connection = spy(WebSocketTransport(URL("https://localhost:4000/socket"), okHttpClient)) @@ -37,647 +39,730 @@ class SocketTest { socket = Socket("wss://localhost:4000/socket") socket.transport = { connection } socket.dispatchQueue = mockDispatchQueue - } - /* constructor */ - @Test - fun `constructor sets defaults`() { - val socket = Socket("wss://localhost:4000/socket") - - assertThat(socket.params).isNull() - assertThat(socket.channels).isEmpty() - assertThat(socket.sendBuffer).isEmpty() - assertThat(socket.ref).isEqualTo(0) - assertThat(socket.endpoint).isEqualTo("wss://localhost:4000/socket/websocket") - assertThat(socket.stateChangeCallbacks.open).isEmpty() - assertThat(socket.stateChangeCallbacks.close).isEmpty() - assertThat(socket.stateChangeCallbacks.error).isEmpty() - assertThat(socket.stateChangeCallbacks.message).isEmpty() - assertThat(socket.timeout).isEqualTo(Defaults.TIMEOUT) - assertThat(socket.heartbeatInterval).isEqualTo(Defaults.HEARTBEAT) - assertThat(socket.logger).isNull() - assertThat(socket.reconnectAfterMs(1)).isEqualTo(1000) - assertThat(socket.reconnectAfterMs(2)).isEqualTo(2000) - assertThat(socket.reconnectAfterMs(3)).isEqualTo(5000) - assertThat(socket.reconnectAfterMs(4)).isEqualTo(10000) - assertThat(socket.reconnectAfterMs(5)).isEqualTo(10000) - } + @Nested + @DisplayName("constructor") + inner class Constructor { + @Test + internal fun `sets defaults`() { + val socket = Socket("wss://localhost:4000/socket") + + assertThat(socket.params).isNull() + assertThat(socket.channels).isEmpty() + assertThat(socket.sendBuffer).isEmpty() + assertThat(socket.ref).isEqualTo(0) + assertThat(socket.endpoint).isEqualTo("wss://localhost:4000/socket/websocket") + assertThat(socket.stateChangeCallbacks.open).isEmpty() + assertThat(socket.stateChangeCallbacks.close).isEmpty() + assertThat(socket.stateChangeCallbacks.error).isEmpty() + assertThat(socket.stateChangeCallbacks.message).isEmpty() + assertThat(socket.timeout).isEqualTo(Defaults.TIMEOUT) + assertThat(socket.heartbeatInterval).isEqualTo(Defaults.HEARTBEAT) + assertThat(socket.logger).isNull() + assertThat(socket.reconnectAfterMs(1)).isEqualTo(1000) + assertThat(socket.reconnectAfterMs(2)).isEqualTo(2000) + assertThat(socket.reconnectAfterMs(3)).isEqualTo(5000) + assertThat(socket.reconnectAfterMs(4)).isEqualTo(10000) + assertThat(socket.reconnectAfterMs(5)).isEqualTo(10000) + } - @Test - fun `constructor overrides some defaults`() { - val socket = Socket("wss://localhost:4000/socket/", mapOf("one" to 2)) - socket.timeout = 40_000 - socket.heartbeatInterval = 60_000 - socket.logger = { } - socket.reconnectAfterMs = { 10 } - - assertThat(socket.params).isEqualTo(mapOf("one" to 2)) - assertThat(socket.endpoint).isEqualTo("wss://localhost:4000/socket/websocket") - assertThat(socket.timeout).isEqualTo(40_000) - assertThat(socket.heartbeatInterval).isEqualTo(60_000) - assertThat(socket.logger).isNotNull() - assertThat(socket.reconnectAfterMs(1)).isEqualTo(10) - assertThat(socket.reconnectAfterMs(2)).isEqualTo(10) - } + @Test + internal fun `overrides some defaults`() { + val socket = Socket("wss://localhost:4000/socket/", mapOf("one" to 2)) + socket.timeout = 40_000 + socket.heartbeatInterval = 60_000 + socket.logger = { } + socket.reconnectAfterMs = { 10 } + + assertThat(socket.params).isEqualTo(mapOf("one" to 2)) + assertThat(socket.endpoint).isEqualTo("wss://localhost:4000/socket/websocket") + assertThat(socket.timeout).isEqualTo(40_000) + assertThat(socket.heartbeatInterval).isEqualTo(60_000) + assertThat(socket.logger).isNotNull() + assertThat(socket.reconnectAfterMs(1)).isEqualTo(10) + assertThat(socket.reconnectAfterMs(2)).isEqualTo(10) + } + + @Test + internal fun `constructs with a valid URL`() { + // Test different schemes + assertThat(Socket("http://localhost:4000/socket/websocket").endpointUrl.toString()) + .isEqualTo("http://localhost:4000/socket/websocket") - @Test - fun `constructor constructs with a valid URL`() { - // Test different schemes - assertThat(Socket("http://localhost:4000/socket/websocket").endpointUrl.toString()) - .isEqualTo("http://localhost:4000/socket/websocket") + assertThat(Socket("https://localhost:4000/socket/websocket").endpointUrl.toString()) + .isEqualTo("https://localhost:4000/socket/websocket") - assertThat(Socket("https://localhost:4000/socket/websocket").endpointUrl.toString()) - .isEqualTo("https://localhost:4000/socket/websocket") + assertThat(Socket("ws://localhost:4000/socket/websocket").endpointUrl.toString()) + .isEqualTo("http://localhost:4000/socket/websocket") - assertThat(Socket("ws://localhost:4000/socket/websocket").endpointUrl.toString()) - .isEqualTo("http://localhost:4000/socket/websocket") + assertThat(Socket("wss://localhost:4000/socket/websocket").endpointUrl.toString()) + .isEqualTo("https://localhost:4000/socket/websocket") - assertThat(Socket("wss://localhost:4000/socket/websocket").endpointUrl.toString()) - .isEqualTo("https://localhost:4000/socket/websocket") + // test params + val singleParam = hashMapOf("token" to "abc123") + assertThat(Socket("ws://localhost:4000/socket/websocket", singleParam).endpointUrl.toString()) + .isEqualTo("http://localhost:4000/socket/websocket?token=abc123") - // test params - val singleParam = hashMapOf("token" to "abc123") - assertThat(Socket("ws://localhost:4000/socket/websocket", singleParam).endpointUrl.toString()) - .isEqualTo("http://localhost:4000/socket/websocket?token=abc123") + val multipleParams = hashMapOf("token" to "abc123", "user_id" to 1) + assertThat( + Socket("http://localhost:4000/socket/websocket", multipleParams).endpointUrl.toString()) + .isEqualTo("http://localhost:4000/socket/websocket?user_id=1&token=abc123") - val multipleParams = hashMapOf("token" to "abc123", "user_id" to 1) - assertThat( - Socket("http://localhost:4000/socket/websocket", multipleParams).endpointUrl.toString()) - .isEqualTo("http://localhost:4000/socket/websocket?user_id=1&token=abc123") + // test params with spaces + val spacesParams = hashMapOf("token" to "abc 123", "user_id" to 1) + assertThat( + Socket("wss://localhost:4000/socket/websocket", spacesParams).endpointUrl.toString()) + .isEqualTo("https://localhost:4000/socket/websocket?user_id=1&token=abc%20123") + } - // test params with spaces - val spacesParams = hashMapOf("token" to "abc 123", "user_id" to 1) - assertThat(Socket("wss://localhost:4000/socket/websocket", spacesParams).endpointUrl.toString()) - .isEqualTo("https://localhost:4000/socket/websocket?user_id=1&token=abc%20123") + /* End Constructor */ } + @Nested + @DisplayName("protocol") + inner class Protocol { + @Test + internal fun `returns wss when protocol is https`() { + val socket = Socket("https://example.com/") + assertThat(socket.protocol).isEqualTo("wss") + } - /* protocol */ - @Test - fun `protocol returns wss when protocol is https`() { - val socket = Socket("https://example.com/") - assertThat(socket.protocol).isEqualTo("wss") - } + @Test + internal fun `returns ws when protocol is http`() { + val socket = Socket("http://example.com/") + assertThat(socket.protocol).isEqualTo("ws") + } - @Test - fun `protocol returns ws when protocol is http`() { - val socket = Socket("http://example.com/") - assertThat(socket.protocol).isEqualTo("ws") - } + @Test + internal fun `returns value if not https or http`() { + val socket = Socket("wss://example.com/") + assertThat(socket.protocol).isEqualTo("wss") + } - @Test - fun `protocol returns value if not https or http`() { - val socket = Socket("wss://example.com/") - assertThat(socket.protocol).isEqualTo("wss") + /* End Protocol */ } - /* isConnected */ - @Test - fun `isConnected returns false if connection is null`() { - assertThat(socket.isConnected).isFalse() - } + @Nested + @DisplayName("isConnected") + inner class IsConnected { + @Test + internal fun `returns false if connection is null`() { + assertThat(socket.isConnected).isFalse() + } - @Test - fun `isConnected is false if state is not open`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.CLOSING) + @Test + internal fun `is false if state is not open`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.CLOSING) - socket.connection = connection - assertThat(socket.isConnected).isFalse() - } + socket.connection = connection + assertThat(socket.isConnected).isFalse() + } - @Test - fun `isConnected is true if state open`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + @Test + internal fun `is true if state open`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + + socket.connection = connection + assertThat(socket.isConnected).isTrue() + } - socket.connection = connection - assertThat(socket.isConnected).isTrue() + /* End IsConnected */ } + @Nested + @DisplayName("connect") + inner class Connect { + @Test + internal fun `establishes websocket connection with endpoint`() { + socket.connect() + assertThat(socket.connection).isNotNull() + } - /* connect() */ - @Test - fun `connect() establishes websocket connection with endpoint`() { - socket.connect() - assertThat(socket.connection).isNotNull() - } + @Test + internal fun `sets callbacks for connection`() { + var open = 0 + socket.onOpen { open += 1 } - @Test - fun `connect() sets callbacks for connection`() { - var open = 0 - socket.onOpen { open += 1 } + var close = 0 + socket.onClose { close += 1 } - var close = 0 - socket.onClose { close += 1 } + var lastError: Throwable? = null + var lastResponse: Response? = null + socket.onError { throwable, response -> + lastError = throwable + lastResponse = response + } - var lastError: Throwable? = null - var lastResponse: Response? = null - socket.onError { throwable, response -> - lastError = throwable - lastResponse = response - } + var lastMessage: Message? = null + socket.onMessage { lastMessage = it } - var lastMessage: Message? = null - socket.onMessage { lastMessage = it } + socket.connect() - socket.connect() + socket.connection?.onOpen?.invoke() + assertThat(open).isEqualTo(1) - socket.connection?.onOpen?.invoke() - assertThat(open).isEqualTo(1) + socket.connection?.onClose?.invoke(1000) + assertThat(close).isEqualTo(1) - socket.connection?.onClose?.invoke(1000) - assertThat(close).isEqualTo(1) + socket.connection?.onError?.invoke(Throwable(), null) + assertThat(lastError).isNotNull() + assertThat(lastResponse).isNull() - socket.connection?.onError?.invoke(Throwable(), null) - assertThat(lastError).isNotNull() - assertThat(lastResponse).isNull() + val data = mapOf( + "topic" to "topic", + "event" to "event", + "payload" to mapOf("go" to true), + "status" to "status" + ) - val data = mapOf( - "topic" to "topic", - "event" to "event", - "payload" to mapOf("go" to true), - "status" to "status" - ) + val json = Defaults.gson.toJson(data) + socket.connection?.onMessage?.invoke(json) + assertThat(lastMessage?.payload).isEqualTo(mapOf("go" to true)) + } - val json = Defaults.gson.toJson(data) - socket.connection?.onMessage?.invoke(json) - assertThat(lastMessage?.payload).isEqualTo(mapOf("go" to true)) - } + @Test + internal fun `removes callbacks`() { + var open = 0 + socket.onOpen { open += 1 } - @Test - fun `connect() removes callbacks`() { - var open = 0 - socket.onOpen { open += 1 } + var close = 0 + socket.onClose { close += 1 } - var close = 0 - socket.onClose { close += 1 } + var lastError: Throwable? = null + var lastResponse: Response? = null + socket.onError { throwable, response -> + lastError = throwable + lastResponse = response + } - var lastError: Throwable? = null - var lastResponse: Response? = null - socket.onError { throwable, response -> - lastError = throwable - lastResponse = response - } + var lastMessage: Message? = null + socket.onMessage { lastMessage = it } - var lastMessage: Message? = null - socket.onMessage { lastMessage = it } + socket.removeAllCallbacks() + socket.connect() - socket.removeAllCallbacks() - socket.connect() + socket.connection?.onOpen?.invoke() + assertThat(open).isEqualTo(0) - socket.connection?.onOpen?.invoke() - assertThat(open).isEqualTo(0) + socket.connection?.onClose?.invoke(1000) + assertThat(close).isEqualTo(0) - socket.connection?.onClose?.invoke(1000) - assertThat(close).isEqualTo(0) + socket.connection?.onError?.invoke(Throwable(), null) + assertThat(lastError).isNull() + assertThat(lastResponse).isNull() - socket.connection?.onError?.invoke(Throwable(), null) - assertThat(lastError).isNull() - assertThat(lastResponse).isNull() + val data = mapOf( + "topic" to "topic", + "event" to "event", + "payload" to mapOf("go" to true), + "status" to "status" + ) - val data = mapOf( - "topic" to "topic", - "event" to "event", - "payload" to mapOf("go" to true), - "status" to "status" - ) + val json = Defaults.gson.toJson(data) + socket.connection?.onMessage?.invoke(json) + assertThat(lastMessage?.payload).isNull() + } - val json = Defaults.gson.toJson(data) - socket.connection?.onMessage?.invoke(json) - assertThat(lastMessage?.payload).isNull() - } + @Test + internal fun `does not connect if already connected`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + socket.connect() + socket.connect() - @Test - fun `connect() does not connect if already connected`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() - socket.connect() + verify(connection, times(1)).connect() + } - verify(connection, times(1)).connect() + /* End Connect */ } - /* disconnect */ - @Test - fun `disconnect() removes existing connection`() { - socket.connect() - socket.disconnect() + @Nested + @DisplayName("disconnect") + inner class Disconnect { + @Test + internal fun `removes existing connection`() { + socket.connect() + socket.disconnect() - assertThat(socket.connection).isNull() - verify(connection).disconnect(WS_CLOSE_NORMAL) - } + assertThat(socket.connection).isNull() + verify(connection).disconnect(WS_CLOSE_NORMAL) + } - @Test - fun `disconnect() calls callback`() { - val mockCallback = mock<() -> Unit> {} + @Test + internal fun `calls callback`() { + val mockCallback = mock<() -> Unit> {} - socket.disconnect(callback = mockCallback) - verify(mockCallback).invoke() - } + socket.disconnect(callback = mockCallback) + verify(mockCallback).invoke() + } - @Test - fun `disconnect() calls connection close callback`() { - socket.connect() - socket.disconnect(10, "reason") - verify(connection).disconnect(10, "reason") - } + @Test + internal fun `calls connection close callback`() { + socket.connect() + socket.disconnect(10, "reason") + verify(connection).disconnect(10, "reason") + } - @Test - fun `disconnect() resets reconnect timer`() { - val mockTimer = mock() - socket.reconnectTimer = mockTimer + @Test + internal fun `resets reconnect timer`() { + val mockTimer = mock() + socket.reconnectTimer = mockTimer - socket.disconnect() - verify(mockTimer).reset() - } + socket.disconnect() + verify(mockTimer).reset() + } - @Test - fun `disconnect() cancels and releases heartbeat timer`() { - val mockTask = mock() - socket.heartbeatTask = mockTask + @Test + internal fun `cancels and releases heartbeat timer`() { + val mockTask = mock() + socket.heartbeatTask = mockTask - socket.disconnect() - verify(mockTask).cancel() - assertThat(socket.heartbeatTask).isNull() - } + socket.disconnect() + verify(mockTask).cancel() + assertThat(socket.heartbeatTask).isNull() + } - @Test - fun `disconnect() does nothing if not connected`() { - socket.disconnect() - verifyZeroInteractions(connection) + @Test + internal fun `does nothing if not connected`() { + socket.disconnect() + verifyZeroInteractions(connection) + } + + /* End Disconnect */ } - /* channel */ - @Test - fun `channel() returns channel with given topic and params`() { - val channel = socket.channel("topic", mapOf("one" to "two")) + @Nested + @DisplayName("channel") + inner class NewChannel { + @Test + internal fun `returns channel with given topic and params`() { + val channel = socket.channel("topic", mapOf("one" to "two")) - assertThat(channel.socket).isEqualTo(socket) - assertThat(channel.topic).isEqualTo("topic") - assertThat(channel.params["one"]).isEqualTo("two") - } + assertThat(channel.socket).isEqualTo(socket) + assertThat(channel.topic).isEqualTo("topic") + assertThat(channel.params["one"]).isEqualTo("two") + } - @Test - fun `channel() adds channel to socket's channel list`() { - assertThat(socket.channels).isEmpty() + @Test + internal fun `adds channel to socket's channel list`() { + assertThat(socket.channels).isEmpty() - val channel = socket.channel("topic", mapOf("one" to "two")) + val channel = socket.channel("topic", mapOf("one" to "two")) - assertThat(socket.channels).hasSize(1) - assertThat(socket.channels.first()).isEqualTo(channel) + assertThat(socket.channels).hasSize(1) + assertThat(socket.channels.first()).isEqualTo(channel) + } + + /* End Channel */ } - @Test - fun `remove() removes given channel from channels`() { - val channel1 = socket.channel("topic-1") - val channel2 = socket.channel("topic-2") + @Nested + @DisplayName("remove") + inner class Remove { + @Test + internal fun `removes given channel from channels`() { + val channel1 = socket.channel("topic-1") + val channel2 = socket.channel("topic-2") - channel1.joinPush.ref = "1" - channel2.joinPush.ref = "2" + channel1.joinPush.ref = "1" + channel2.joinPush.ref = "2" + + socket.remove(channel1) + assertThat(socket.channels).doesNotContain(channel1) + assertThat(socket.channels).contains(channel2) + } - socket.remove(channel1) - assertThat(socket.channels).doesNotContain(channel1) - assertThat(socket.channels).contains(channel2) + /* End Remove */ } - /* push */ - @Test - fun `push() sends data to connection when connected`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + @Nested + @DisplayName("push") + inner class Push { + @Test + internal fun `sends data to connection when connected`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() - socket.push("topic", "event", mapOf("one" to "two"), "ref", "join-ref") + socket.connect() + socket.push("topic", "event", mapOf("one" to "two"), "ref", "join-ref") - val expect = - "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"ref\":\"ref\",\"join_ref\":\"join-ref\"}" - verify(connection).send(expect) - } + val expect = + "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"ref\":\"ref\",\"join_ref\":\"join-ref\"}" + verify(connection).send(expect) + } - @Test - fun `push() excludes ref information if not passed`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + @Test + internal fun `excludes ref information if not passed`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() - socket.push("topic", "event", mapOf("one" to "two")) + socket.connect() + socket.push("topic", "event", mapOf("one" to "two")) - val expect = "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"}}" - verify(connection).send(expect) - } + val expect = "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"}}" + verify(connection).send(expect) + } - @Test - fun `push() buffers data when not connected`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.CLOSED) - socket.connect() + @Test + internal fun `buffers data when not connected`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.CLOSED) + socket.connect() - socket.push("topic", "event1", mapOf("one" to "two")) - verify(connection, never()).send(any()) - assertThat(socket.sendBuffer).hasSize(1) + socket.push("topic", "event1", mapOf("one" to "two")) + verify(connection, never()).send(any()) + assertThat(socket.sendBuffer).hasSize(1) - socket.push("topic", "event2", mapOf("one" to "two")) - verify(connection, never()).send(any()) - assertThat(socket.sendBuffer).hasSize(2) + socket.push("topic", "event2", mapOf("one" to "two")) + verify(connection, never()).send(any()) + assertThat(socket.sendBuffer).hasSize(2) - socket.sendBuffer.forEach { it.invoke() } - verify(connection, times(2)).send(any()) - } + socket.sendBuffer.forEach { it.invoke() } + verify(connection, times(2)).send(any()) + } - /* makeRef */ - @Test - fun `makeRef() returns next message ref`() { - assertThat(socket.ref).isEqualTo(0) - assertThat(socket.makeRef()).isEqualTo("1") - assertThat(socket.ref).isEqualTo(1) - assertThat(socket.makeRef()).isEqualTo("2") - assertThat(socket.ref).isEqualTo(2) + /* End Push */ } - @Test - fun `makeRef() resets to 0 if it hits max int`() { - socket.ref = Int.MAX_VALUE + @Nested + @DisplayName("makeRef") + inner class MakeRef { + @Test + internal fun `returns next message ref`() { + assertThat(socket.ref).isEqualTo(0) + assertThat(socket.makeRef()).isEqualTo("1") + assertThat(socket.ref).isEqualTo(1) + assertThat(socket.makeRef()).isEqualTo("2") + assertThat(socket.ref).isEqualTo(2) + } + + @Test + internal fun `resets to 0 if it hits max int`() { + socket.ref = Int.MAX_VALUE - assertThat(socket.makeRef()).isEqualTo("0") - assertThat(socket.ref).isEqualTo(0) + assertThat(socket.makeRef()).isEqualTo("0") + assertThat(socket.ref).isEqualTo(0) + } + + /* End MakeRef */ } - /* sendHeartbeat */ - @Test - fun `sendHeartbeat() closes socket when heartbeat is not ack'd within heartbeat window`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() + @Nested + @DisplayName("sendHeartbeat") + inner class SendHeartbeat { + @Test + internal fun `closes socket when heartbeat is not ack'd within heartbeat window`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + socket.connect() - socket.sendHeartbeat() - verify(connection, never()).disconnect(any(), any()) - assertThat(socket.pendingHeartbeatRef).isNotNull() + socket.sendHeartbeat() + verify(connection, never()).disconnect(any(), any()) + assertThat(socket.pendingHeartbeatRef).isNotNull() - socket.sendHeartbeat() - verify(connection).disconnect(WS_CLOSE_NORMAL, "Heartbeat timed out") - assertThat(socket.pendingHeartbeatRef).isNull() - } + socket.sendHeartbeat() + verify(connection).disconnect(WS_CLOSE_NORMAL, "Heartbeat timed out") + assertThat(socket.pendingHeartbeatRef).isNull() + } - @Test - fun `sendHeartbeat() pushes heartbeat data when connected`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() + @Test + internal fun `pushes heartbeat data when connected`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + socket.connect() - socket.sendHeartbeat() + socket.sendHeartbeat() - val expected = "{\"topic\":\"phoenix\",\"event\":\"heartbeat\",\"payload\":{},\"ref\":\"1\"}" - assertThat(socket.pendingHeartbeatRef).isEqualTo(socket.ref.toString()) - verify(connection).send(expected) - } + val expected = "{\"topic\":\"phoenix\",\"event\":\"heartbeat\",\"payload\":{},\"ref\":\"1\"}" + assertThat(socket.pendingHeartbeatRef).isEqualTo(socket.ref.toString()) + verify(connection).send(expected) + } - @Test - fun `sendHeartbeat() does nothing when not connected`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.CLOSED) - socket.connect() - socket.sendHeartbeat() + @Test + internal fun `does nothing when not connected`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.CLOSED) + socket.connect() + socket.sendHeartbeat() - verify(connection, never()).disconnect(any(), any()) - verify(connection, never()).send(any()) - } + verify(connection, never()).disconnect(any(), any()) + verify(connection, never()).send(any()) + } - /* flushSendBuffer */ - @Test - fun `flushSendBuffer() invokes callbacks in buffer when connected`() { - var oneCalled = 0 - socket.sendBuffer.add { oneCalled += 1 } - var twoCalled = 0 - socket.sendBuffer.add { twoCalled += 1 } - val threeCalled = 0 - - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - - // does nothing if not connected - socket.flushSendBuffer() - assertThat(oneCalled).isEqualTo(0) - - // connect - socket.connect() - - // sends once connected - socket.flushSendBuffer() - assertThat(oneCalled).isEqualTo(1) - assertThat(twoCalled).isEqualTo(1) - assertThat(threeCalled).isEqualTo(0) + /* End SendHeartbeat */ } - @Test - fun `flushSendBuffer() empties send buffer`() { - socket.sendBuffer.add { } + @Nested + @DisplayName("flushSendBuffer") + inner class FlushSendBuffer { + @Test + internal fun `invokes callbacks in buffer when connected`() { + var oneCalled = 0 + socket.sendBuffer.add { oneCalled += 1 } + var twoCalled = 0 + socket.sendBuffer.add { twoCalled += 1 } + val threeCalled = 0 - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - assertThat(socket.sendBuffer).isNotEmpty() - socket.flushSendBuffer() + // does nothing if not connected + socket.flushSendBuffer() + assertThat(oneCalled).isEqualTo(0) - assertThat(socket.sendBuffer).isEmpty() - } + // connect + socket.connect() - /* onConnectionOpen */ - @Test - fun `onConnectionOpened() flushes the send buffer`() { - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() + // sends once connected + socket.flushSendBuffer() + assertThat(oneCalled).isEqualTo(1) + assertThat(twoCalled).isEqualTo(1) + assertThat(threeCalled).isEqualTo(0) + } - var oneCalled = 0 - socket.sendBuffer.add { oneCalled += 1 } + @Test + internal fun `empties send buffer`() { + socket.sendBuffer.add { } - socket.onConnectionOpened() - assertThat(oneCalled).isEqualTo(1) - assertThat(socket.sendBuffer).isEmpty() - } + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + socket.connect() + + assertThat(socket.sendBuffer).isNotEmpty() + socket.flushSendBuffer() - @Test - fun `onConnectionOpened() resets reconnect timer`() { - val mockTimer = mock() - socket.reconnectTimer = mockTimer + assertThat(socket.sendBuffer).isEmpty() + } - socket.onConnectionOpened() - verify(mockTimer).reset() + /* End FlushSendBuffer */ } - @Test - fun `onConnectionOpened() resets the heartbeat`() { - val mockTask = mock() - socket.heartbeatTask = mockTask + @Nested + @DisplayName("onConnectionOpened") + inner class OnConnectionOpened { + @Test + internal fun `flushes the send buffer`() { + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + socket.connect() - socket.onConnectionOpened() - verify(mockTask).cancel() - verify(mockDispatchQueue).queueAtFixedRate(any(), any(), any(), any()) - } + var oneCalled = 0 + socket.sendBuffer.add { oneCalled += 1 } - @Test - fun `onConnectionOpened() invokes all onOpen callbacks`() { - var oneCalled = 0 - socket.onOpen { oneCalled += 1 } - var twoCalled = 0 - socket.onOpen { twoCalled += 1 } - var threeCalled = 0 - socket.onClose { threeCalled += 1 } - - socket.onConnectionOpened() - assertThat(oneCalled).isEqualTo(1) - assertThat(twoCalled).isEqualTo(1) - assertThat(threeCalled).isEqualTo(0) - } + socket.onConnectionOpened() + assertThat(oneCalled).isEqualTo(1) + assertThat(socket.sendBuffer).isEmpty() + } - /* resetHeartbeat */ - @Test - fun `resetHeartbeat() clears any pending heartbeat`() { - socket.pendingHeartbeatRef = "1" - socket.resetHeartbeat() + @Test + internal fun `resets reconnect timer`() { + val mockTimer = mock() + socket.reconnectTimer = mockTimer - assertThat(socket.pendingHeartbeatRef).isNull() - } + socket.onConnectionOpened() + verify(mockTimer).reset() + } + + @Test + internal fun `resets the heartbeat`() { + val mockTask = mock() + socket.heartbeatTask = mockTask - @Test - fun `resetHeartbeat() does not schedule heartbeat if skipHeartbeat == true`() { - socket.skipHeartbeat = true - socket.resetHeartbeat() + socket.onConnectionOpened() + verify(mockTask).cancel() + verify(mockDispatchQueue).queueAtFixedRate(any(), any(), any(), any()) + } - verifyZeroInteractions(mockDispatchQueue) + @Test + internal fun `invokes all onOpen callbacks`() { + var oneCalled = 0 + socket.onOpen { oneCalled += 1 } + var twoCalled = 0 + socket.onOpen { twoCalled += 1 } + var threeCalled = 0 + socket.onClose { threeCalled += 1 } + + socket.onConnectionOpened() + assertThat(oneCalled).isEqualTo(1) + assertThat(twoCalled).isEqualTo(1) + assertThat(threeCalled).isEqualTo(0) + } + + /* End OnConnectionOpened */ } - @Test - fun `resetHeartbeat() creates a future heartbeat task`() { - val mockTask = mock() - whenever(mockDispatchQueue.queueAtFixedRate(any(), any(), any(), any())).thenReturn(mockTask) + @Nested + @DisplayName("resetHeartbeat") + inner class ResetHeartbeat { + @Test + internal fun `clears any pending heartbeat`() { + socket.pendingHeartbeatRef = "1" + socket.resetHeartbeat() - whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) - socket.connect() - socket.heartbeatInterval = 5_000 + assertThat(socket.pendingHeartbeatRef).isNull() + } - assertThat(socket.heartbeatTask).isNull() - socket.resetHeartbeat() + @Test + fun `does not schedule heartbeat if skipHeartbeat == true`() { + socket.skipHeartbeat = true + socket.resetHeartbeat() - assertThat(socket.heartbeatTask).isNotNull() - argumentCaptor<() -> Unit> { - verify(mockDispatchQueue).queueAtFixedRate(eq(5_000L), eq(5_000L), eq(TimeUnit.MILLISECONDS), capture()) + verifyZeroInteractions(mockDispatchQueue) + } - // fire the task - allValues.first().invoke() + @Test + internal fun `creates a future heartbeat task`() { + val mockTask = mock() + whenever(mockDispatchQueue.queueAtFixedRate(any(), any(), any(), any())).thenReturn(mockTask) - val expected = "{\"topic\":\"phoenix\",\"event\":\"heartbeat\",\"payload\":{},\"ref\":\"1\"}" - verify(connection).send(expected) + whenever(connection.readyState).thenReturn(Transport.ReadyState.OPEN) + socket.connect() + socket.heartbeatInterval = 5_000 + + assertThat(socket.heartbeatTask).isNull() + socket.resetHeartbeat() + + assertThat(socket.heartbeatTask).isNotNull() + argumentCaptor<() -> Unit> { + verify(mockDispatchQueue).queueAtFixedRate(eq(5_000L), eq(5_000L), + eq(TimeUnit.MILLISECONDS), capture()) + + // fire the task + allValues.first().invoke() + + val expected = + "{\"topic\":\"phoenix\",\"event\":\"heartbeat\",\"payload\":{},\"ref\":\"1\"}" + verify(connection).send(expected) + } } + + /* End ResetHeartbeat */ } - /* onConnectionClosed */ - @Test - fun `onConnectionClosed() it does not schedule reconnectTimer timeout if normal close`() { - val mockTimer = mock() - socket.reconnectTimer = mockTimer + @Nested + @DisplayName("onConnectionClosed") + inner class OnConnectionClosed { + @Test + internal fun `it does not schedule reconnectTimer timeout if normal close`() { + val mockTimer = mock() + socket.reconnectTimer = mockTimer - socket.onConnectionClosed(WS_CLOSE_NORMAL) - verify(mockTimer, never()).scheduleTimeout() - } + socket.onConnectionClosed(WS_CLOSE_NORMAL) + verify(mockTimer, never()).scheduleTimeout() + } - @Test - fun `onConnectionClosed schedules reconnectTimer if not normal close`() { - val mockTimer = mock() - socket.reconnectTimer = mockTimer + @Test + internal fun `schedules reconnectTimer if not normal close`() { + val mockTimer = mock() + socket.reconnectTimer = mockTimer - socket.onConnectionClosed(1001) - verify(mockTimer).scheduleTimeout() - } + socket.onConnectionClosed(1001) + verify(mockTimer).scheduleTimeout() + } - @Test - fun `onConnectionClosed() cancels heartbeat task`() { - val mockTask = mock() - socket.heartbeatTask = mockTask + @Test + internal fun `cancels heartbeat task`() { + val mockTask = mock() + socket.heartbeatTask = mockTask - socket.onConnectionClosed(1000) - verify(mockTask).cancel() - assertThat(socket.heartbeatTask).isNull() - } + socket.onConnectionClosed(1000) + verify(mockTask).cancel() + assertThat(socket.heartbeatTask).isNull() + } - @Test - fun `onConnectionClosed() triggers onClose callbacks`() { - var oneCalled = 0 - socket.onClose { oneCalled += 1 } - var twoCalled = 0 - socket.onClose { twoCalled += 1 } - var threeCalled = 0 - socket.onOpen { threeCalled += 1 } - - socket.onConnectionClosed(1000) - assertThat(oneCalled).isEqualTo(1) - assertThat(twoCalled).isEqualTo(1) - assertThat(threeCalled).isEqualTo(0) - } + @Test + internal fun `triggers onClose callbacks`() { + var oneCalled = 0 + socket.onClose { oneCalled += 1 } + var twoCalled = 0 + socket.onClose { twoCalled += 1 } + var threeCalled = 0 + socket.onOpen { threeCalled += 1 } + + socket.onConnectionClosed(1000) + assertThat(oneCalled).isEqualTo(1) + assertThat(twoCalled).isEqualTo(1) + assertThat(threeCalled).isEqualTo(0) + } - @Test - fun `onConnectionClosed() triggers channel error`() { - val channel = mock() - socket.channels.add(channel) + @Test + internal fun `triggers channel error`() { + val channel = mock() + socket.channels.add(channel) - socket.onConnectionClosed(1001) - verify(channel).trigger("phx_error") - } + socket.onConnectionClosed(1001) + verify(channel).trigger("phx_error") + } - /* onConnectionError */ - @Test - fun `onConnectionError() triggers onClose callbacks`() { - var oneCalled = 0 - socket.onError { _, _ -> oneCalled += 1 } - var twoCalled = 0 - socket.onError { _, _ -> twoCalled += 1 } - var threeCalled = 0 - socket.onOpen { threeCalled += 1 } - - socket.onConnectionError(Throwable(), null) - assertThat(oneCalled).isEqualTo(1) - assertThat(twoCalled).isEqualTo(1) - assertThat(threeCalled).isEqualTo(0) - } + /* End OnConnectionClosed */ + } + + @Nested + @DisplayName("onConnectionError") + inner class OnConnectionError { + @Test + internal fun `triggers onClose callbacks`() { + var oneCalled = 0 + socket.onError { _, _ -> oneCalled += 1 } + var twoCalled = 0 + socket.onError { _, _ -> twoCalled += 1 } + var threeCalled = 0 + socket.onOpen { threeCalled += 1 } + + socket.onConnectionError(Throwable(), null) + assertThat(oneCalled).isEqualTo(1) + assertThat(twoCalled).isEqualTo(1) + assertThat(threeCalled).isEqualTo(0) + } - @Test - fun `onConnectionError() triggers channel error`() { - val channel = mock() - socket.channels.add(channel) + @Test + internal fun `triggers channel error`() { + val channel = mock() + socket.channels.add(channel) - socket.onConnectionError(Throwable(), null) - verify(channel).trigger("phx_error") + socket.onConnectionError(Throwable(), null) + verify(channel).trigger("phx_error") + } + + /* End OnConnectionError */ } - @Test - fun `onConnectionMessage() parses raw messages and triggers channel event`() { - val targetChannel = mock() - whenever(targetChannel.isMember(any())).thenReturn(true) - val otherChannel = mock() - whenever(otherChannel.isMember(any())).thenReturn(false) + @Nested + @DisplayName("onConnectionMessage") + inner class OnConnectionMessage { + @Test + internal fun `parses raw messages and triggers channel event`() { + val targetChannel = mock() + whenever(targetChannel.isMember(any())).thenReturn(true) + val otherChannel = mock() + whenever(otherChannel.isMember(any())).thenReturn(false) - socket.channels.add(targetChannel) - socket.channels.add(otherChannel) + socket.channels.add(targetChannel) + socket.channels.add(otherChannel) - val rawMessage = - "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"status\":\"ok\"}" - socket.onConnectionMessage(rawMessage) + val rawMessage = + "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"status\":\"ok\"}" + socket.onConnectionMessage(rawMessage) - verify(targetChannel).trigger(message = any()) - verify(otherChannel, never()).trigger(message = any()) - } + verify(targetChannel).trigger(message = any()) + verify(otherChannel, never()).trigger(message = any()) + } - @Test - fun `onConnectionMessage() invokes onMessage callbacks`() { - var message: Message? = null - socket.onMessage { message = it } + @Test + internal fun `invokes onMessage callbacks`() { + var message: Message? = null + socket.onMessage { message = it } - val rawMessage = - "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"status\":\"ok\"}" - socket.onConnectionMessage(rawMessage) + val rawMessage = + "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"status\":\"ok\"}" + socket.onConnectionMessage(rawMessage) - assertThat(message?.topic).isEqualTo("topic") - assertThat(message?.event).isEqualTo("event") - } + assertThat(message?.topic).isEqualTo("topic") + assertThat(message?.event).isEqualTo("event") + } - @Test - fun `onConnectionMessage() clears pending heartbeat`() { - socket.pendingHeartbeatRef = "5" + @Test + internal fun `clears pending heartbeat`() { + socket.pendingHeartbeatRef = "5" - val rawMessage = - "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"ref\":\"5\"}" - socket.onConnectionMessage(rawMessage) - assertThat(socket.pendingHeartbeatRef).isNull() + val rawMessage = + "{\"topic\":\"topic\",\"event\":\"event\",\"payload\":{\"one\":\"two\"},\"ref\":\"5\"}" + socket.onConnectionMessage(rawMessage) + assertThat(socket.pendingHeartbeatRef).isNull() + } + + /* End OnConnectionMessage */ } + } \ No newline at end of file diff --git a/src/test/kotlin/org/phoenixframework/TimeoutTimerTest.kt b/src/test/kotlin/org/phoenixframework/TimeoutTimerTest.kt index 282e7e5..df6792b 100644 --- a/src/test/kotlin/org/phoenixframework/TimeoutTimerTest.kt +++ b/src/test/kotlin/org/phoenixframework/TimeoutTimerTest.kt @@ -1,16 +1,19 @@ package org.phoenixframework -import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test import org.mockito.Mock import org.mockito.MockitoAnnotations +import org.phoenixframework.DispatchQueue +import org.phoenixframework.DispatchWorkItem +import org.phoenixframework.TimeoutTimer import java.util.concurrent.TimeUnit class TimeoutTimerTest { @@ -21,8 +24,8 @@ class TimeoutTimerTest { private var callbackCallCount: Int = 0 private lateinit var timeoutTimer: TimeoutTimer - @Before - fun setUp() { + @BeforeEach + internal fun setUp() { MockitoAnnotations.initMocks(this) callbackCallCount = 0 @@ -37,28 +40,28 @@ class TimeoutTimerTest { } @Test - fun `scheduleTimeout executes with backoff`() { + internal fun `scheduleTimeout executes with backoff`() { argumentCaptor<() -> Unit> { timeoutTimer.scheduleTimeout() verify(mockDispatchQueue).queue(eq(1000L), eq(TimeUnit.MILLISECONDS), capture()) lastValue.invoke() - assertThat(callbackCallCount).isEqualTo(1) + Truth.assertThat(callbackCallCount).isEqualTo(1) timeoutTimer.scheduleTimeout() verify(mockDispatchQueue).queue(eq(2000L), eq(TimeUnit.MILLISECONDS), capture()) lastValue.invoke() - assertThat(callbackCallCount).isEqualTo(2) + Truth.assertThat(callbackCallCount).isEqualTo(2) timeoutTimer.scheduleTimeout() verify(mockDispatchQueue).queue(eq(5000L), eq(TimeUnit.MILLISECONDS), capture()) lastValue.invoke() - assertThat(callbackCallCount).isEqualTo(3) + Truth.assertThat(callbackCallCount).isEqualTo(3) timeoutTimer.reset() timeoutTimer.scheduleTimeout() verify(mockDispatchQueue, times(2)).queue(eq(1000L), eq(TimeUnit.MILLISECONDS), capture()) lastValue.invoke() - assertThat(callbackCallCount).isEqualTo(4) + Truth.assertThat(callbackCallCount).isEqualTo(4) } } } \ No newline at end of file diff --git a/src/test/kotlin/org/phoenixframework/WebSocketTransportTest.kt b/src/test/kotlin/org/phoenixframework/WebSocketTransportTest.kt index cc2353b..ebcd9a3 100644 --- a/src/test/kotlin/org/phoenixframework/WebSocketTransportTest.kt +++ b/src/test/kotlin/org/phoenixframework/WebSocketTransportTest.kt @@ -8,14 +8,16 @@ import com.nhaarman.mockitokotlin2.whenever import okhttp3.OkHttpClient import okhttp3.Response import okhttp3.WebSocket -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.mockito.Mock import org.mockito.MockitoAnnotations -import java.io.EOFException +import org.phoenixframework.Transport +import org.phoenixframework.WebSocketTransport import java.net.SocketException import java.net.URL -import javax.net.ssl.SSLException class WebSocketTransportTest { @@ -23,132 +25,156 @@ class WebSocketTransportTest { @Mock lateinit var mockWebSocket: WebSocket @Mock lateinit var mockResponse: Response - lateinit var transport: WebSocketTransport + private lateinit var transport: WebSocketTransport - @Before - fun setUp() { + @BeforeEach + internal fun setUp() { MockitoAnnotations.initMocks(this) val url = URL("http://localhost:400/socket/websocket") transport = WebSocketTransport(url, mockClient) } - @Test - fun `connect sets ready state to CONNECTING and creates connection`() { - whenever(mockClient.newWebSocket(any(), any())).thenReturn(mockWebSocket) + @Nested + @DisplayName("connect") + inner class Connect { + @Test + internal fun `sets ready state to CONNECTING and creates connection`() { + whenever(mockClient.newWebSocket(any(), any())).thenReturn(mockWebSocket) - transport.connect() - assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CONNECTING) - assertThat(transport.connection).isNotNull() - } - - @Test - fun `disconnect closes and releases the connection`() { - transport.connection = mockWebSocket + transport.connect() + assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CONNECTING) + assertThat(transport.connection).isNotNull() + } - transport.disconnect(10, "Test reason") - verify(mockWebSocket).close(10, "Test reason") - assertThat(transport.connection).isNull() + /* End Connect */ } - @Test - fun `send sends text through the connection`() { - transport.connection = mockWebSocket + @Nested + @DisplayName("disconnect") + inner class Disconnect { + @Test + internal fun `closes and releases connection`() { + transport.connection = mockWebSocket + + transport.disconnect(10, "Test reason") + verify(mockWebSocket).close(10, "Test reason") + assertThat(transport.connection).isNull() + } - transport.send("some data") - verify(mockWebSocket).send("some data") + /* End Disconnect */ } - @Test - fun `onOpen sets ready state to OPEN and invokes the onOpen callback`() { - val mockClosure = mock<() -> Unit>() - transport.onOpen = mockClosure + @Nested + @DisplayName("send") + inner class Send { + @Test + internal fun `sends text through the connection`() { + transport.connection = mockWebSocket - assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED) + transport.send("some data") + verify(mockWebSocket).send("some data") + } - transport.onOpen(mockWebSocket, mockResponse) - assertThat(transport.readyState).isEqualTo(Transport.ReadyState.OPEN) - verify(mockClosure).invoke() + /* End Send */ } - @Test - fun `onFailure sets ready state to CLOSED and invokes onError callback`() { - val mockClosure = mock<(Throwable, Response?) -> Unit>() - transport.onError = mockClosure + @Nested + @DisplayName("onOpen") + inner class OnOpen { + @Test + internal fun `sets ready state to OPEN and invokes the onOpen callback`() { + val mockClosure = mock<() -> Unit>() + transport.onOpen = mockClosure - transport.readyState = Transport.ReadyState.CONNECTING + assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED) - val throwable = Throwable() - transport.onFailure(mockWebSocket, throwable, mockResponse) - assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED) - verify(mockClosure).invoke(throwable, mockResponse) - } + transport.onOpen(mockWebSocket, mockResponse) + assertThat(transport.readyState).isEqualTo(Transport.ReadyState.OPEN) + verify(mockClosure).invoke() + } - @Test - fun `onFailure also triggers onClose for SocketException`() { - val mockOnError = mock<(Throwable, Response?) -> Unit>() - val mockOnClose = mock<(Int) -> Unit>() - transport.onClose = mockOnClose - transport.onError = mockOnError - - val throwable = SocketException() - transport.onFailure(mockWebSocket, throwable, mockResponse) - verify(mockOnError).invoke(throwable, mockResponse) - verify(mockOnClose).invoke(4000) + /* End OnOpen */ } - @Test - fun `onFailure also triggers onClose for SSLException`() { - val mockOnError = mock<(Throwable, Response?) -> Unit>() - val mockOnClose = mock<(Int) -> Unit>() - transport.onClose = mockOnClose - transport.onError = mockOnError - - val throwable = SSLException("t") - transport.onFailure(mockWebSocket, throwable, mockResponse) - verify(mockOnError).invoke(throwable, mockResponse) - verify(mockOnClose).invoke(4000) - } - @Test - fun `onFailure also triggers onClose for EOFException`() { - val mockOnError = mock<(Throwable, Response?) -> Unit>() - val mockOnClose = mock<(Int) -> Unit>() - transport.onClose = mockOnClose - transport.onError = mockOnError - - val throwable = EOFException() - transport.onFailure(mockWebSocket, throwable, mockResponse) - verify(mockOnError).invoke(throwable, mockResponse) - verify(mockOnClose).invoke(4000) + @Nested + @DisplayName("onFailure") + inner class OnFailure { + @Test + internal fun `sets ready state to CLOSED and invokes onError callback`() { + val mockClosure = mock<(Throwable, Response?) -> Unit>() + transport.onError = mockClosure + + transport.readyState = Transport.ReadyState.CONNECTING + + val throwable = Throwable() + transport.onFailure(mockWebSocket, throwable, mockResponse) + assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED) + verify(mockClosure).invoke(throwable, mockResponse) + } + + @Test + internal fun `also triggers onClose`() { + val mockOnError = mock<(Throwable, Response?) -> Unit>() + val mockOnClose = mock<(Int) -> Unit>() + transport.onClose = mockOnClose + transport.onError = mockOnError + + val throwable = SocketException() + transport.onFailure(mockWebSocket, throwable, mockResponse) + verify(mockOnError).invoke(throwable, mockResponse) + verify(mockOnClose).invoke(4000) + } + + /* End OnFailure */ } - @Test - fun `onClosing sets ready state to CLOSING`() { - transport.readyState = Transport.ReadyState.OPEN + @Nested + @DisplayName("onClosing") + inner class OnClosing { + @Test + internal fun `sets ready state to CLOSING`() { + transport.readyState = Transport.ReadyState.OPEN - transport.onClosing(mockWebSocket, 10, "reason") - assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSING) + transport.onClosing(mockWebSocket, 10, "reason") + assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSING) + } + + /* End OnClosing */ } - @Test - fun `onMessage invokes onMessage closure`() { - val mockClosure = mock<(String) -> Unit>() - transport.onMessage = mockClosure + @Nested + @DisplayName("onMessage") + inner class OnMessage { + @Test + internal fun `invokes onMessage closure`() { + val mockClosure = mock<(String) -> Unit>() + transport.onMessage = mockClosure + + transport.onMessage(mockWebSocket, "text") + verify(mockClosure).invoke("text") + } - transport.onMessage(mockWebSocket, "text") - verify(mockClosure).invoke("text") + /* End OnMessage*/ } - @Test - fun `onClosed sets readyState to CLOSED and invokes closure`() { - val mockClosure = mock<(Int) -> Unit>() - transport.onClose = mockClosure - transport.readyState = Transport.ReadyState.CONNECTING + @Nested + @DisplayName("onClosed") + inner class OnClosed { + @Test + internal fun `sets readyState to CLOSED and invokes closure`() { + val mockClosure = mock<(Int) -> Unit>() + transport.onClose = mockClosure + + transport.readyState = Transport.ReadyState.CONNECTING + + transport.onClosed(mockWebSocket, 10, "reason") + assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED) + verify(mockClosure).invoke(10) + } - transport.onClosed(mockWebSocket, 10, "reason") - assertThat(transport.readyState).isEqualTo(Transport.ReadyState.CLOSED) - verify(mockClosure).invoke(10) + /* End OnClosed */ } } \ No newline at end of file diff --git a/src/test/kotlin/org/phoenixframework/TestUtilities.kt b/src/test/kotlin/org/phoenixframework/utilities/TestUtilities.kt similarity index 91% rename from src/test/kotlin/org/phoenixframework/TestUtilities.kt rename to src/test/kotlin/org/phoenixframework/utilities/TestUtilities.kt index 3b4d193..c68cb96 100644 --- a/src/test/kotlin/org/phoenixframework/TestUtilities.kt +++ b/src/test/kotlin/org/phoenixframework/utilities/TestUtilities.kt @@ -1,5 +1,9 @@ -package org.phoenixframework +package org.phoenixframework.utilities +import org.phoenixframework.Binding +import org.phoenixframework.Channel +import org.phoenixframework.DispatchQueue +import org.phoenixframework.DispatchWorkItem import java.util.concurrent.TimeUnit //------------------------------------------------------------------------------ @@ -58,7 +62,8 @@ class ManualDispatchQueue : DispatchQueue { val periodInMs = tickTimeUnit.convert(period, unit) val deadline = tickTime + delayInMs - val workItem = ManualDispatchWorkItem(runnable, deadline, periodInMs) + val workItem = + ManualDispatchWorkItem(runnable, deadline, periodInMs) workItems.add(workItem) return workItem From 9f4ccabc28d40fb22e6657b893a0c049443ac307 Mon Sep 17 00:00:00 2001 From: Daniel Rees Date: Wed, 19 Jun 2019 13:41:31 -0400 Subject: [PATCH 2/2] Adding Jacoco test coverage --- .travis.yml | 2 ++ README.md | 3 ++- build.gradle | 17 +++++++++++++++++ script/deploy.sh | 2 +- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 22da5ee..89f0cc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: java +after_success: + - bash <(curl -s https://codecov.io/bash) jdk: - oraclejdk8 sudo: false diff --git a/README.md b/README.md index 26e4e91..df40c00 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # JavaPhoenixClient -[ ![Download](https://api.bintray.com/packages/drees/java-phoenix-client/JavaPhoenixClient/images/download.svg) ](https://bintray.com/drees/java-phoenix-client/JavaPhoenixClient/_latestVersion) +[![Download](https://api.bintray.com/packages/drees/java-phoenix-client/JavaPhoenixClient/images/download.svg) ](https://bintray.com/drees/java-phoenix-client/JavaPhoenixClient/_latestVersion) [![Build Status](https://travis-ci.com/dsrees/JavaPhoenixClient.svg?branch=master)](https://travis-ci.com/dsrees/JavaPhoenixClient) +[![codecov](https://codecov.io/gh/dsrees/JavaPhoenixClient/branch/master/graph/badge.svg)](https://codecov.io/gh/dsrees/JavaPhoenixClient) JavaPhoenixClient is a Kotlin implementation of the [phoenix.js](https://hexdocs.pm/phoenix/js/) client used to manage Phoenix channels. diff --git a/build.gradle b/build.gradle index 30d99c5..d11e95e 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,15 @@ repositories { } test { + // JUnit 5 Support useJUnitPlatform() + + // This allows us see tests execution progress in the output on the CI. + testLogging { + events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' + exceptionFormat 'full' + } + } dependencies { @@ -34,6 +42,15 @@ dependencies { testCompile group: 'com.nhaarman.mockitokotlin2', name: 'mockito-kotlin', version: '2.1.0' } +jacocoTestReport { + reports { + xml.enabled true + html.enabled false + } +} + + + compileKotlin { kotlinOptions.jvmTarget = "1.8" } diff --git a/script/deploy.sh b/script/deploy.sh index 7acc031..8b5988e 100755 --- a/script/deploy.sh +++ b/script/deploy.sh @@ -5,5 +5,5 @@ if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then ./gradlew bintrayUpload --stacktrace else echo -e '#### Build for Test => Branch ['$TRAVIS_BRANCH'] Pull Request ['$TRAVIS_PULL_REQUEST'] ####' - ./gradlew build + ./gradlew clean build jacocoTestReport fi \ No newline at end of file