Skip to content

Commit 5153c46

Browse files
committed
Add test coverage to ManualEventHandler
1 parent b83ac17 commit 5153c46

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

Sources/StreamChat/Workers/ManualEventHandler.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ class ManualEventHandler {
2121

2222
init(
2323
database: DatabaseContainer,
24+
cachedChannels: [ChannelId: ChatChannel] = [:],
2425
queue: DispatchQueue = DispatchQueue(label: "io.getstream.chat.manualEventHandler", qos: .utility)
2526
) {
2627
self.database = database
28+
self.cachedChannels = cachedChannels
2729
self.queue = queue
2830
}
2931

StreamChat.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,7 @@
16161616
AD7C76712E3CE1E0009250FB /* DemoLivestreamChatMessageListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C766D2E3CE1E0009250FB /* DemoLivestreamChatMessageListVC.swift */; };
16171617
AD7C76722E3CE1E0009250FB /* DemoLivestreamMessageActionsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C766F2E3CE1E0009250FB /* DemoLivestreamMessageActionsVC.swift */; };
16181618
AD7C76752E3D0486009250FB /* DemoLivestreamReactionsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C76742E3D047E009250FB /* DemoLivestreamReactionsListView.swift */; };
1619+
AD7C767F2E426B34009250FB /* ManualEventHandler_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C767E2E426B34009250FB /* ManualEventHandler_Tests.swift */; };
16191620
AD7C76812E4275B3009250FB /* LivestreamChannelController_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C76802E4275B3009250FB /* LivestreamChannelController_Tests.swift */; };
16201621
AD7C76832E42C0B5009250FB /* LivestreamChannelController+Combine_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C76822E42C0B5009250FB /* LivestreamChannelController+Combine_Tests.swift */; };
16211622
AD7C76852E42CDF6009250FB /* ManualEventHandler_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7C76842E42CDF6009250FB /* ManualEventHandler_Mock.swift */; };
@@ -4418,6 +4419,7 @@
44184419
AD7C766D2E3CE1E0009250FB /* DemoLivestreamChatMessageListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoLivestreamChatMessageListVC.swift; sourceTree = "<group>"; };
44194420
AD7C766F2E3CE1E0009250FB /* DemoLivestreamMessageActionsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoLivestreamMessageActionsVC.swift; sourceTree = "<group>"; };
44204421
AD7C76742E3D047E009250FB /* DemoLivestreamReactionsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoLivestreamReactionsListView.swift; sourceTree = "<group>"; };
4422+
AD7C767E2E426B34009250FB /* ManualEventHandler_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualEventHandler_Tests.swift; sourceTree = "<group>"; };
44214423
AD7C76802E4275B3009250FB /* LivestreamChannelController_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LivestreamChannelController_Tests.swift; sourceTree = "<group>"; };
44224424
AD7C76822E42C0B5009250FB /* LivestreamChannelController+Combine_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LivestreamChannelController+Combine_Tests.swift"; sourceTree = "<group>"; };
44234425
AD7C76842E42CDF6009250FB /* ManualEventHandler_Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualEventHandler_Mock.swift; sourceTree = "<group>"; };
@@ -7300,6 +7302,7 @@
73007302
A364D09627D0C56C0029857A /* Workers */ = {
73017303
isa = PBXGroup;
73027304
children = (
7305+
AD7C767E2E426B34009250FB /* ManualEventHandler_Tests.swift */,
73037306
AD9490582BF5701D00E69224 /* ThreadsRepository_Tests.swift */,
73047307
792921C424C0479700116BBB /* ChannelListUpdater_Tests.swift */,
73057308
882C5765252C7F7000E60C44 /* ChannelMemberListUpdater_Tests.swift */,
@@ -12037,6 +12040,7 @@
1203712040
A30C3F22276B4F8800DA5968 /* UnknownUserEvent_Tests.swift in Sources */,
1203812041
DA84074025260CA3005A0F62 /* UserListController_Tests.swift in Sources */,
1203912042
AD545E852D5D7591008FD399 /* DraftListQuery_Tests.swift in Sources */,
12043+
AD7C767F2E426B34009250FB /* ManualEventHandler_Tests.swift in Sources */,
1204012044
C1EE53A727BA53F300B1A6CA /* Endpoint_Tests.swift in Sources */,
1204112045
84A1D2F426AB221E00014712 /* ChannelEventsController_Tests.swift in Sources */,
1204212046
88381E6E258259310047A6A3 /* FileUploadPayload_Tests.swift in Sources */,
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import CoreData
6+
import Foundation
7+
@testable import StreamChat
8+
@testable import StreamChatTestTools
9+
import XCTest
10+
11+
final class ManualEventHandler_Tests: XCTestCase {
12+
var database: DatabaseContainer_Spy!
13+
var handler: ManualEventHandler!
14+
var cid: ChannelId!
15+
var cachedChannel: ChatChannel!
16+
17+
override func setUp() {
18+
super.setUp()
19+
20+
database = DatabaseContainer_Spy()
21+
cid = .unique
22+
23+
// Setup database with channel and current user
24+
try! database.createChannel(cid: cid, withMessages: false)
25+
try! database.createCurrentUser()
26+
27+
// Get the channel from database to use as cached channel
28+
cachedChannel = .mock(cid: cid)
29+
30+
// Create handler with pre-cached channel to avoid registration requirements
31+
handler = ManualEventHandler(
32+
database: database,
33+
cachedChannels: [cid: cachedChannel]
34+
)
35+
36+
// Register the channel so events are processed
37+
handler.register(channelId: cid)
38+
}
39+
40+
override func tearDown() {
41+
handler = nil
42+
database = nil
43+
cachedChannel = nil
44+
cid = nil
45+
super.tearDown()
46+
}
47+
48+
// MARK: - Event Handling - Non-EventDTO
49+
50+
func test_handle_nonEventDTO_returnsNil() {
51+
struct NonEventDTO: Event {}
52+
let event = NonEventDTO()
53+
54+
let result = handler.handle(event)
55+
XCTAssertNil(result)
56+
}
57+
58+
// MARK: - Event Handling - Missing CID
59+
60+
func test_handle_eventWithoutCid_returnsNil() throws {
61+
// Create a simple event DTO that has no cid
62+
struct TestEventDTO: EventDTO {
63+
let payload: EventPayload = EventPayload(
64+
eventType: .healthCheck,
65+
connectionId: .unique
66+
)
67+
}
68+
69+
let eventDTO = TestEventDTO()
70+
var result: Event!
71+
try database.writeSynchronously { _ in
72+
result = self.handler.handle(eventDTO)
73+
}
74+
75+
XCTAssertNil(result, "Events without cid should return nil")
76+
}
77+
78+
// MARK: - Event Handling - Unregistered Channel
79+
80+
func test_handle_unregisteredChannel_returnsNil() throws {
81+
let unregisteredCid: ChannelId = .unique
82+
let eventPayload = EventPayload(
83+
eventType: .messageNew,
84+
cid: unregisteredCid,
85+
user: .dummy(userId: .unique),
86+
message: .dummy(messageId: .unique, authorUserId: .unique),
87+
createdAt: .unique
88+
)
89+
let eventDTO = try! MessageNewEventDTO(from: eventPayload)
90+
91+
var result: Event!
92+
try database.writeSynchronously { _ in
93+
result = self.handler.handle(eventDTO)
94+
}
95+
XCTAssertNil(result)
96+
}
97+
98+
// MARK: - Event Handling - Unsupported Event Type
99+
100+
func test_handle_unsupportedEventType_returnsNil() throws {
101+
// Use a typing event which is not handled by ManualEventHandler
102+
let eventPayload = EventPayload(
103+
eventType: .userStartTyping,
104+
cid: cid,
105+
user: .dummy(userId: .unique),
106+
createdAt: .unique
107+
)
108+
let eventDTO = try! TypingEventDTO(from: eventPayload)
109+
110+
var result: Event!
111+
try database.writeSynchronously { _ in
112+
result = self.handler.handle(eventDTO)
113+
}
114+
XCTAssertNil(result, "Unsupported event types should return nil")
115+
}
116+
117+
// MARK: - Message New Event
118+
119+
func test_handle_messageNewEvent_withValidData_returnsEvent() throws {
120+
let userId: UserId = .unique
121+
let messageId: MessageId = .unique
122+
let createdAt = Date.unique
123+
124+
let eventPayload = EventPayload(
125+
eventType: .messageNew,
126+
cid: cid,
127+
user: .dummy(userId: userId),
128+
message: .dummy(messageId: messageId, authorUserId: userId),
129+
watcherCount: 10,
130+
unreadCount: .init(channels: 1, messages: 2, threads: 0),
131+
createdAt: createdAt
132+
)
133+
let eventDTO = try! MessageNewEventDTO(from: eventPayload)
134+
135+
var result: Event!
136+
try database.writeSynchronously { _ in
137+
result = self.handler.handle(eventDTO)
138+
}
139+
140+
let messageNewEvent = try XCTUnwrap(result as? MessageNewEvent)
141+
XCTAssertEqual(messageNewEvent.user.id, userId)
142+
XCTAssertEqual(messageNewEvent.message.id, messageId)
143+
XCTAssertEqual(messageNewEvent.cid, cid)
144+
XCTAssertEqual(messageNewEvent.watcherCount, 10)
145+
XCTAssertEqual(messageNewEvent.unreadCount?.messages, 2)
146+
XCTAssertEqual(messageNewEvent.createdAt, createdAt)
147+
}
148+
149+
// MARK: - Message Updated Event
150+
151+
func test_handle_messageUpdatedEvent_withValidData_returnsEvent() throws {
152+
let userId: UserId = .unique
153+
let messageId: MessageId = .unique
154+
let createdAt = Date.unique
155+
156+
let eventPayload = EventPayload(
157+
eventType: .messageUpdated,
158+
cid: cid,
159+
user: .dummy(userId: userId),
160+
message: .dummy(messageId: messageId, authorUserId: userId),
161+
createdAt: createdAt
162+
)
163+
let eventDTO = try! MessageUpdatedEventDTO(from: eventPayload)
164+
165+
var result: Event!
166+
try database.writeSynchronously { _ in
167+
result = self.handler.handle(eventDTO)
168+
}
169+
170+
let messageUpdatedEvent = try XCTUnwrap(result as? MessageUpdatedEvent)
171+
XCTAssertEqual(messageUpdatedEvent.user.id, userId)
172+
XCTAssertEqual(messageUpdatedEvent.message.id, messageId)
173+
XCTAssertEqual(messageUpdatedEvent.cid, cid)
174+
XCTAssertEqual(messageUpdatedEvent.createdAt, createdAt)
175+
}
176+
177+
// MARK: - Message Deleted Event
178+
179+
func test_handle_messageDeletedEvent_withValidData_returnsEvent() throws {
180+
let userId: UserId = .unique
181+
let messageId: MessageId = .unique
182+
let createdAt = Date.unique
183+
184+
let eventPayload = EventPayload(
185+
eventType: .messageDeleted,
186+
cid: cid,
187+
user: .dummy(userId: userId),
188+
message: .dummy(messageId: messageId, authorUserId: userId),
189+
createdAt: createdAt,
190+
hardDelete: true
191+
)
192+
let eventDTO = try! MessageDeletedEventDTO(from: eventPayload)
193+
194+
var result: Event!
195+
try database.writeSynchronously { _ in
196+
result = self.handler.handle(eventDTO)
197+
}
198+
199+
let messageDeletedEvent = try XCTUnwrap(result as? MessageDeletedEvent)
200+
XCTAssertEqual(messageDeletedEvent.user?.id, userId)
201+
XCTAssertEqual(messageDeletedEvent.message.id, messageId)
202+
XCTAssertEqual(messageDeletedEvent.cid, cid)
203+
XCTAssertEqual(messageDeletedEvent.isHardDelete, true)
204+
XCTAssertEqual(messageDeletedEvent.createdAt, createdAt)
205+
}
206+
207+
func test_handle_messageDeletedEvent_withoutUser_returnsEvent() throws {
208+
let messageId: MessageId = .unique
209+
let createdAt = Date.unique
210+
211+
let eventPayload = EventPayload(
212+
eventType: .messageDeleted,
213+
cid: cid,
214+
user: nil,
215+
message: .dummy(messageId: messageId, authorUserId: .unique),
216+
createdAt: createdAt,
217+
hardDelete: false
218+
)
219+
let eventDTO = try! MessageDeletedEventDTO(from: eventPayload)
220+
221+
var result: Event!
222+
try database.writeSynchronously { _ in
223+
result = self.handler.handle(eventDTO)
224+
}
225+
226+
let messageDeletedEvent = try XCTUnwrap(result as? MessageDeletedEvent)
227+
XCTAssertNil(messageDeletedEvent.user)
228+
XCTAssertEqual(messageDeletedEvent.message.id, messageId)
229+
XCTAssertEqual(messageDeletedEvent.cid, cid)
230+
XCTAssertEqual(messageDeletedEvent.isHardDelete, false)
231+
XCTAssertEqual(messageDeletedEvent.createdAt, createdAt)
232+
}
233+
234+
// MARK: - Reaction New Event
235+
236+
func test_handle_reactionNewEvent_withValidData_returnsEvent() throws {
237+
let userId: UserId = .unique
238+
let messageId: MessageId = .unique
239+
let reactionType: MessageReactionType = "like"
240+
let createdAt = Date.unique
241+
242+
let eventPayload = EventPayload(
243+
eventType: .reactionNew,
244+
cid: cid,
245+
user: .dummy(userId: userId),
246+
message: .dummy(messageId: messageId, authorUserId: userId),
247+
reaction: .dummy(type: reactionType, messageId: messageId, user: .dummy(userId: userId)),
248+
createdAt: createdAt
249+
)
250+
let eventDTO = try! ReactionNewEventDTO(from: eventPayload)
251+
252+
var result: Event!
253+
try database.writeSynchronously { _ in
254+
result = self.handler.handle(eventDTO)
255+
}
256+
257+
let reactionNewEvent = try XCTUnwrap(result as? ReactionNewEvent)
258+
XCTAssertEqual(reactionNewEvent.user.id, userId)
259+
XCTAssertEqual(reactionNewEvent.message.id, messageId)
260+
XCTAssertEqual(reactionNewEvent.cid, cid)
261+
XCTAssertEqual(reactionNewEvent.reaction.type, reactionType)
262+
XCTAssertEqual(reactionNewEvent.createdAt, createdAt)
263+
}
264+
265+
// MARK: - Reaction Updated Event
266+
267+
func test_handle_reactionUpdatedEvent_withValidData_returnsEvent() throws {
268+
let userId: UserId = .unique
269+
let messageId: MessageId = .unique
270+
let reactionType: MessageReactionType = "love"
271+
let createdAt = Date.unique
272+
273+
let eventPayload = EventPayload(
274+
eventType: .reactionUpdated,
275+
cid: cid,
276+
user: .dummy(userId: userId),
277+
message: .dummy(messageId: messageId, authorUserId: userId),
278+
reaction: .dummy(type: reactionType, messageId: messageId, user: .dummy(userId: userId)),
279+
createdAt: createdAt
280+
)
281+
let eventDTO = try! ReactionUpdatedEventDTO(from: eventPayload)
282+
283+
var result: Event!
284+
try database.writeSynchronously { _ in
285+
result = self.handler.handle(eventDTO)
286+
}
287+
288+
let reactionUpdatedEvent = try XCTUnwrap(result as? ReactionUpdatedEvent)
289+
XCTAssertEqual(reactionUpdatedEvent.user.id, userId)
290+
XCTAssertEqual(reactionUpdatedEvent.message.id, messageId)
291+
XCTAssertEqual(reactionUpdatedEvent.cid, cid)
292+
XCTAssertEqual(reactionUpdatedEvent.reaction.type, reactionType)
293+
XCTAssertEqual(reactionUpdatedEvent.createdAt, createdAt)
294+
}
295+
296+
// MARK: - Reaction Deleted Event
297+
298+
func test_handle_reactionDeletedEvent_withValidData_returnsEvent() throws {
299+
let userId: UserId = .unique
300+
let messageId: MessageId = .unique
301+
let reactionType: MessageReactionType = "angry"
302+
let createdAt = Date.unique
303+
304+
let eventPayload = EventPayload(
305+
eventType: .reactionDeleted,
306+
cid: cid,
307+
user: .dummy(userId: userId),
308+
message: .dummy(messageId: messageId, authorUserId: userId),
309+
reaction: .dummy(type: reactionType, messageId: messageId, user: .dummy(userId: userId)),
310+
createdAt: createdAt
311+
)
312+
let eventDTO = try! ReactionDeletedEventDTO(from: eventPayload)
313+
314+
var result: Event!
315+
try database.writeSynchronously { _ in
316+
result = self.handler.handle(eventDTO)
317+
}
318+
319+
let reactionDeletedEvent = try XCTUnwrap(result as? ReactionDeletedEvent)
320+
XCTAssertEqual(reactionDeletedEvent.user.id, userId)
321+
XCTAssertEqual(reactionDeletedEvent.message.id, messageId)
322+
XCTAssertEqual(reactionDeletedEvent.cid, cid)
323+
XCTAssertEqual(reactionDeletedEvent.reaction.type, reactionType)
324+
XCTAssertEqual(reactionDeletedEvent.createdAt, createdAt)
325+
}
326+
}

0 commit comments

Comments
 (0)