Skip to content

Commit bef8992

Browse files
committed
Add reaction tests.
1 parent c9f0256 commit bef8992

File tree

3 files changed

+69
-16
lines changed

3 files changed

+69
-16
lines changed

Sources/AblyChat/DefaultRoomReactions.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal final class DefaultRoomReactions: RoomReactions {
1111
try await implementation.send(params: params)
1212
}
1313

14+
@discardableResult
1415
internal func subscribe(_ callback: @escaping @MainActor (Reaction) -> Void) -> SubscriptionHandle {
1516
implementation.subscribe(callback)
1617
}

Tests/AblyChatTests/DefaultRoomReactionsTests.swift

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,72 @@ struct DefaultRoomReactionsTests {
2929
#expect(channel.publishedMessages.last?.extras == ["headers": ["someHeadersKey": "someHeadersValue"], "ephemeral": true])
3030
}
3131

32-
// @spec CHA-ER4
32+
// @spec CHA-ER4a
33+
// @spec CHA-ER4b
3334
@Test
34-
func subscribe_returnsSubscription() async throws {
35-
// all setup values here are arbitrary
35+
func subscriptionCanBeRegisteredToReceiveReactionEvents() async throws {
3636
// Given
37-
let channel = MockRealtimeChannel(name: "basketball::$chat")
37+
func generateMessage(serial: String, reactionType: String) -> ARTMessage {
38+
let message = ARTMessage()
39+
message.action = .create // arbitrary
40+
message.serial = serial // arbitrary
41+
message.clientId = "" // arbitrary
42+
message.data = [
43+
"type": reactionType,
44+
]
45+
message.version = "0"
46+
return message
47+
}
3848

39-
// When
49+
let channel = MockRealtimeChannel(
50+
messageToEmitOnSubscribe: generateMessage(serial: "1", reactionType: ":like:")
51+
)
4052
let defaultRoomReactions = DefaultRoomReactions(channel: channel, clientID: "mockClientId", roomID: "basketball", logger: TestLogger())
4153

4254
// When
43-
let subscription: Subscription<Reaction>? = defaultRoomReactions.subscribe()
55+
let subscriptionHandle = defaultRoomReactions.subscribe { reaction in
56+
// Then
57+
#expect(reaction.type == ":like:")
58+
}
4459

45-
// Then
46-
#expect(subscription != nil)
60+
// CHA-ER4b
61+
subscriptionHandle.unsubscribe()
62+
63+
// will not be received and expectations above will not fail
64+
channel.simulateIncomingMessage(
65+
generateMessage(serial: "2", reactionType: ":dislike:"),
66+
for: RoomReactionEvents.reaction.rawValue
67+
)
68+
}
69+
70+
// CHA-ER4c is currently untestable due to not subscribing to those events on lower level
71+
// @spec CHA-ER4d
72+
@Test
73+
func malformedRealtimeEventsShallNotBeEmittedToSubscribers() async throws {
74+
// Given
75+
let channel = MockRealtimeChannel(
76+
messageJSONToEmitOnSubscribe: [
77+
"foo": "bar", // malformed reaction message
78+
],
79+
messageToEmitOnSubscribe: {
80+
let message = ARTMessage()
81+
message.action = .create // arbitrary
82+
message.serial = "123" // arbitrary
83+
message.clientId = "" // arbitrary
84+
message.data = [
85+
"type": ":like:",
86+
]
87+
message.extras = [:] as any ARTJsonCompatible
88+
message.version = "0"
89+
return message
90+
}()
91+
)
92+
let defaultRoomReactions = DefaultRoomReactions(channel: channel, clientID: "mockClientId", roomID: "basketball", logger: TestLogger())
93+
94+
// When
95+
defaultRoomReactions.subscribe { reaction in
96+
// Then: `messageJSONToEmitOnSubscribe` is processed ahead of `messageToEmitOnSubscribe` in the mock, but the first message is not the malformed one
97+
#expect(reaction.type == ":like:")
98+
}
4799
}
48100
}

Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class MockRealtimeChannel: InternalRealtimeChannelProtocol {
2929
initialErrorReason: ARTErrorInfo? = nil,
3030
attachBehavior: AttachOrDetachBehavior? = nil,
3131
detachBehavior: AttachOrDetachBehavior? = nil,
32-
messageJSONToEmitOnSubscribe: [String: Sendable]? = nil,
32+
messageJSONToEmitOnSubscribe: [String: JSONValue]? = nil,
3333
messageToEmitOnSubscribe: ARTMessage? = nil,
3434
stateChangeToEmitForListener: ARTChannelStateChange? = nil
3535
) {
@@ -133,28 +133,28 @@ final class MockRealtimeChannel: InternalRealtimeChannelProtocol {
133133
try result.get()
134134
}
135135

136-
let messageJSONToEmitOnSubscribe: [String: Sendable]?
136+
let messageJSONToEmitOnSubscribe: [String: JSONValue]?
137137
let messageToEmitOnSubscribe: ARTMessage?
138138

139139
// Added the ability to emit a message whenever we want instead of just on subscribe... I didn't want to dig into what the messageToEmitOnSubscribe is too much so just if/else between the two.
140140
private var channelSubscriptions: [(String, (ARTMessage) -> Void)] = []
141141

142142
func subscribe(_ name: String, callback: @escaping @MainActor (ARTMessage) -> Void) -> ARTEventListener? {
143143
if let json = messageJSONToEmitOnSubscribe {
144-
let message = ARTMessage(name: nil, data: json["data"] ?? "")
145-
if let action = json["action"] as? UInt {
144+
let message = ARTMessage(name: nil, data: json["data"]?.toAblyCocoaData ?? "")
145+
if let action = json["action"]?.numberValue as? UInt {
146146
message.action = ARTMessageAction(rawValue: action) ?? .create
147147
}
148-
if let serial = json["serial"] as? String {
148+
if let serial = json["serial"]?.stringValue {
149149
message.serial = serial
150150
}
151-
if let clientId = json["clientId"] as? String {
151+
if let clientId = json["clientId"]?.stringValue {
152152
message.clientId = clientId
153153
}
154-
if let extras = json["extras"] as? ARTJsonCompatible {
154+
if let extras = json["extras"]?.objectValue?.toARTJsonCompatible {
155155
message.extras = extras
156156
}
157-
if let ts = json["timestamp"] as? String {
157+
if let ts = json["timestamp"]?.stringValue {
158158
message.timestamp = Date(timeIntervalSince1970: TimeInterval(ts)!)
159159
}
160160
callback(message)

0 commit comments

Comments
 (0)