-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added tests for presence and typing indicators (WIP).
- Loading branch information
Showing
6 changed files
with
458 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
import Ably | ||
@testable import AblyChat | ||
import Testing | ||
|
||
struct DefaultRoomPresenceTests { | ||
// @spec CHA-PR1 | ||
@Test | ||
func channelNameIsSetAsChatMessagesChannelName() async throws { | ||
// Given | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages") | ||
let featureChannel = MockFeatureChannel(channel: channel) | ||
|
||
// When | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger()) | ||
|
||
// Then | ||
#expect(defaultPresence.channel.name == "basketball::$chat::$chatMessages") | ||
} | ||
|
||
// @spec CHA-PR5 | ||
@Test | ||
func ifUserIsPresent() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1", "client2"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) // CHA-PR6d | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger()) | ||
|
||
// When | ||
let isUserPresent1 = try await defaultPresence.isUserPresent(clientID: "client2") | ||
let isUserPresent2 = try await defaultPresence.isUserPresent(clientID: "client3") | ||
|
||
// Then | ||
#expect(isUserPresent1 == true) | ||
#expect(isUserPresent2 == false) | ||
} | ||
|
||
// @spec CHA-PR3a | ||
// @spec CHA-PR3e | ||
@Test | ||
func usersMayEnterPresence() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "client2", logger: TestLogger()) | ||
|
||
// When | ||
try await defaultPresence.enter(data: ["status": "Online"]) | ||
|
||
// Then | ||
let presenceMembers = try await defaultPresence.get() | ||
#expect(presenceMembers.map { $0.clientID }.sorted() == ["client1", "client2"]) | ||
let client2 = presenceMembers.filter { member in | ||
member.clientID == "client2" && member.data?.objectValue?["status"]?.stringValue == "Online" | ||
} | ||
#expect(client2 != nil) | ||
} | ||
|
||
// @spec CHA-PR10a | ||
// @spec CHA-PR10e | ||
@Test | ||
func usersMayUpdatePresence() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "client1", logger: TestLogger()) | ||
|
||
// When | ||
try await defaultPresence.update(data: ["status": "Online"]) | ||
|
||
// Then | ||
let presenceMembers = try await defaultPresence.get() | ||
let client1 = presenceMembers.filter { member in | ||
member.clientID == "client1" && member.data?.objectValue?["status"]?.stringValue == "Online" | ||
} | ||
#expect(client1 != nil) | ||
} | ||
|
||
// @spec CHA-PR4a | ||
@Test | ||
func usersMayLeavePresence() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "client1", logger: TestLogger()) | ||
|
||
// When | ||
try await defaultPresence.leave() | ||
|
||
// Then | ||
let presenceMembers = try await defaultPresence.get() | ||
#expect(presenceMembers.isEmpty) | ||
} | ||
|
||
// @spec CHA-PR6 | ||
// @spec CHA-PR6d | ||
@Test | ||
func retrieveAllTheMembersOfThePresenceSet() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1", "client2"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger()) | ||
|
||
// When | ||
let presenceMembers = try await defaultPresence.get() | ||
|
||
// Then | ||
#expect(presenceMembers.map { $0.clientID }.sorted() == ["client1", "client2"]) | ||
} | ||
|
||
// @spec CHA-PR6h | ||
@Test | ||
func failToRetrieveAllTheMembersOfThePresenceSetWhenRoomInInvalidState() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1", "client2"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let error = ARTErrorInfo(chatError: .presenceOperationRequiresRoomAttach(feature: .presence)) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .failure(error)) | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger()) | ||
|
||
// Then | ||
await #expect(throws: ARTErrorInfo.self) { | ||
do { | ||
_ = try await defaultPresence.get() | ||
} catch { | ||
let error = try #require(error as? ARTErrorInfo) | ||
#expect(error.statusCode == 400) | ||
#expect(error.localizedDescription.contains("attach")) | ||
throw error | ||
} | ||
} | ||
} | ||
|
||
// @spec CHA-PR7a | ||
// @spec CHA-PR7b | ||
// @spec CHA-PR7c | ||
@Test | ||
func usersMaySubscribeToAllPresenceEvents() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence(["client1", "client2"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) // CHA-PR6d | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger()) | ||
|
||
// Given | ||
let subscription = await defaultPresence.subscribe(events: .all) // CHA-PR7a and CHA-PR7b since `all` is just a selection of all events | ||
|
||
// When | ||
subscription.emit(PresenceEvent(action: .present, clientID: "client1", timestamp: Date(), data: nil)) | ||
|
||
// Then | ||
let presentEvent = try #require(await subscription.first { _ in true }) | ||
#expect(presentEvent.action == .present) | ||
#expect(presentEvent.clientID == "client1") | ||
|
||
// When | ||
subscription.emit(PresenceEvent(action: .enter, clientID: "client1", timestamp: Date(), data: nil)) | ||
|
||
// Then | ||
let enterEvent = try #require(await subscription.first { _ in true }) | ||
#expect(enterEvent.action == .enter) | ||
#expect(enterEvent.clientID == "client1") | ||
|
||
// When | ||
subscription.emit(PresenceEvent(action: .update, clientID: "client1", timestamp: Date(), data: nil)) | ||
|
||
// Then | ||
let updateEvent = try #require(await subscription.first { _ in true }) | ||
#expect(updateEvent.action == .update) | ||
#expect(updateEvent.clientID == "client1") | ||
|
||
// When | ||
subscription.emit(PresenceEvent(action: .leave, clientID: "client1", timestamp: Date(), data: nil)) | ||
|
||
// Then | ||
let leaveEvent = try #require(await subscription.first { _ in true }) | ||
#expect(leaveEvent.action == .leave) | ||
#expect(leaveEvent.clientID == "client1") | ||
|
||
// CHA-PR7c | ||
|
||
// When | ||
subscription.unsubscribe() | ||
|
||
// When | ||
subscription.emit(PresenceEvent(action: .present, clientID: "client1", timestamp: Date(), data: nil)) | ||
subscription.emit(PresenceEvent(action: .enter, clientID: "client1", timestamp: Date(), data: nil)) | ||
subscription.emit(PresenceEvent(action: .leave, clientID: "client1", timestamp: Date(), data: nil)) | ||
subscription.emit(PresenceEvent(action: .update, clientID: "client1", timestamp: Date(), data: nil)) | ||
|
||
// Then | ||
let nilAnyEvent = await subscription.first { _ in true } | ||
#expect(nilAnyEvent == nil) | ||
} | ||
|
||
// @spec CHA-PR8 | ||
@Test | ||
func onDiscontinuity() async throws { | ||
// Given | ||
let realtimePresence = MockRealtimePresence([]) | ||
let channel = MockRealtimeChannel(mockPresence: realtimePresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultPresence = await DefaultPresence(featureChannel: featureChannel, roomID: "basketball", clientID: "client1", logger: TestLogger()) | ||
|
||
// When: The feature channel emits a discontinuity through `onDiscontinuity` | ||
let featureChannelDiscontinuity = DiscontinuityEvent(error: ARTErrorInfo.createUnknownError()) // arbitrary error | ||
let discontinuitySubscription = await defaultPresence.onDiscontinuity() | ||
await featureChannel.emitDiscontinuity(featureChannelDiscontinuity) | ||
|
||
// Then: The DefaultOccupancy instance emits this discontinuity through `onDiscontinuity` | ||
let discontinuity = try #require(await discontinuitySubscription.first { _ in true }) | ||
#expect(discontinuity == featureChannelDiscontinuity) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import Ably | ||
@testable import AblyChat | ||
import Testing | ||
|
||
struct DefaultRoomTypingTests { | ||
// @spec CHA-T1 | ||
@Test | ||
func channelNameIsSetAsTypingIndicatorsChannelName() async throws { | ||
// Given | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$typingIndicators") | ||
let featureChannel = MockFeatureChannel(channel: channel) | ||
|
||
// When | ||
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger(), timeout: 5) | ||
|
||
// Then | ||
#expect(defaultTyping.channel.name == "basketball::$chat::$typingIndicators") | ||
} | ||
|
||
// @spec CHA-T2 | ||
@Test | ||
func retrieveCurrentlyTypingClientIDs() async throws { | ||
// Given | ||
let typingPresence = MockRealtimePresence(["client1", "client2"].map { .init(clientId: $0) }) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$typingIndicators", mockPresence: typingPresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger(), timeout: 5) | ||
|
||
// When | ||
let typingInfo = try await defaultTyping.get() | ||
|
||
// Then | ||
#expect(typingInfo.sorted() == ["client1", "client2"]) | ||
} | ||
|
||
// @spec CHA-T4 | ||
// @spec CHA-T5 | ||
@Test | ||
func usersMayIndicateThatTheyHaveStartedOrStoppedTyping() async throws { | ||
// Given | ||
let typingPresence = MockRealtimePresence([]) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$typingIndicators", mockPresence: typingPresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "client1", logger: TestLogger(), timeout: 5) | ||
|
||
// CHA-T4 | ||
|
||
// When | ||
try await defaultTyping.start() | ||
|
||
// Then | ||
var typingInfo = try await defaultTyping.get() | ||
#expect(typingInfo == ["client1"]) | ||
|
||
// CHA-T5 | ||
|
||
// When | ||
try await defaultTyping.stop() | ||
|
||
// Then | ||
typingInfo = try await defaultTyping.get() | ||
#expect(typingInfo.isEmpty) | ||
} | ||
|
||
// @spec CHA-T6 | ||
@Test | ||
func usersMaySubscribeToTypingEvents() async throws { | ||
// Given | ||
let typingPresence = MockRealtimePresence([]) | ||
let channel = MockRealtimeChannel(name: "basketball::$chat::$typingIndicators", mockPresence: typingPresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "client1", logger: TestLogger(), timeout: 5) | ||
|
||
// When | ||
let subscription = await defaultTyping.subscribe() | ||
subscription.emit(TypingEvent(currentlyTyping: ["client1"])) | ||
|
||
// Then | ||
let typingEvent = try #require(await subscription.first { _ in true }) | ||
#expect(typingEvent.currentlyTyping == ["client1"]) | ||
} | ||
|
||
// @spec CHA-T7 | ||
@Test | ||
func onDiscontinuity() async throws { | ||
// Given | ||
let typingPresence = MockRealtimePresence([]) | ||
let channel = MockRealtimeChannel(mockPresence: typingPresence) | ||
let featureChannel = MockFeatureChannel(channel: channel, resultOfWaitToBeAblePerformPresenceOperations: .success(())) | ||
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "client1", logger: TestLogger(), timeout: 5) | ||
|
||
// When: The feature channel emits a discontinuity through `onDiscontinuity` | ||
let featureChannelDiscontinuity = DiscontinuityEvent(error: ARTErrorInfo.createUnknownError()) // arbitrary error | ||
let discontinuitySubscription = await defaultTyping.onDiscontinuity() | ||
await featureChannel.emitDiscontinuity(featureChannelDiscontinuity) | ||
|
||
// Then: The DefaultOccupancy instance emits this discontinuity through `onDiscontinuity` | ||
let discontinuity = try #require(await discontinuitySubscription.first { _ in true }) | ||
#expect(discontinuity == featureChannelDiscontinuity) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.