1package com.peachpasswords.sync23import kotlinx.coroutines.runBlocking4import org.junit.Assert.assertArrayEquals5import org.junit.Assert.assertEquals6import org.junit.Assert.assertTrue7import org.junit.Test8910111213141516class RelayHubTest {1718 private val keyA = "aa".repeat(32)19 private val keyB = "bb".repeat(32)2021 private class Peer {22 val sent = mutableListOf<ByteArray>()23 val messages: List<RelayMessage?> get() = sent.map { decodeRelayMessage(it) }24 val sink: suspend (ByteArray) -> Unit = { sent.add(it) }25 }2627 private fun hub(enabled: Boolean = true) = RelayHub(object : RelayPolicy {28 override fun isRelayEnabled() = enabled29 })3031 @Test fun `relaying turned off is reported as disabled, not unavailable`() = runBlocking {32 val hub = hub(enabled = false)33 val a = Peer()34 val b = Peer()35 val linkA = hub.register(keyA, a.sink)36 hub.register(keyB, b.sink)3738 hub.onRequest(linkA, keyB)3940 assertEquals(listOf(RelayMessage.Disabled), a.messages)41 assertTrue("the target must not be told about a refused request", b.sent.isEmpty())42 }4344 @Test fun `an absent target is unavailable`() = runBlocking {45 val hub = hub()46 val a = Peer()47 val linkA = hub.register(keyA, a.sink)4849 hub.onRequest(linkA, keyB)5051 assertEquals(listOf(RelayMessage.Unavailable), a.messages)52 }5354 @Test fun `a target busy with its own sync is unavailable`() = runBlocking {55 val hub = hub()56 val a = Peer()57 val b = Peer()58 val linkA = hub.register(keyA, a.sink)59 val linkB = hub.register(keyB, b.sink)60 hub.beginSync(linkB)6162 hub.onRequest(linkA, keyB)63 assertEquals(listOf(RelayMessage.Unavailable), a.messages)6465 66 hub.endSync(linkB)67 hub.onRequest(linkA, keyB)68 assertEquals(RelayMessage.Accept, a.messages.last())69 }7071 @Test fun `a channel opens with the target told first`() = runBlocking {72 val hub = hub()73 val a = Peer()74 val b = Peer()75 val linkA = hub.register(keyA, a.sink)76 hub.register(keyB, b.sink)7778 hub.onRequest(linkA, keyB)7980 assertEquals(listOf(RelayMessage.Open(keyA)), b.messages)81 assertEquals(listOf(RelayMessage.Accept), a.messages)82 }8384 @Test fun `frames cross verbatim in both directions`() = runBlocking {85 val hub = hub()86 val a = Peer()87 val b = Peer()88 val linkA = hub.register(keyA, a.sink)89 val linkB = hub.register(keyB, b.sink)90 hub.onRequest(linkA, keyB)9192 93 94 val opaque = "{\"type\":\"RELAY_FRAME\",\"payload\":\"ZmFrZQ==\"}".toByteArray()95 assertTrue(hub.forward(linkA, opaque))96 assertArrayEqualsLast(opaque, b.sent)9798 val back = "{\"type\":\"RELAY_FRAME\",\"payload\":\"b3RoZXI=\"}".toByteArray()99 assertTrue(hub.forward(linkB, back))100 assertArrayEqualsLast(back, a.sent)101 }102103 @Test fun `forwarding without a channel fails rather than guessing a destination`() = runBlocking {104 val hub = hub()105 val a = Peer()106 hub.register(keyB, Peer().sink)107 val linkA = hub.register(keyA, a.sink)108109 assertEquals(false, hub.forward(linkA, "x".toByteArray()))110 }111112 @Test fun `a dropped connection closes the other end of the channel`() = runBlocking {113 val hub = hub()114 val a = Peer()115 val b = Peer()116 val linkA = hub.register(keyA, a.sink)117 val linkB = hub.register(keyB, b.sink)118 hub.onRequest(linkA, keyB)119120 hub.unregister(linkA)121122 assertEquals(RelayMessage.Close, b.messages.last())123 124 val c = Peer()125 val linkC = hub.register("cc".repeat(32), c.sink)126 hub.onRequest(linkC, keyB)127 assertEquals(RelayMessage.Accept, c.messages.last())128 }129130 @Test fun `a link cannot relay to itself`() = runBlocking {131 val hub = hub()132 val a = Peer()133 val linkA = hub.register(keyA, a.sink)134135 hub.onRequest(linkA, keyA)136137 assertEquals(listOf(RelayMessage.Unavailable), a.messages)138 }139140 @Test fun `two laptop pairs can be relayed at once`() = runBlocking {141 val hub = hub()142 val peers = List(4) { Peer() }143 val keys = List(4) { "0$it".repeat(32) }144 val links = keys.mapIndexed { i, k -> hub.register(k, peers[i].sink) }145146 hub.onRequest(links[0], keys[1])147 hub.onRequest(links[2], keys[3])148149 assertEquals(RelayMessage.Accept, peers[0].messages.last())150 assertEquals(RelayMessage.Accept, peers[2].messages.last())151 assertTrue(hub.forward(links[0], "one".toByteArray()))152 assertTrue(hub.forward(links[2], "two".toByteArray()))153 assertArrayEqualsLast("one".toByteArray(), peers[1].sent)154 assertArrayEqualsLast("two".toByteArray(), peers[3].sent)155 }156157 private fun assertArrayEqualsLast(expected: ByteArray, sent: List<ByteArray>) {158 val last = sent.lastOrNull()159 assertTrue("nothing was forwarded", last != null)160 assertArrayEquals(expected, last)161 }162}