package com.peachpasswords.sync import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Test /** * The phone is the only side that generates pairing codes and the browser is * the only side that reads them, so a drift here is invisible until a real * pairing fails with nothing to look at. The golden vector below was produced * by running the browser's own `shared/pairing-code.ts` — not derived from this * implementation — so it fails if either side moves. */ class PairingCodeTest { // encodePairingCode("192.168.7.115", 49142, deadbeef01234567) in peach-browser. private val vectorCode = "R2M0-EWXZ-YVFA-VFQF-04HM-ASR" private val vectorToken = byteArrayOf( 0xde.toByte(), 0xad.toByte(), 0xbe.toByte(), 0xef.toByte(), 0x01, 0x23, 0x45, 0x67, ) @Test fun `encodes the browser's golden vector`() { assertEquals(vectorCode, PairingCode.encode("192.168.7.115", 49142, vectorToken)) } @Test fun `parses the browser's golden vector`() { val parsed = PairingCode.parse(vectorCode)!! assertEquals("192.168.7.115", parsed.ip) assertEquals(49142, parsed.port) assertEquals("deadbeef01234567", parsed.tokenHex) } @Test fun `round-trips a freshly generated token`() { val token = PairingCode.newToken() val parsed = PairingCode.parse(PairingCode.encode("10.0.0.7", 8765, token))!! assertEquals("10.0.0.7", parsed.ip) assertEquals(8765, parsed.port) assertEquals(token.joinToString("") { "%02x".format(it) }, parsed.tokenHex) } @Test fun `folds the ambiguous glyphs a user might read off a screen`() { // I/L -> 1 and O -> 0, so the doc's own example resolves rather than // failing on characters Crockford's alphabet excludes. val typed = PairingCode.parse("r2mO-ewxz-yvfa-vfqf-O4hm-asr") assertEquals(PairingCode.parse(vectorCode), typed) } @Test fun `rejects input that is not a pairing code`() { assertNull(PairingCode.parse("")) assertNull(PairingCode.parse("418207")) // the 6-digit SAS assertNull(PairingCode.parse("not-a-code!!")) // U is not in the alphabet } @Test(expected = IllegalArgumentException::class) fun `refuses to encode a zero port`() { PairingCode.encode("10.0.0.1", 0, PairingCode.newToken()) } @Test(expected = IllegalArgumentException::class) fun `refuses a token below the browser's four-byte floor`() { PairingCode.encode("10.0.0.1", 8765, byteArrayOf(1, 2, 3)) } }