Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/AblyChat/DefaultRoomReactions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal final class DefaultRoomReactions: RoomReactions {
try await implementation.send(params: params)
}

@discardableResult
internal func subscribe(_ callback: @escaping @MainActor (Reaction) -> Void) -> SubscriptionProtocol {
implementation.subscribe(callback)
}
Expand Down
69 changes: 61 additions & 8 deletions Tests/AblyChatTests/DefaultRoomReactionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,73 @@ struct DefaultRoomReactionsTests {
#expect(channel.publishedMessages.last?.extras == ["headers": ["someHeadersKey": "someHeadersValue"], "ephemeral": true])
}

// @spec CHA-ER4
// @spec CHA-ER4a
// @spec CHA-ER4b
@Test
func subscribe_returnsSubscription() async throws {
// all setup values here are arbitrary
func subscriptionCanBeRegisteredToReceiveReactionEvents() async throws {
// Given
let channel = MockRealtimeChannel(name: "basketball::$chat")
func generateMessage(serial: String, reactionType: String) -> ARTMessage {
let message = ARTMessage()
message.action = .create // arbitrary
message.serial = serial // arbitrary
message.clientId = "" // arbitrary
message.data = [
"type": reactionType,
]
message.version = "0"
return message
}

// When
let channel = MockRealtimeChannel(
messageToEmitOnSubscribe: generateMessage(serial: "1", reactionType: ":like:")
)
let defaultRoomReactions = DefaultRoomReactions(channel: channel, clientID: "mockClientId", roomID: "basketball", logger: TestLogger())

// When
let subscription: SubscriptionAsyncSequence<Reaction>? = defaultRoomReactions.subscribe()
let subscription = defaultRoomReactions.subscribe { reaction in
// Then
#expect(reaction.type == ":like:")
}

// Then
#expect(subscription != nil)
// CHA-ER4b
subscription.unsubscribe()

// will not be received and expectations above will not fail
channel.simulateIncomingMessage(
generateMessage(serial: "2", reactionType: ":dislike:"),
for: RoomReactionEvents.reaction.rawValue
)
}

// CHA-ER4c is currently untestable due to not subscribing to those events on lower level
// @spec CHA-ER4d
@Test
func malformedRealtimeEventsShallNotBeEmittedToSubscribers() async throws {
// Given
let channel = MockRealtimeChannel(
messageToEmitOnSubscribe: {
let message = ARTMessage()
message.action = .create // arbitrary
message.serial = "123" // arbitrary
message.clientId = "" // arbitrary
message.data = [
"type": ":like:",
]
message.extras = [:] as any ARTJsonCompatible
message.version = "0"
return message
}()
)
let defaultRoomReactions = DefaultRoomReactions(channel: channel, clientID: "mockClientId", roomID: "basketball", logger: TestLogger())

// When
defaultRoomReactions.subscribe { reaction in
#expect(reaction.type == ":like:")
}
// will not be received and expectations above will not fail
channel.simulateIncomingMessage(
ARTMessage(), // malformed message
for: RealtimeMessageName.chatMessage.rawValue
)
}
}
17 changes: 8 additions & 9 deletions Tests/AblyChatTests/Helpers/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,17 +150,16 @@ class MockMethodCallRecorder: @unchecked Sendable {
}

func hasRecord(matching signature: String, arguments: [String: Any]) -> Bool {
mutex.lock()
let result = records.contains { record in
guard record.signature == signature else {
return false
mutex.withLock {
records.contains { record in
guard record.signature == signature else {
return false
}
let args1 = record.arguments.sorted()
let args2 = arguments.map { MethodArgument(name: $0.key, value: $0.value) }.sorted()
return args1 == args2
}
let args1 = record.arguments.sorted()
let args2 = arguments.map { MethodArgument(name: $0.key, value: $0.value) }.sorted()
return args1 == args2
}
mutex.unlock()
return result
}
}

Expand Down