Skip to content

Commit

Permalink
Implementing throttling for typing and message receipt events
Browse files Browse the repository at this point in the history
  • Loading branch information
mliao95 committed Jun 27, 2024
1 parent 2287910 commit 68dca26
Show file tree
Hide file tree
Showing 14 changed files with 875 additions and 194 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@
F76BB94D2C1898BE0005C3EC /* APIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76BB94C2C1898BE0005C3EC /* APIClient.swift */; };
F76BB9532C18F84D0005C3EC /* MockAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76BB9522C18F84D0005C3EC /* MockAPIClient.swift */; };
F76BB9562C1900480005C3EC /* APIClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76BB9552C1900480005C3EC /* APIClientTests.swift */; };
F76BB9722C2A48C10005C3EC /* MessageReceiptsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76BB9712C2A48C10005C3EC /* MessageReceiptsManager.swift */; };
F76BB9742C2BA73E0005C3EC /* MessageReceiptsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76BB9732C2BA73E0005C3EC /* MessageReceiptsManagerTests.swift */; };
F76BB9762C2BFED80005C3EC /* MockMessageReceiptsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76BB9752C2BFED80005C3EC /* MockMessageReceiptsManager.swift */; };
F7B0747C2BDB298400E8B805 /* HeartbeatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B0747B2BDB298400E8B805 /* HeartbeatManager.swift */; };
F7B0747F2BDB2A0700E8B805 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B0747E2BDB2A0700E8B805 /* Notifications.swift */; };
F7B074822BDB2D3A00E8B805 /* NetworkConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B074812BDB2D3A00E8B805 /* NetworkConnectionManager.swift */; };
F7B4A1282C1157B0005C7921 /* AttachmentTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B4A1272C1157B0005C7921 /* AttachmentTypes.swift */; };
F7B4A1362C14FDA9005C7921 /* MockAttachmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B4A1352C14FDA9005C7921 /* MockAttachmentManager.swift */; };
F7B4A1382C15649B005C7921 /* MockHttpClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B4A1372C15649B005C7921 /* MockHttpClient.swift */; };
F7B4A13A2C17AE43005C7921 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B4A1392C17AE43005C7921 /* TestUtils.swift */; };
F7B4A13C2C17E3C3005C7921 /* MockURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B4A13B2C17E3C3005C7921 /* MockURLSession.swift */; };
Expand Down Expand Up @@ -119,11 +121,13 @@
F76BB94C2C1898BE0005C3EC /* APIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIClient.swift; sourceTree = "<group>"; };
F76BB9522C18F84D0005C3EC /* MockAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAPIClient.swift; sourceTree = "<group>"; };
F76BB9552C1900480005C3EC /* APIClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIClientTests.swift; sourceTree = "<group>"; };
F76BB9712C2A48C10005C3EC /* MessageReceiptsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiptsManager.swift; sourceTree = "<group>"; };
F76BB9732C2BA73E0005C3EC /* MessageReceiptsManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiptsManagerTests.swift; sourceTree = "<group>"; };
F76BB9752C2BFED80005C3EC /* MockMessageReceiptsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessageReceiptsManager.swift; sourceTree = "<group>"; };
F7B0747B2BDB298400E8B805 /* HeartbeatManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeartbeatManager.swift; sourceTree = "<group>"; };
F7B0747E2BDB2A0700E8B805 /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
F7B074812BDB2D3A00E8B805 /* NetworkConnectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnectionManager.swift; sourceTree = "<group>"; };
F7B4A1272C1157B0005C7921 /* AttachmentTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentTypes.swift; sourceTree = "<group>"; };
F7B4A1352C14FDA9005C7921 /* MockAttachmentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAttachmentManager.swift; sourceTree = "<group>"; };
F7B4A1372C15649B005C7921 /* MockHttpClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHttpClient.swift; sourceTree = "<group>"; };
F7B4A1392C17AE43005C7921 /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
F7B4A13B2C17E3C3005C7921 /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -176,6 +180,7 @@
F76BB9542C19000F0005C3EC /* Mocks */,
7E2336E22BFE7D7400E6C4B7 /* AWSClientTests.swift */,
F76BB9552C1900480005C3EC /* APIClientTests.swift */,
F76BB9732C2BA73E0005C3EC /* MessageReceiptsManagerTests.swift */,
);
path = Network;
sourceTree = "<group>";
Expand All @@ -184,7 +189,6 @@
isa = PBXGroup;
children = (
7E2336E72BFE7D7400E6C4B7 /* MockConnectionDetailsProvider.swift */,
F7B4A1352C14FDA9005C7921 /* MockAttachmentManager.swift */,
F7B4A1392C17AE43005C7921 /* TestUtils.swift */,
);
path = utils;
Expand Down Expand Up @@ -279,6 +283,7 @@
F74726462BF41948002B278E /* MetricsManager.swift */,
7EF438492BFF056400E7F8E9 /* AWSConnectParticipantAdapter.swift */,
F76BB94C2C1898BE0005C3EC /* APIClient.swift */,
F76BB9712C2A48C10005C3EC /* MessageReceiptsManager.swift */,
);
path = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -355,6 +360,7 @@
F76BB9522C18F84D0005C3EC /* MockAPIClient.swift */,
F7B4A1372C15649B005C7921 /* MockHttpClient.swift */,
F7B4A13B2C17E3C3005C7921 /* MockURLSession.swift */,
F76BB9752C2BFED80005C3EC /* MockMessageReceiptsManager.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -550,6 +556,7 @@
7ED5DEC42BEAFD42001693FC /* Event.swift in Sources */,
7E58A60A2BBB460A00965327 /* HttpMethod.swift in Sources */,
F7B0747C2BDB298400E8B805 /* HeartbeatManager.swift in Sources */,
F76BB9722C2A48C10005C3EC /* MessageReceiptsManager.swift in Sources */,
7ED5DEC22BEAF933001693FC /* TranscriptItem.swift in Sources */,
F7B4A1282C1157B0005C7921 /* AttachmentTypes.swift in Sources */,
7ED5DEC62BEB38B8001693FC /* Metadata.swift in Sources */,
Expand Down Expand Up @@ -577,15 +584,16 @@
buildActionMask = 2147483647;
files = (
F7B4A13C2C17E3C3005C7921 /* MockURLSession.swift in Sources */,
F7B4A1362C14FDA9005C7921 /* MockAttachmentManager.swift in Sources */,
F76BB9532C18F84D0005C3EC /* MockAPIClient.swift in Sources */,
F76BB9762C2BFED80005C3EC /* MockMessageReceiptsManager.swift in Sources */,
F76BB9562C1900480005C3EC /* APIClientTests.swift in Sources */,
F7B4A13A2C17AE43005C7921 /* TestUtils.swift in Sources */,
7E2336EF2BFE7D7400E6C4B7 /* MockAWSConnectParticipant.swift in Sources */,
7E2336F22BFE7D7400E6C4B7 /* ChatServiceTests.swift in Sources */,
7E2336F32BFE7D7400E6C4B7 /* MockChatService.swift in Sources */,
7E2336EE2BFE7D7400E6C4B7 /* MockWebSocketManager.swift in Sources */,
7E2336EC2BFE7D7400E6C4B7 /* AWSClientTests.swift in Sources */,
F76BB9742C2BA73E0005C3EC /* MessageReceiptsManagerTests.swift in Sources */,
7E2336F12BFE7D7400E6C4B7 /* ChatSessionTests.swift in Sources */,
7E2336F02BFE7D7400E6C4B7 /* MockConnectionDetailsProvider.swift in Sources */,
F7B4A1382C15649B005C7921 /* MockHttpClient.swift in Sources */,
Expand Down Expand Up @@ -736,7 +744,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 94KV3E626L;
DEVELOPMENT_TEAM = 324G6572KE;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
Expand Down Expand Up @@ -769,7 +777,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 94KV3E626L;
DEVELOPMENT_TEAM = 324G6572KE;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

import XCTest
import AWSConnectParticipant
@testable import AmazonConnectChatIOS

class MessageReceiptsManagerTests: XCTestCase {
var messageReceiptsManager: MockMessageReceiptsManager?

override func setUp() {
messageReceiptsManager = MockMessageReceiptsManager()
messageReceiptsManager?.throttleTime = 0.1
messageReceiptsManager?.deliveredThrottleTime = 0.1
super.setUp()
}

override func tearDown() {
super.tearDown()
}

func waitFor(seconds: TimeInterval) {
let expectation = self.expectation(description: "Waiting")
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
expectation.fulfill()
}
waitForExpectations(timeout: seconds + 1, handler: nil)
}

func testThrottleAndSendMessageReceipt_Succeeds() {
let expectation = self.expectation(description: "throttleAndSendMessageReceipt succeeds")
messageReceiptsManager?.mockThrottleAndSendMessageReceipt = false
let mockPendingMessageReceipts = PendingMessageReceipts(readReceiptMessageId: "12345", deliveredReceiptMessageId: "67890")
messageReceiptsManager?.pendingMessageReceipts = mockPendingMessageReceipts

messageReceiptsManager?.throttleAndSendMessageReceipt(event: .messageRead, messageId: "12345") { result in
switch result {
case .success(let pendingMessageReceipts):
XCTAssertEqual(pendingMessageReceipts.readReceiptMessageId, mockPendingMessageReceipts.readReceiptMessageId)
XCTAssertEqual(pendingMessageReceipts.deliveredReceiptMessageId, mockPendingMessageReceipts.deliveredReceiptMessageId)
expectation.fulfill()
case .failure(let error):
XCTFail("Expected success, got unexpected failure: \(String(describing: error))")
}
}

waitForExpectations(timeout: 1.0, handler: nil)
}

func testHandleMessageReceipt_ReadSucceeds() {

// Check that handling a read receipt updates the set and pendingMessageReceipts object
messageReceiptsManager?.handleMessageReceipt(event: .messageRead, messageId: "12345")
XCTAssertEqual(messageReceiptsManager?.readReceiptSet.count, 1)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.readReceiptMessageId, "12345")

// Check that sending a read receipt for the same message doesn't change the set
messageReceiptsManager?.handleMessageReceipt(event: .messageRead, messageId: "12345")
XCTAssertEqual(messageReceiptsManager?.readReceiptSet.count, 1)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.readReceiptMessageId, "12345")

// Check that sending a read receipt for a different message increases set size and updates pendingMessageReceipts object
messageReceiptsManager?.handleMessageReceipt(event: .messageRead, messageId: "67890")
XCTAssertEqual(messageReceiptsManager?.readReceiptSet.count, 2)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.readReceiptMessageId, "67890")
}

func testHandleMessageReceipt_DeliveredSucceeds() {
// Check that sending a delivered receipt increases set size and is initialy throttled before updating pendingMessageReceipts object
messageReceiptsManager?.handleMessageReceipt(event: .messageDelivered, messageId: "12345")
XCTAssertEqual(messageReceiptsManager?.deliveredReceiptSet.count, 1)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.deliveredReceiptMessageId, nil)
waitFor(seconds: 0.2)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.deliveredReceiptMessageId, "12345")

// Check that sending a duplicate delivered receipt does not increase set.
messageReceiptsManager?.handleMessageReceipt(event: .messageDelivered, messageId: "12345")
XCTAssertEqual(messageReceiptsManager?.deliveredReceiptSet.count, 1)

// Check that sending a delivered receipt for a new message increases set size and updates pendingMessageReceipts object
messageReceiptsManager?.handleMessageReceipt(event: .messageDelivered, messageId: "67890")
XCTAssertEqual(messageReceiptsManager?.deliveredReceiptSet.count, 2)
waitFor(seconds: 0.2)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.deliveredReceiptMessageId, "67890")
}

func testHandleMessageReceipt_ReadDeliveredSucceeds() {
// Send message receipt for a given message
messageReceiptsManager?.handleMessageReceipt(event: .messageRead, messageId: "12345")
XCTAssertEqual(messageReceiptsManager?.readReceiptSet.count, 1)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.readReceiptMessageId, "12345")

// Send delivered receipt for same message and expect pendingMessageReceipts.deliveredReceiptMessageId to not be updated
messageReceiptsManager?.handleMessageReceipt(event: .messageDelivered, messageId: "12345")
XCTAssertEqual(messageReceiptsManager?.deliveredReceiptSet.count, 0)
waitFor(seconds: 0.2)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.deliveredReceiptMessageId, nil)

// Send delivered receipt immediately followed by read receipt and expect pendingMessageReceipts.deliveredReceiptMessageId to be unchanged.
messageReceiptsManager?.handleMessageReceipt(event: .messageDelivered, messageId: "67890")
messageReceiptsManager?.handleMessageReceipt(event: .messageRead, messageId: "67890")
XCTAssertEqual(messageReceiptsManager?.deliveredReceiptSet.count, 1)
waitFor(seconds: 0.2)
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.readReceiptMessageId, "67890")
XCTAssertEqual(messageReceiptsManager?.pendingMessageReceipts.deliveredReceiptMessageId, nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class MockAWSClient: AWSClientProtocol {
var startAttachmentUploadResult: Result<AWSConnectParticipantStartAttachmentUploadResponse, Error>?
var completeAttachmentUploadResult: Result<AWSConnectParticipantCompleteAttachmentUploadResponse, Error>?
var getAttachmentResult: Result<AWSConnectParticipantGetAttachmentResponse, Error>?
var numTypingEventCalled: Int = 0

func createParticipantConnection(participantToken: String, completion: @escaping (Result<ConnectionDetails, Error>) -> Void) {
if let result = createParticipantConnectionResult {
Expand All @@ -34,6 +35,9 @@ class MockAWSClient: AWSClientProtocol {
}

func sendEvent(connectionToken: String, contentType: ContentType, content: String, completion: @escaping (Result<Bool, Error>) -> Void) {
if contentType == .typing {
numTypingEventCalled += 1
}
if let result = sendEventResult {
completion(result)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

import UniformTypeIdentifiers
import AWSConnectParticipant

@testable import AmazonConnectChatIOS

class MockMessageReceiptsManager: MessageReceiptsManager {
var mockThrottleAndSendMessageReceipt = true

var numThrottleAndSendMessageReceiptCalled = 0
var numInvalidateTimerCalled = 0
var numHandleMessageReceiptCalled = 0

var throttleAndSendMessageReceiptResult: Result<AmazonConnectChatIOS.PendingMessageReceipts, Error>?

override func throttleAndSendMessageReceipt(event: AmazonConnectChatIOS.MessageReceiptType, messageId: String, completion: @escaping (Result<AmazonConnectChatIOS.PendingMessageReceipts, any Error>) -> Void) {

numThrottleAndSendMessageReceiptCalled += 1
if !mockThrottleAndSendMessageReceipt {
super.throttleAndSendMessageReceipt(event: event, messageId: messageId, completion: completion)
return
}

if let result = throttleAndSendMessageReceiptResult {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
switch result {
case .success(let pendingMessageReceipts):
completion(.success(pendingMessageReceipts))
break
case .failure(let error):
completion(.failure(error))
}
}
}
}

override func invalidateTimer() {
numInvalidateTimerCalled += 1
super.invalidateTimer()
}

override func handleMessageReceipt(event: AmazonConnectChatIOS.MessageReceiptType, messageId: String) {
numHandleMessageReceiptCalled += 1
super.handleMessageReceipt(event: event, messageId: messageId)
}
}
Loading

0 comments on commit 68dca26

Please sign in to comment.