import Testing @testable import Rosetta /// Verifies PacketPushNotification wire format matches Server/Android: /// writeInt16(packetId=0x10) /// writeString(notificationToken) /// writeInt8(action) /// writeInt8(tokenType) /// writeString(deviceId) struct PushNotificationPacketTests { // MARK: - Enum Value Parity @Test("PushNotificationAction.subscribe == 0 (server: SUBSCRIBE)") func subscribeActionValue() { #expect(PushNotificationAction.subscribe.rawValue == 0) } @Test("PushNotificationAction.unsubscribe == 1 (server: UNSUBSCRIBE)") func unsubscribeActionValue() { #expect(PushNotificationAction.unsubscribe.rawValue == 1) } @Test("PushTokenType.fcm == 0") func fcmTokenTypeValue() { #expect(PushTokenType.fcm.rawValue == 0) } @Test("PushTokenType.voipApns == 1") func voipTokenTypeValue() { #expect(PushTokenType.voipApns.rawValue == 1) } // MARK: - Round Trip (encode → decode) @Test("Subscribe round-trip preserves all fields") func subscribeRoundTrip() throws { var packet = PacketPushNotification() packet.notificationsToken = "test-fcm-token-abc123" packet.action = .subscribe packet.tokenType = .fcm packet.deviceId = "ios-device-1" let decoded = try decodePushNotification(packet) #expect(decoded.notificationsToken == "test-fcm-token-abc123") #expect(decoded.action == .subscribe) #expect(decoded.tokenType == .fcm) #expect(decoded.deviceId == "ios-device-1") } @Test("Unsubscribe round-trip preserves all fields") func unsubscribeRoundTrip() throws { var packet = PacketPushNotification() packet.notificationsToken = "test-token-deadbeef" packet.action = .unsubscribe packet.tokenType = .voipApns packet.deviceId = "ios-device-2" let decoded = try decodePushNotification(packet) #expect(decoded.notificationsToken == "test-token-deadbeef") #expect(decoded.action == .unsubscribe) #expect(decoded.tokenType == .voipApns) #expect(decoded.deviceId == "ios-device-2") } @Test("Empty token round-trip") func emptyFieldsRoundTrip() throws { var packet = PacketPushNotification() packet.notificationsToken = "" packet.action = .subscribe packet.tokenType = .fcm packet.deviceId = "" let decoded = try decodePushNotification(packet) #expect(decoded.notificationsToken == "") #expect(decoded.tokenType == .fcm) #expect(decoded.deviceId == "") } // MARK: - Wire Format Byte Verification @Test("Packet ID is 0x10 in encoded data") func packetIdInEncodedData() { #expect(PacketPushNotification.packetId == 0x10) let packet = PacketPushNotification() let data = PacketRegistry.encode(packet) // First 2 bytes = packetId in big-endian: 0x00 0x10 #expect(data.count >= 2) #expect(data[0] == 0x00) #expect(data[1] == 0x10) } @Test("Wire format field order matches server: token → action → tokenType → deviceId") func wireFormatFieldOrder() throws { // Use known short values so we can verify byte positions. var packet = PacketPushNotification() packet.notificationsToken = "T" // 1 UTF-16 code unit packet.action = .subscribe // 0 packet.tokenType = .fcm // 0 packet.deviceId = "D" // 1 UTF-16 code unit let data = PacketRegistry.encode(packet) // Expected layout: // [0-1] packetId = 0x0010 (2 bytes) // [2-5] string length = 1 (UInt32 big-endian) for "T" // [6-7] 'T' = 0x0054 (UInt16 big-endian) // [8] action = 0 (subscribe) // [9] tokenType = 0 (fcm) // [10-13] string length = 1 for "D" // [14-15] 'D' = 0x0044 #expect(data.count == 16) // packetId #expect(data[0] == 0x00) #expect(data[1] == 0x10) // token string length = 1 #expect(data[2] == 0x00) #expect(data[3] == 0x00) #expect(data[4] == 0x00) #expect(data[5] == 0x01) // 'T' in UTF-16 BE #expect(data[6] == 0x00) #expect(data[7] == 0x54) // action = 0 (subscribe) #expect(data[8] == 0x00) // tokenType = 0 (fcm) #expect(data[9] == 0x00) // deviceId string length = 1 #expect(data[10] == 0x00) #expect(data[11] == 0x00) #expect(data[12] == 0x00) #expect(data[13] == 0x01) // 'D' in UTF-16 BE #expect(data[14] == 0x00) #expect(data[15] == 0x44) } // MARK: - Helper private func decodePushNotification( _ packet: PacketPushNotification ) throws -> PacketPushNotification { let encoded = PacketRegistry.encode(packet) guard let decoded = PacketRegistry.decode(from: encoded), let decodedPacket = decoded.packet as? PacketPushNotification else { throw NSError( domain: "PushNotificationPacketTests", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decode PacketPushNotification"] ) } #expect(decoded.packetId == 0x10) return decodedPacket } }