Skip to content

Commit 52efeee

Browse files
committed
Add test coverage to ManualEventHandler
1 parent b83ac17 commit 52efeee

File tree

3 files changed

+302
-0
lines changed

3 files changed

+302
-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: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
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 = try! database.writableContext.channel(cid: cid)!.asModel()
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() {
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+
let result = handler.handle(eventDTO)
71+
72+
XCTAssertNil(result, "Events without cid should return nil")
73+
}
74+
75+
// MARK: - Event Handling - Unregistered Channel
76+
77+
func test_handle_unregisteredChannel_returnsNil() {
78+
let unregisteredCid: ChannelId = .unique
79+
let eventPayload = EventPayload(
80+
eventType: .messageNew,
81+
cid: unregisteredCid,
82+
user: .dummy(userId: .unique),
83+
message: .dummy(messageId: .unique, authorUserId: .unique),
84+
createdAt: .unique
85+
)
86+
let eventDTO = try! MessageNewEventDTO(from: eventPayload)
87+
88+
let result = handler.handle(eventDTO)
89+
XCTAssertNil(result)
90+
}
91+
92+
// MARK: - Event Handling - Unsupported Event Type
93+
94+
func test_handle_unsupportedEventType_returnsNil() {
95+
// Use a typing event which is not handled by ManualEventHandler
96+
let eventPayload = EventPayload(
97+
eventType: .userStartTyping,
98+
cid: cid,
99+
user: .dummy(userId: .unique),
100+
createdAt: .unique
101+
)
102+
let eventDTO = try! TypingEventDTO(from: eventPayload)
103+
104+
let result = handler.handle(eventDTO)
105+
XCTAssertNil(result, "Unsupported event types should return nil")
106+
}
107+
108+
// MARK: - Message New Event
109+
110+
func test_handle_messageNewEvent_withValidData_returnsEvent() throws {
111+
let userId: UserId = .unique
112+
let messageId: MessageId = .unique
113+
let createdAt = Date.unique
114+
115+
let eventPayload = EventPayload(
116+
eventType: .messageNew,
117+
cid: cid,
118+
user: .dummy(userId: userId),
119+
message: .dummy(messageId: messageId, authorUserId: userId),
120+
watcherCount: 10,
121+
unreadCount: .init(channels: 1, messages: 2, threads: 0),
122+
createdAt: createdAt
123+
)
124+
let eventDTO = try! MessageNewEventDTO(from: eventPayload)
125+
126+
let result = handler.handle(eventDTO)
127+
128+
let messageNewEvent = try XCTUnwrap(result as? MessageNewEvent)
129+
XCTAssertEqual(messageNewEvent.user.id, userId)
130+
XCTAssertEqual(messageNewEvent.message.id, messageId)
131+
XCTAssertEqual(messageNewEvent.cid, cid)
132+
XCTAssertEqual(messageNewEvent.watcherCount, 10)
133+
XCTAssertEqual(messageNewEvent.unreadCount?.messages, 2)
134+
XCTAssertEqual(messageNewEvent.createdAt, createdAt)
135+
}
136+
137+
// MARK: - Message Updated Event
138+
139+
func test_handle_messageUpdatedEvent_withValidData_returnsEvent() throws {
140+
let userId: UserId = .unique
141+
let messageId: MessageId = .unique
142+
let createdAt = Date.unique
143+
144+
let eventPayload = EventPayload(
145+
eventType: .messageUpdated,
146+
cid: cid,
147+
user: .dummy(userId: userId),
148+
message: .dummy(messageId: messageId, authorUserId: userId),
149+
createdAt: createdAt
150+
)
151+
let eventDTO = try! MessageUpdatedEventDTO(from: eventPayload)
152+
153+
let result = handler.handle(eventDTO)
154+
155+
let messageUpdatedEvent = try XCTUnwrap(result as? MessageUpdatedEvent)
156+
XCTAssertEqual(messageUpdatedEvent.user.id, userId)
157+
XCTAssertEqual(messageUpdatedEvent.message.id, messageId)
158+
XCTAssertEqual(messageUpdatedEvent.cid, cid)
159+
XCTAssertEqual(messageUpdatedEvent.createdAt, createdAt)
160+
}
161+
162+
// MARK: - Message Deleted Event
163+
164+
func test_handle_messageDeletedEvent_withValidData_returnsEvent() throws {
165+
let userId: UserId = .unique
166+
let messageId: MessageId = .unique
167+
let createdAt = Date.unique
168+
169+
let eventPayload = EventPayload(
170+
eventType: .messageDeleted,
171+
cid: cid,
172+
user: .dummy(userId: userId),
173+
message: .dummy(messageId: messageId, authorUserId: userId),
174+
createdAt: createdAt,
175+
hardDelete: true
176+
)
177+
let eventDTO = try! MessageDeletedEventDTO(from: eventPayload)
178+
179+
let result = handler.handle(eventDTO)
180+
181+
let messageDeletedEvent = try XCTUnwrap(result as? MessageDeletedEvent)
182+
XCTAssertEqual(messageDeletedEvent.user?.id, userId)
183+
XCTAssertEqual(messageDeletedEvent.message.id, messageId)
184+
XCTAssertEqual(messageDeletedEvent.cid, cid)
185+
XCTAssertEqual(messageDeletedEvent.isHardDelete, true)
186+
XCTAssertEqual(messageDeletedEvent.createdAt, createdAt)
187+
}
188+
189+
func test_handle_messageDeletedEvent_withoutUser_returnsEvent() throws {
190+
let messageId: MessageId = .unique
191+
let createdAt = Date.unique
192+
193+
let eventPayload = EventPayload(
194+
eventType: .messageDeleted,
195+
cid: cid,
196+
user: nil,
197+
message: .dummy(messageId: messageId, authorUserId: .unique),
198+
createdAt: createdAt,
199+
hardDelete: false
200+
)
201+
let eventDTO = try! MessageDeletedEventDTO(from: eventPayload)
202+
203+
let result = handler.handle(eventDTO)
204+
205+
let messageDeletedEvent = try XCTUnwrap(result as? MessageDeletedEvent)
206+
XCTAssertNil(messageDeletedEvent.user)
207+
XCTAssertEqual(messageDeletedEvent.message.id, messageId)
208+
XCTAssertEqual(messageDeletedEvent.cid, cid)
209+
XCTAssertEqual(messageDeletedEvent.isHardDelete, false)
210+
XCTAssertEqual(messageDeletedEvent.createdAt, createdAt)
211+
}
212+
213+
// MARK: - Reaction New Event
214+
215+
func test_handle_reactionNewEvent_withValidData_returnsEvent() throws {
216+
let userId: UserId = .unique
217+
let messageId: MessageId = .unique
218+
let reactionType: MessageReactionType = "like"
219+
let createdAt = Date.unique
220+
221+
let eventPayload = EventPayload(
222+
eventType: .reactionNew,
223+
cid: cid,
224+
user: .dummy(userId: userId),
225+
message: .dummy(messageId: messageId, authorUserId: userId),
226+
reaction: .dummy(type: reactionType, messageId: messageId, user: .dummy(userId: userId)),
227+
createdAt: createdAt
228+
)
229+
let eventDTO = try! ReactionNewEventDTO(from: eventPayload)
230+
231+
let result = handler.handle(eventDTO)
232+
233+
let reactionNewEvent = try XCTUnwrap(result as? ReactionNewEvent)
234+
XCTAssertEqual(reactionNewEvent.user.id, userId)
235+
XCTAssertEqual(reactionNewEvent.message.id, messageId)
236+
XCTAssertEqual(reactionNewEvent.cid, cid)
237+
XCTAssertEqual(reactionNewEvent.reaction.type, reactionType)
238+
XCTAssertEqual(reactionNewEvent.createdAt, createdAt)
239+
}
240+
241+
// MARK: - Reaction Updated Event
242+
243+
func test_handle_reactionUpdatedEvent_withValidData_returnsEvent() throws {
244+
let userId: UserId = .unique
245+
let messageId: MessageId = .unique
246+
let reactionType: MessageReactionType = "love"
247+
let createdAt = Date.unique
248+
249+
let eventPayload = EventPayload(
250+
eventType: .reactionUpdated,
251+
cid: cid,
252+
user: .dummy(userId: userId),
253+
message: .dummy(messageId: messageId, authorUserId: userId),
254+
reaction: .dummy(type: reactionType, messageId: messageId, user: .dummy(userId: userId)),
255+
createdAt: createdAt
256+
)
257+
let eventDTO = try! ReactionUpdatedEventDTO(from: eventPayload)
258+
259+
let result = handler.handle(eventDTO)
260+
261+
let reactionUpdatedEvent = try XCTUnwrap(result as? ReactionUpdatedEvent)
262+
XCTAssertEqual(reactionUpdatedEvent.user.id, userId)
263+
XCTAssertEqual(reactionUpdatedEvent.message.id, messageId)
264+
XCTAssertEqual(reactionUpdatedEvent.cid, cid)
265+
XCTAssertEqual(reactionUpdatedEvent.reaction.type, reactionType)
266+
XCTAssertEqual(reactionUpdatedEvent.createdAt, createdAt)
267+
}
268+
269+
// MARK: - Reaction Deleted Event
270+
271+
func test_handle_reactionDeletedEvent_withValidData_returnsEvent() throws {
272+
let userId: UserId = .unique
273+
let messageId: MessageId = .unique
274+
let reactionType: MessageReactionType = "angry"
275+
let createdAt = Date.unique
276+
277+
let eventPayload = EventPayload(
278+
eventType: .reactionDeleted,
279+
cid: cid,
280+
user: .dummy(userId: userId),
281+
message: .dummy(messageId: messageId, authorUserId: userId),
282+
reaction: .dummy(type: reactionType, messageId: messageId, user: .dummy(userId: userId)),
283+
createdAt: createdAt
284+
)
285+
let eventDTO = try! ReactionDeletedEventDTO(from: eventPayload)
286+
287+
let result = handler.handle(eventDTO)
288+
289+
let reactionDeletedEvent = try XCTUnwrap(result as? ReactionDeletedEvent)
290+
XCTAssertEqual(reactionDeletedEvent.user.id, userId)
291+
XCTAssertEqual(reactionDeletedEvent.message.id, messageId)
292+
XCTAssertEqual(reactionDeletedEvent.cid, cid)
293+
XCTAssertEqual(reactionDeletedEvent.reaction.type, reactionType)
294+
XCTAssertEqual(reactionDeletedEvent.createdAt, createdAt)
295+
}
296+
}

0 commit comments

Comments
 (0)