Skip to content

Commit 49e5214

Browse files
added mock infrastructure and initial tests
1 parent 22058fd commit 49e5214

File tree

65 files changed

+5531
-37
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+5531
-37
lines changed

StreamChatSwiftUI.xcodeproj/project.pbxproj

Lines changed: 425 additions & 4 deletions
Large diffs are not rendered by default.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
@testable import StreamChat
7+
import XCTest
8+
9+
/// Mock implementation of APIClient allowing easy control and simulation of responses.
10+
class APIClientMock: APIClient {
11+
@Atomic var request_allRecordedCalls: [(endpoint: AnyEndpoint, completion: Any?)] = []
12+
13+
/// The last endpoint `request` function was called with.
14+
@Atomic var request_endpoint: AnyEndpoint?
15+
@Atomic var request_completion: Any?
16+
17+
/// The last endpoint `uploadFile` function was called with.
18+
@Atomic var uploadFile_attachment: AnyChatMessageAttachment?
19+
@Atomic var uploadFile_progress: ((Double) -> Void)?
20+
@Atomic var uploadFile_completion: ((Result<URL, Error>) -> Void)?
21+
22+
/// The last params `flushRequestsQueue` function was called with.
23+
@Atomic var flushRequestsQueue_timeout: TimeInterval?
24+
@Atomic var flushRequestsQueue_itemAction: ((APIClient.RequestsQueueItem) -> Void)?
25+
26+
@Atomic var init_sessionConfiguration: URLSessionConfiguration
27+
@Atomic var init_requestEncoder: RequestEncoder
28+
@Atomic var init_requestDecoder: RequestDecoder
29+
@Atomic var init_CDNClient: CDNClient
30+
@Atomic var request_expectation: XCTestExpectation
31+
32+
// Cleans up all recorded values
33+
func cleanUp() {
34+
request_allRecordedCalls = []
35+
request_endpoint = nil
36+
request_completion = nil
37+
request_expectation = .init()
38+
39+
uploadFile_attachment = nil
40+
uploadFile_progress = nil
41+
uploadFile_completion = nil
42+
43+
flushRequestsQueue_timeout = nil
44+
flushRequestsQueue_itemAction = nil
45+
}
46+
47+
override init(
48+
sessionConfiguration: URLSessionConfiguration,
49+
requestEncoder: RequestEncoder,
50+
requestDecoder: RequestDecoder,
51+
CDNClient: CDNClient,
52+
tokenRefresher: ((ClientError, @escaping () -> Void) -> Void)!
53+
) {
54+
init_sessionConfiguration = sessionConfiguration
55+
init_requestEncoder = requestEncoder
56+
init_requestDecoder = requestDecoder
57+
init_CDNClient = CDNClient
58+
request_expectation = .init()
59+
60+
super.init(
61+
sessionConfiguration: sessionConfiguration,
62+
requestEncoder: requestEncoder,
63+
requestDecoder: requestDecoder,
64+
CDNClient: CDNClient,
65+
tokenRefresher: tokenRefresher
66+
)
67+
}
68+
69+
/// Simulates the response of the last `request` method call
70+
func test_simulateResponse<Response: Decodable>(_ response: Result<Response, Error>) {
71+
let completion = request_completion as! ((Result<Response, Error>) -> Void)
72+
completion(response)
73+
}
74+
75+
override func request<Response>(
76+
endpoint: Endpoint<Response>,
77+
timeout: TimeInterval,
78+
completion: @escaping (Result<Response, Error>) -> Void
79+
) where Response: Decodable {
80+
request_endpoint = AnyEndpoint(endpoint)
81+
request_completion = completion
82+
_request_allRecordedCalls.mutate { $0.append((request_endpoint!, request_completion!)) }
83+
request_expectation.fulfill()
84+
}
85+
86+
override func uploadAttachment(
87+
_ attachment: AnyChatMessageAttachment,
88+
progress: ((Double) -> Void)?,
89+
completion: @escaping (Result<URL, Error>) -> Void
90+
) {
91+
uploadFile_attachment = attachment
92+
uploadFile_progress = progress
93+
uploadFile_completion = completion
94+
}
95+
96+
override func flushRequestsQueue(
97+
after timeout: TimeInterval = 0,
98+
itemAction: ((APIClient.RequestsQueueItem) -> Void)? = nil
99+
) {
100+
flushRequestsQueue_timeout = timeout
101+
flushRequestsQueue_itemAction = itemAction
102+
}
103+
104+
@discardableResult
105+
func waitForRequest(timeout: Double = 0.5) -> AnyEndpoint? {
106+
XCTWaiter().wait(for: [request_expectation], timeout: timeout)
107+
return request_endpoint
108+
}
109+
}
110+
111+
extension APIClientMock {
112+
convenience init() {
113+
self.init(
114+
sessionConfiguration: .ephemeral,
115+
requestEncoder: DefaultRequestEncoder(baseURL: .unique(), apiKey: .init(.unique)),
116+
requestDecoder: DefaultRequestDecoder(),
117+
CDNClient: CDNClient_Mock(),
118+
tokenRefresher: { _, _ in }
119+
)
120+
}
121+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
@testable import StreamChat
7+
8+
struct AnyEndpoint: Equatable {
9+
let path: String
10+
let method: EndpointMethod
11+
let queryItems: AnyEncodable?
12+
let requiresConnectionId: Bool
13+
let body: AnyEncodable?
14+
let payloadType: Decodable.Type
15+
16+
init<T: Decodable>(_ endpoint: Endpoint<T>) {
17+
path = endpoint.path
18+
method = endpoint.method
19+
queryItems = endpoint.queryItems?.asAnyEncodable
20+
requiresConnectionId = endpoint.requiresConnectionId
21+
body = endpoint.body?.asAnyEncodable
22+
payloadType = T.self
23+
}
24+
25+
static func == (lhs: AnyEndpoint, rhs: AnyEndpoint) -> Bool {
26+
lhs.path == rhs.path
27+
&& lhs.method == rhs.method
28+
&& lhs.queryItems == rhs.queryItems
29+
&& lhs.requiresConnectionId == rhs.requiresConnectionId
30+
&& lhs.body == rhs.body
31+
&& lhs.payloadType == rhs.payloadType
32+
}
33+
}
34+
35+
// MARK: - Helper AnyEncodable
36+
37+
extension AnyEncodable: Equatable {
38+
public static func == (lhs: AnyEncodable, rhs: AnyEncodable) -> Bool {
39+
do {
40+
let encoder = JSONEncoder.default
41+
let encodedLhs = try encoder.encode(lhs)
42+
let encodedRhs = try encoder.encode(rhs)
43+
try CompareJSONEqual(encodedLhs, encodedRhs)
44+
return true
45+
} catch {
46+
return String(describing: lhs) == String(describing: rhs)
47+
}
48+
}
49+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
@testable import StreamChat
7+
8+
final class CDNClient_Mock: CDNClient {
9+
static var maxAttachmentSize: Int64 { .max }
10+
11+
lazy var uploadAttachmentMockFunc = MockFunc.mock(for: uploadAttachment)
12+
func uploadAttachment(
13+
_ attachment: AnyChatMessageAttachment,
14+
progress: ((Double) -> Void)?,
15+
completion: @escaping (Result<URL, Error>) -> Void
16+
) {
17+
uploadAttachmentMockFunc.callAndReturn(
18+
(
19+
attachment,
20+
progress,
21+
completion
22+
)
23+
)
24+
}
25+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
@testable import StreamChat
7+
8+
class InternetConnectionMock: InternetConnection {
9+
private(set) var monitorMock: InternetConnectionMonitorMock!
10+
private(set) var init_notificationCenter: NotificationCenter!
11+
12+
init(
13+
monitor: InternetConnectionMonitorMock = .init(),
14+
notificationCenter: NotificationCenter = .default
15+
) {
16+
super.init(notificationCenter: notificationCenter, monitor: monitor)
17+
init_notificationCenter = notificationCenter
18+
monitorMock = monitor
19+
}
20+
}
21+
22+
class InternetConnectionMonitorMock: InternetConnectionMonitor {
23+
weak var delegate: InternetConnectionDelegate?
24+
25+
var status: InternetConnection.Status = .unknown {
26+
didSet {
27+
delegate?.internetConnectionStatusDidChange(status: status)
28+
}
29+
}
30+
31+
var isStarted = false
32+
33+
func start() {
34+
isStarted = true
35+
status = .available(.great)
36+
}
37+
38+
func stop() {
39+
isStarted = false
40+
status = .unknown
41+
}
42+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// Copyright © 2021 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
@testable import StreamChat
7+
8+
class MockBackgroundTaskScheduler: BackgroundTaskScheduler {
9+
var beginBackgroundTask_called: Bool = false
10+
var beginBackgroundTask_expirationHandler: (() -> Void)?
11+
var beginBackgroundTask_returns: Bool = true
12+
func beginTask(expirationHandler: (() -> Void)?) -> Bool {
13+
beginBackgroundTask_called = true
14+
beginBackgroundTask_expirationHandler = expirationHandler
15+
return beginBackgroundTask_returns
16+
}
17+
18+
var endBackgroundTask_called: Bool = false
19+
func endTask() {
20+
endBackgroundTask_called = true
21+
}
22+
23+
var startListeningForAppStateUpdates_called: Bool = false
24+
var startListeningForAppStateUpdates_onBackground: (() -> Void)?
25+
var startListeningForAppStateUpdates_onForeground: (() -> Void)?
26+
func startListeningForAppStateUpdates(
27+
onEnteringBackground: @escaping () -> Void,
28+
onEnteringForeground: @escaping () -> Void
29+
) {
30+
startListeningForAppStateUpdates_called = true
31+
startListeningForAppStateUpdates_onBackground = onEnteringBackground
32+
startListeningForAppStateUpdates_onForeground = onEnteringForeground
33+
}
34+
35+
var stopListeningForAppStateUpdates_called: Bool = false
36+
func stopListeningForAppStateUpdates() {
37+
stopListeningForAppStateUpdates_called = true
38+
}
39+
}

0 commit comments

Comments
 (0)