168 lines
5.5 KiB
Swift
168 lines
5.5 KiB
Swift
import Testing
|
|
@testable import Rosetta
|
|
|
|
/// Verifies PacketPushNotification wire format matches server
|
|
/// (im.rosetta.packet.Packet16PushNotification).
|
|
///
|
|
/// Server wire format:
|
|
/// writeInt16(packetId=0x10)
|
|
/// writeString(notificationToken)
|
|
/// writeInt8(action) — 0=subscribe, 1=unsubscribe
|
|
/// writeInt8(tokenType) — 0=FCM, 1=VoIPApns
|
|
/// 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 (server: FCM)")
|
|
func fcmTokenTypeValue() {
|
|
#expect(PushTokenType.fcm.rawValue == 0)
|
|
}
|
|
|
|
@Test("PushTokenType.voipApns == 1 (server: VoIPApns)")
|
|
func voipTokenTypeValue() {
|
|
#expect(PushTokenType.voipApns.rawValue == 1)
|
|
}
|
|
|
|
// MARK: - Round Trip (encode → decode)
|
|
|
|
@Test("FCM subscribe round-trip preserves all fields")
|
|
func fcmSubscribeRoundTrip() throws {
|
|
var packet = PacketPushNotification()
|
|
packet.notificationsToken = "test-fcm-token-abc123"
|
|
packet.action = .subscribe
|
|
packet.tokenType = .fcm
|
|
packet.deviceId = "device-id-xyz"
|
|
|
|
let decoded = try decodePushNotification(packet)
|
|
#expect(decoded.notificationsToken == "test-fcm-token-abc123")
|
|
#expect(decoded.action == .subscribe)
|
|
#expect(decoded.tokenType == .fcm)
|
|
#expect(decoded.deviceId == "device-id-xyz")
|
|
}
|
|
|
|
@Test("VoIP unsubscribe round-trip preserves all fields")
|
|
func voipUnsubscribeRoundTrip() throws {
|
|
var packet = PacketPushNotification()
|
|
packet.notificationsToken = "voip-hex-token-deadbeef"
|
|
packet.action = .unsubscribe
|
|
packet.tokenType = .voipApns
|
|
packet.deviceId = "another-device-id"
|
|
|
|
let decoded = try decodePushNotification(packet)
|
|
#expect(decoded.notificationsToken == "voip-hex-token-deadbeef")
|
|
#expect(decoded.action == .unsubscribe)
|
|
#expect(decoded.tokenType == .voipApns)
|
|
#expect(decoded.deviceId == "another-device-id")
|
|
}
|
|
|
|
@Test("Empty token and deviceId 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.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 = .voipApns // 1
|
|
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 = 1 (voipApns)
|
|
// [10-13] string length = 1 for "D"
|
|
// [14-15] 'D' = 0x0044 (UInt16 big-endian)
|
|
|
|
#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 = 1 (voipApns)
|
|
#expect(data[9] == 0x01)
|
|
|
|
// 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
|
|
}
|
|
}
|