Skip to content

Commit

Permalink
Add test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
nuno-vieira committed Nov 12, 2024
1 parent dfdf8b6 commit 548b5a1
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 13 deletions.
1 change: 1 addition & 0 deletions TestTools/StreamChatTestTools/Fixtures/JSONs/Member.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"ban_expires" : "2021-03-08T15:42:31.355923Z",
"user_id" : "broken-waterfall-5",
"channel_role" : "owner",
"is_premium": true,
"user" : {
"id" : "broken-waterfall-5",
"banned" : false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ final class ChannelMemberUpdater_Mock: ChannelMemberUpdater {
@Atomic var unbanMember_completion: ((Error?) -> Void)?
@Atomic var unbanMember_completion_result: Result<Void, Error>?

@Atomic var partialUpdate_userId: UserId?
@Atomic var partialUpdate_cid: ChannelId?
@Atomic var partialUpdate_extraData: [String: RawJSON]?
@Atomic var partialUpdate_unset: [String]?
@Atomic var partialUpdate_completion: ((Result<ChatChannelMember, Error>) -> Void)?
@Atomic var partialUpdate_completion_result: Result<ChatChannelMember, Error>?

func cleanUp() {
banMember_userId = nil
banMember_cid = nil
Expand All @@ -33,6 +40,13 @@ final class ChannelMemberUpdater_Mock: ChannelMemberUpdater {
unbanMember_cid = nil
unbanMember_completion = nil
unbanMember_completion_result = nil

partialUpdate_userId = nil
partialUpdate_cid = nil
partialUpdate_extraData = nil
partialUpdate_unset = nil
partialUpdate_completion = nil
partialUpdate_completion_result = nil
}

override func banMember(
Expand Down Expand Up @@ -61,4 +75,19 @@ final class ChannelMemberUpdater_Mock: ChannelMemberUpdater {
unbanMember_completion = completion
unbanMember_completion_result?.invoke(with: completion)
}

override func partialUpdate(
userId: UserId,
in cid: ChannelId,
extraData: [String: RawJSON]?,
unset: [String]?,
completion: @escaping ((Result<ChatChannelMember, Error>) -> Void)
) {
partialUpdate_userId = userId
partialUpdate_cid = cid
partialUpdate_extraData = extraData
partialUpdate_unset = unset
partialUpdate_completion = completion
partialUpdate_completion_result?.invoke(with: completion)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ final class ChannelEndpoints_Tests: XCTestCase {
func test_addMembers_buildsCorrectly() {
let cid = ChannelId.unique
let userIds: Set<UserId> = Set([UserId.unique])
let members = userIds.map { MemberInfoRequest(userId: $0, extraData: nil) }
let members = userIds.map { MemberInfoRequest(userId: $0, extraData: ["is_premium": true]) }

let expectedEndpoint = Endpoint<EmptyResponse>(
path: .channelUpdate(cid.apiPath),
Expand Down
11 changes: 11 additions & 0 deletions Tests/StreamChatTests/APIClient/Endpoints/EndpointPath_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ final class EndpointPathTests: XCTestCase {
XCTAssertFalse(EndpointPath.unread.shouldBeQueuedOffline)
}

func test_partialMemberUpdate_shouldNOTBeQueuedOffline() {
XCTAssertFalse(EndpointPath.partialMemberUpdate(userId: "1", cid: .unique).shouldBeQueuedOffline)
}

func test_partialMemberUpdate_value() {
let cid = ChannelId.unique
let path = EndpointPath.partialMemberUpdate(userId: "1", cid: cid).value
XCTAssertEqual(path, "channels/\(cid.apiPath)/member/1")
}

// MARK: - Codable

func test_isProperlyEncodedAndDecoded() throws {
Expand All @@ -78,6 +88,7 @@ final class EndpointPathTests: XCTestCase {
assertResultEncodingAndDecoding(.users)
assertResultEncodingAndDecoding(.guest)
assertResultEncodingAndDecoding(.members)
assertResultEncodingAndDecoding(.partialMemberUpdate(userId: "1", cid: .init(type: .messaging, id: "2")))
assertResultEncodingAndDecoding(.search)
assertResultEncodingAndDecoding(.devices)
assertResultEncodingAndDecoding(.threads)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,34 @@ final class MemberEndpoints_Tests: XCTestCase {
XCTAssertEqual(AnyEndpoint(expectedEndpoint), AnyEndpoint(endpoint))
XCTAssertEqual("members", endpoint.path.value)
}

func test_partialMemberUpdate_buildsCorrectly() {
let userId: UserId = "test-user"
let cid: ChannelId = .unique
let extraData: [String: RawJSON] = ["is_premium": .bool(true)]
let unset: [String] = ["is_cool"]

let body: [String: AnyEncodable] = [
"set": AnyEncodable(["is_premium": true]),
"unset": AnyEncodable(["is_cool"])
]
let expectedEndpoint = Endpoint<PartialMemberUpdateResponse>(
path: .partialMemberUpdate(userId: userId, cid: cid),
method: .patch,
queryItems: nil,
requiresConnectionId: false,
body: body
)

// Build endpoint.
let endpoint: Endpoint<PartialMemberUpdateResponse> = .partialMemberUpdate(
userId: userId,
cid: cid,
extraData: extraData,
unset: unset
)

// Assert endpoint is built correctly.
XCTAssertEqual(AnyEndpoint(expectedEndpoint), AnyEndpoint(endpoint))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ final class MemberPayload_Tests: XCTestCase {
XCTAssertEqual(payload.isBanned, true)
XCTAssertEqual(payload.isShadowBanned, true)
XCTAssertEqual(payload.notificationsMuted, true)
XCTAssertEqual(payload.extraData?["is_premium"], true)

XCTAssertNotNil(payload.user)
XCTAssertEqual(payload.user!.id, "broken-waterfall-5")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3473,11 +3473,11 @@ final class ChannelController_Tests: XCTestCase {
// Create `ChannelController` for new channel
let query = ChannelQuery(channelPayload: .unique)
setupControllerForNewChannel(query: query)
let members: Set<UserId> = [.unique]
let members: [MemberInfo] = [.init(userId: .unique, extraData: nil)]

// Simulate `addMembers` call and assert error is returned
var error: Error? = try waitFor { [callbackQueueID] completion in
controller.addMembers(userIds: members) { error in
controller.addMembers(members) { error in
AssertTestQueue(withId: callbackQueueID)
completion(error)
}
Expand All @@ -3489,7 +3489,7 @@ final class ChannelController_Tests: XCTestCase {

// Simulate `addMembers` call and assert no error is returned
error = try waitFor { [callbackQueueID] completion in
controller.addMembers(userIds: members) { error in
controller.addMembers(members) { error in
AssertTestQueue(withId: callbackQueueID)
completion(error)
}
Expand All @@ -3500,11 +3500,11 @@ final class ChannelController_Tests: XCTestCase {
}

func test_addMembers_callsChannelUpdater() {
let members: Set<UserId> = [.unique]
let members: [MemberInfo] = [.init(userId: .unique, extraData: ["is_premium": true])]

// Simulate `addMembers` call and catch the completion
var completionCalled = false
controller.addMembers(userIds: members) { [callbackQueueID] error in
controller.addMembers(members) { [callbackQueueID] error in
AssertTestQueue(withId: callbackQueueID)
XCTAssertNil(error)
completionCalled = true
Expand All @@ -3519,7 +3519,7 @@ final class ChannelController_Tests: XCTestCase {

// Assert cid and members state are passed to `channelUpdater`, completion is not called yet
XCTAssertEqual(env.channelUpdater!.addMembers_cid, channelId)
XCTAssertEqual(env.channelUpdater!.addMembers_userIds, members)
XCTAssertEqual(env.channelUpdater!.addMembers_memberInfos?.map(\.userId), members.map(\.userId))
XCTAssertFalse(completionCalled)

// Simulate successful update
Expand All @@ -3534,11 +3534,11 @@ final class ChannelController_Tests: XCTestCase {
}

func test_addMembers_propagatesErrorFromUpdater() {
let members: Set<UserId> = [.unique]
let members: [MemberInfo] = [.init(userId: .unique, extraData: nil)]

// Simulate `addMembers` call and catch the completion
var completionCalledError: Error?
controller.addMembers(userIds: members) { [callbackQueueID] in
controller.addMembers(members) { [callbackQueueID] in
AssertTestQueue(withId: callbackQueueID)
completionCalledError = $0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,67 @@ final class MemberController_Tests: XCTestCase {
XCTAssertEqual(env.memberUpdater!.unbanMember_userId, controller.userId)
XCTAssertEqual(env.memberUpdater!.unbanMember_cid, controller.cid)
}

// MARK: - Partial Update

func test_partialUpdate_propagatesError() {
let expectedError = TestError()

// Simulate `partialUpdate` call and catch the completion
var receivedResult: Result<ChatChannelMember, Error>?
controller.partialUpdate(extraData: ["key": .string("value")], unsetProperties: ["field"]) { [callbackQueueID] result in
AssertTestQueue(withId: callbackQueueID)
receivedResult = result
}

// Simulate network response with error
env.memberUpdater!.partialUpdate_completion?(.failure(expectedError))

// Assert error is propagated
AssertAsync.willBeEqual(receivedResult?.error as? TestError, expectedError)
}

func test_partialUpdate_propagatesSuccess() {
let expectedMember: ChatChannelMember = .mock(id: .unique)

// Simulate `partialUpdate` call and catch the completion
var receivedResult: Result<ChatChannelMember, Error>?
controller.partialUpdate(extraData: ["key": .string("value")], unsetProperties: ["field"]) { [callbackQueueID] result in
AssertTestQueue(withId: callbackQueueID)
receivedResult = result
}

// Keep a weak ref so we can check if it's actually deallocated
weak var weakController = controller

// (Try to) deallocate the controller
// by not keeping any references to it
controller = nil

// Simulate successful network response
env.memberUpdater!.partialUpdate_completion?(.success(expectedMember))
// Release reference of completion so we can deallocate stuff
env.memberUpdater!.partialUpdate_completion = nil

// Assert success is propagated
AssertAsync.willBeEqual(receivedResult?.value?.id, expectedMember.id)
// `weakController` should be deallocated too
AssertAsync.canBeReleased(&weakController)
}

func test_partialUpdate_callsMemberUpdater_withCorrectValues() {
let extraData: [String: RawJSON] = ["key": .string("value")]
let unsetProperties = ["field1", "field2"]

// Simulate `partialUpdate` call
controller.partialUpdate(extraData: extraData, unsetProperties: unsetProperties)

// Assert updater is called with correct values
XCTAssertEqual(env.memberUpdater!.partialUpdate_userId, controller.userId)
XCTAssertEqual(env.memberUpdater!.partialUpdate_cid, controller.cid)
XCTAssertEqual(env.memberUpdater!.partialUpdate_extraData, extraData)
XCTAssertEqual(env.memberUpdater!.partialUpdate_unset, unsetProperties)
}
}

private class TestEnvironment {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ final class MemberModelDTO_Tests: XCTestCase {
banExpiresAt: .unique,
isBanned: true,
isShadowBanned: true,
notificationsMuted: true
notificationsMuted: true,
extraData: ["is_premium": .bool(true)]
)

// Asynchronously save the payload to the db
Expand Down Expand Up @@ -82,6 +83,7 @@ final class MemberModelDTO_Tests: XCTestCase {
Assert.willBeEqual(payload.user!.extraData, loadedMember?.extraData)
Assert.willBeEqual(Set(payload.user!.teams), loadedMember?.teams)
Assert.willBeEqual(payload.user!.language!, loadedMember?.language?.languageCode)
Assert.willBeEqual(true, loadedMember?.memberExtraData["is_premium"]?.boolValue)
}
}

Expand Down
6 changes: 3 additions & 3 deletions Tests/StreamChatTests/StateLayer/Chat_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ final class Chat_Tests: XCTestCase {
func test_addMembers_whenChannelUpdaterSucceeds_thenAddMembersSucceeds() async throws {
for hideHistory in [true, false] {
env.channelUpdaterMock.addMembers_completion_result = .success(())
let memberIds: [UserId] = [.unique, .unique]
try await chat.addMembers(memberIds, systemMessage: "My system message", hideHistory: hideHistory)
let members: [MemberInfo] = [.init(userId: .unique, extraData: nil), .init(userId: .unique, extraData: nil)]
try await chat.addMembers(members, systemMessage: "My system message", hideHistory: hideHistory)
XCTAssertEqual(channelId, env.channelUpdaterMock.addMembers_cid)
XCTAssertEqual(memberIds.sorted(), env.channelUpdaterMock.addMembers_userIds?.sorted())
XCTAssertEqual(members.map(\.userId).sorted(), env.channelUpdaterMock.addMembers_userIds?.sorted())
XCTAssertEqual("My system message", env.channelUpdaterMock.addMembers_message)
XCTAssertEqual(hideHistory, env.channelUpdaterMock.addMembers_hideHistory)
XCTAssertEqual(currentUserId, env.channelUpdaterMock.addMembers_currentUserId)
Expand Down
79 changes: 79 additions & 0 deletions Tests/StreamChatTests/Workers/ChannelMemberUpdater_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,83 @@ final class ChannelMemberUpdater_Tests: XCTestCase {
// Assert the completion is called with the error
AssertAsync.willBeEqual(completionCalledError as? TestError, error)
}

// MARK: - Partial Update

func test_partialUpdate_makesCorrectAPICall() {
let userId: UserId = .unique
let cid: ChannelId = .unique
let extraData: [String: RawJSON] = ["key": .string("value")]
let unset: [String] = ["field1"]

// Simulate `partialUpdate` call
updater.partialUpdate(
userId: userId,
in: cid,
extraData: extraData,
unset: unset,
completion: { _ in }
)

// Assert correct endpoint is called
XCTAssertEqual(
apiClient.request_endpoint,
AnyEndpoint(
.partialMemberUpdate(
userId: userId,
cid: cid,
extraData: extraData,
unset: unset
)
)
)
}

func test_partialUpdate_propagatesSuccessfulResponse() {
let cid: ChannelId = .unique
let memberPayload: MemberPayload = .dummy()

// Simulate `partialUpdate` call
var completionResult: Result<ChatChannelMember, Error>?
updater.partialUpdate(
userId: .unique,
in: cid,
extraData: nil,
unset: nil
) { result in
completionResult = result
}

// Simulate API response with success
let response = PartialMemberUpdateResponse(channelMember: memberPayload)
apiClient.test_simulateResponse(Result<PartialMemberUpdateResponse, Error>.success(response))

// Assert completion is called with the member
AssertAsync {
Assert.willBeTrue(completionResult?.value?.id == memberPayload.userId)
}
}

func test_partialUpdate_propagatesError() {
// Simulate `partialUpdate` call
var completionResult: Result<ChatChannelMember, Error>?
updater.partialUpdate(
userId: .unique,
in: .unique,
extraData: nil,
unset: nil
) { result in
completionResult = result
}

// Simulate API response with failure
let error = TestError()
apiClient.test_simulateResponse(Result<PartialMemberUpdateResponse, Error>.failure(error))

// Assert the completion is called with the error
AssertAsync {
Assert.willBeTrue(completionResult?.isError == true)
Assert.willBeEqual(completionResult?.error as? TestError, error)
}
}
}

0 comments on commit 548b5a1

Please sign in to comment.