Skip to content

Commit

Permalink
Added typing tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
maratal committed Jan 12, 2025
1 parent 444de76 commit 172a9d5
Showing 1 changed file with 199 additions and 0 deletions.
199 changes: 199 additions & 0 deletions Tests/AblyChatTests/DefaultRoomTypingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import Ably
@testable import AblyChat
import Testing

struct DefaultRoomTypingTests {
// MARK: - Test helpers

// @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"])
}

// @specPartial CHA-T2c
@Test
func retrieveCurrentlyTypingClientIDsWhileAttaching() async throws {
// Given: A DefaultRoomLifecycleManager, with an ATTACH operation in progress and hence in the ATTACHING status
let contributor = RoomLifecycleHelper.createContributor(feature: .presence, attachBehavior: .completeAndChangeState(.success, newState: .attached, delayInMilliseconds: RoomLifecycleHelper.fakeNetworkDelay))
let lifecycleManager = await RoomLifecycleHelper.createManager(contributors: [contributor])

// Given: A DefaultPresence with DefaultFeatureChannel and MockRoomLifecycleContributor
let realtimePresence = MockRealtimePresence(["client1"].map { .init(clientId: $0) })
let channel = MockRealtimeChannel(name: "basketball::$chat::$chatMessages", mockPresence: realtimePresence)
let featureChannel = DefaultFeatureChannel(channel: channel, contributor: contributor, roomLifecycleManager: lifecycleManager)
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger(), timeout: 5)

let attachingStatusWaitSubscription = await lifecycleManager.testsOnly_subscribeToStatusChangeWaitEvents()

// When: The room is in the attaching state
let roomStatusSubscription = await lifecycleManager.onRoomStatusChange(bufferingPolicy: .unbounded)

let attachOperationID = UUID()
async let _ = lifecycleManager.performAttachOperation(testsOnly_forcingOperationID: attachOperationID)

// Wait for room to become ATTACHING
_ = try #require(await roomStatusSubscription.attachingElements().first { _ in true })

// When: And presence get is called
_ = try await defaultTyping.get()

// Then: The manager was waiting for its room status to change before presence `get` was called
_ = try #require(await attachingStatusWaitSubscription.first { _ in true })
}

// @specPartial CHA-T2c
@Test
func retrieveCurrentlyTypingClientIDsWhileAttachingWithFailure() async throws {
// Given: attachment failure
let attachError = ARTErrorInfo(domain: "SomeDomain", code: 123)

// Given: A DefaultRoomLifecycleManager, with an ATTACH operation in progress and hence in the ATTACHING status
let contributor = RoomLifecycleHelper.createContributor(feature: .presence, attachBehavior: .completeAndChangeState(.failure(attachError), newState: .failed, delayInMilliseconds: RoomLifecycleHelper.fakeNetworkDelay))
let lifecycleManager = await RoomLifecycleHelper.createManager(contributors: [contributor])

// Given: A DefaultPresence with DefaultFeatureChannel and MockRoomLifecycleContributor
let realtimePresence = MockRealtimePresence(["client1"].map { .init(clientId: $0) })
let channel = MockRealtimeChannel(mockPresence: realtimePresence)
let featureChannel = DefaultFeatureChannel(channel: channel, contributor: contributor, roomLifecycleManager: lifecycleManager)
let defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger(), timeout: 5)

let attachingStatusWaitSubscription = await lifecycleManager.testsOnly_subscribeToStatusChangeWaitEvents()

// When: The room is in the attaching state
let roomStatusSubscription = await lifecycleManager.onRoomStatusChange(bufferingPolicy: .unbounded)

let attachOperationID = UUID()
async let _ = lifecycleManager.performAttachOperation(testsOnly_forcingOperationID: attachOperationID)

// Wait for room to become ATTACHING
_ = try #require(await roomStatusSubscription.attachingElements().first { _ in true })

// When: And fails to attach
await #expect(throws: ARTErrorInfo.self) {
do {
_ = try await defaultTyping.get()
} catch {
// Then: An exception with status code of 500 should be thrown
let error = try #require(error as? ARTErrorInfo)
#expect(error.statusCode == 500)
#expect(error.code == ErrorCode.roomInInvalidState.rawValue)
throw error
}
}
// Then: The manager were waiting for its room status to change from attaching
_ = try #require(await attachingStatusWaitSubscription.first { _ in true })
}

// @spec CHA-T2g
@Test
func failToRetrieveCurrentlyTypingClientIDsWhenRoomInInvalidState() 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 defaultTyping = DefaultTyping(featureChannel: featureChannel, roomID: "basketball", clientID: "mockClientId", logger: TestLogger(), timeout: 5)

// Then
await #expect(throws: ARTErrorInfo.self) {
do {
_ = try await defaultTyping.get()
} catch {
let error = try #require(error as? ARTErrorInfo)
#expect(error.statusCode == 400)
#expect(error.localizedDescription.contains("attach"))
throw error
}
}
}

// @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-T6a
// @spec CHA-T6b
@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)

// CHA-T6a

// 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"])

// CHA-T6b

// When
subscription.unsubscribe()
subscription.emit(TypingEvent(currentlyTyping: ["client1"]))

// Then
let nilTypingEvento = await subscription.first { _ in true }
#expect(nilTypingEvento == nil)
}

// @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)
}
}

0 comments on commit 172a9d5

Please sign in to comment.