166 lines
5.3 KiB
Swift
166 lines
5.3 KiB
Swift
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
|
|
}
|
|
}
|