1package com.peachpasswords.sync23import org.junit.Assert.assertEquals4import org.junit.Assert.assertNull5import org.junit.Assert.assertTrue6import org.junit.Assert.fail7import org.junit.Test8910111213141516private const val MAX_DEVICE_NAME_FOR_TEST = 641718class PairIdentityFramesTest {19 private val base = LocalDeviceIdentity(20 deviceId = "client-1",21 deviceName = "Phone",22 deviceType = DeviceType.ANDROID,23 longTermPublicKeyHex = "cd".repeat(32),24 signingPublicKeyHex = "ef".repeat(32),25 relayUrl = "wss://relay.peachpasswords.com",26 )2728 @Test29 fun `round-trips a device name containing every JSON escape character`() {30 val tricky = "a\"b\\c\nd\te\rf"31 val wire = encodePairIdentityFrame(base.copy(deviceName = tricky))32 val parsed = parsePairIdentityFrame(wire)33 assertEquals(tricky, parsed.deviceName)34 }3536 @Test37 fun `decoder unescapes a literal backslash-quote in the name`() {38 39 40 val raw = """{"type":"PAIR_IDENTITY","device_id":"d","device_name":"a\"b","device_type":"android","long_term_public_key_hex":"${"cd".repeat(32)}","signing_public_key_hex":"${"ef".repeat(32)}","relay_url":"wss://r.example.com"}"""41 val parsed = parsePairIdentityFrame(raw.toByteArray())42 assertEquals("a\"b", parsed.deviceName)43 }4445 @Test46 fun `decoder rejects a non-hex long term public key`() {47 val raw = """{"type":"PAIR_IDENTITY","device_id":"d","device_name":"n","device_type":"android","long_term_public_key_hex":"${"zz".repeat(32)}","signing_public_key_hex":"${"ef".repeat(32)}","relay_url":"wss://r.example.com"}"""48 try {49 parsePairIdentityFrame(raw.toByteArray())50 fail("expected hex rejection")51 } catch (e: IllegalArgumentException) {52 assertTrue(e.message!!.contains("hex"))53 }54 }5556 @Test57 fun `signing key is null when omitted from the wire`() {58 val omitted = """{"type":"PAIR_IDENTITY","device_id":"d","device_name":"n","device_type":"android","long_term_public_key_hex":"${"cd".repeat(32)}","relay_url":"wss://r.example.com"}"""59 val parsed = parsePairIdentityFrame(omitted.toByteArray())60 assertNull(parsed.signingPublicKeyHex)61 }6263 @Test64 fun `wire format preserves the type discriminator and snake_case keys`() {65 val wire = String(encodePairIdentityFrame(base), Charsets.UTF_8)66 assertTrue("frame must include type discriminator",67 wire.contains("\"type\":\"PAIR_IDENTITY\""))68 assertTrue("frame must use snake_case keys", wire.contains("\"device_id\""))69 assertTrue("frame must use snake_case keys", wire.contains("\"long_term_public_key_hex\""))70 }71}