RelayGateTest.kt

3.5 KB · raw

1package com.peachpasswords.sync23import org.junit.Assert.assertEquals4import org.junit.Assert.assertFalse5import org.junit.Assert.assertNull6import org.junit.Assert.assertTrue7import org.junit.Test89/**10 * The fail-closed relay gate, mirroring the browser's `sync-settings.test.ts`.11 * Everything here is the pure decision; the store around it is only12 * SharedPreferences and a Context this suite has no reason to stand up.13 */14class RelayGateTest {15    private val selfHosted = "wss://relay.example.com"1617    @Test18    fun `public dials peach's relay`() {19        assertEquals(20            RelayDecision.Dial(Rendezvous.DEFAULT_RELAY_URL),21            relayInForce(SyncMode.PUBLIC, Rendezvous.DEFAULT_RELAY_URL),22        )23    }2425    @Test26    fun `private with an address dials it`() {27        assertEquals(28            RelayDecision.Dial(selfHosted),29            relayInForce(SyncMode.PRIVATE, selfHosted),30        )31    }3233    @Test34    fun `private with no address refuses instead of falling back to public`() {35        assertTrue(isPrivateRelayUnconfigured(SyncMode.PRIVATE, Rendezvous.DEFAULT_RELAY_URL))36        assertEquals(37            RelayDecision.Refuse(SyncOutcomeReason.PRIVATE_RELAY_UNSET),38            relayInForce(SyncMode.PRIVATE, Rendezvous.DEFAULT_RELAY_URL),39        )40    }4142    @Test43    fun `off refuses with sync_disabled`() {44        assertEquals(45            RelayDecision.Refuse(SyncOutcomeReason.SYNC_DISABLED),46            relayInForce(SyncMode.OFF, selfHosted),47        )48        assertEquals("sync_disabled", SyncOutcomeReason.SYNC_DISABLED.wire())49        assertEquals("private_relay_unset", SyncOutcomeReason.PRIVATE_RELAY_UNSET.wire())50    }5152    @Test53    fun `saving peach's own url lands on public, not a broken private`() {54        assertEquals(SyncMode.PUBLIC, modeForRelayUrl(Rendezvous.DEFAULT_RELAY_URL))55        assertEquals(SyncMode.PRIVATE, modeForRelayUrl(selfHosted))56        // Adoption hands the source's relay through this same path, so a57        // receiver that joined over the public relay must not be relabelled58        // private and stranded in the unconfigured state.59        assertFalse(60            isPrivateRelayUnconfigured(61                modeForRelayUrl(Rendezvous.DEFAULT_RELAY_URL),62                Rendezvous.DEFAULT_RELAY_URL,63            ),64        )65    }6667    /**68     * The one guarantee behind mesh propagation: [RelayAdoptionHost] and the69     * reconnect loop can only obtain a URL out of [RelayDecision.Dial], so an70     * unconfigured private relay leaves nothing to put in an invitation and71     * nothing to hand a peer.72     */73    @Test74    fun `no relay is available to push to peers while private is unconfigured`() {75        val decision = relayInForce(SyncMode.PRIVATE, Rendezvous.DEFAULT_RELAY_URL)76        assertNull((decision as? RelayDecision.Dial)?.relayUrl)77        assertNull((relayInForce(SyncMode.OFF, selfHosted) as? RelayDecision.Dial)?.relayUrl)78    }7980    @Test81    fun `a stored mode the app no longer understands is not a crash`() {82        assertNull(SyncMode.parse("lan_only"))83        assertNull(SyncMode.parse(null))84        assertEquals(SyncMode.PRIVATE, SyncMode.parse("private"))85    }8687    @Test88    fun `the private-relay refusal is a red light with the browser's words`() {89        val summary = SyncOutcomeSummary(reason = SyncOutcomeReason.PRIVATE_RELAY_UNSET)90        assertTrue(SyncOutcomeReason.PRIVATE_RELAY_UNSET.isFailure)91        assertEquals(SyncIndicator.ERROR, syncIndicator(summary, pairedCount = 1))92        assertEquals("Private relay chosen but no address set", describeSyncOutcome(summary))93    }94}