diff --git a/Utils/Tests/UtilsTests/EventBus/EventBusSubscribingTests.swift b/Utils/Tests/UtilsTests/EventBus/EventBusSubscribingTests.swift new file mode 100644 index 00000000..c277305b --- /dev/null +++ b/Utils/Tests/UtilsTests/EventBus/EventBusSubscribingTests.swift @@ -0,0 +1,252 @@ +// generated by polka.codes +import Foundation +import Synchronization +import Testing + +@testable import Utils + +// TestEvent and AnotherTestEvent are defined in EventBusTests + +// A helper actor to safely check if a handler was called and capture values +private actor TestHandlerChecker { + private(set) var callCount = 0 + private(set) var capturedValues: [T] = [] + + func handlerCalled(with value: T) { + callCount += 1 + capturedValues.append(value) + } +} + +struct EventBusSubscribingTests { + @Test func testBasicSubscription() async throws { + let eventBus = EventBus() + let handlerChecker = TestHandlerChecker() + + let testEvent = TestEvent(id: 1, value: "test") + + let token = await eventBus.subscribe(TestEvent.self) { event in + await handlerChecker.handlerCalled(with: event) + } + + await eventBus.publish(testEvent) + + // Give some time for the event to be processed + try await Task.sleep(for: .milliseconds(100)) + + let callCount = await handlerChecker.callCount + #expect(callCount == 1) + let capturedEvent = await handlerChecker.capturedValues.first + #expect(capturedEvent?.id == testEvent.id) + #expect(capturedEvent?.value == testEvent.value) + + await eventBus.unsubscribe(token: token) + } + + @Test func testUnsubscribe() async throws { + let eventBus = EventBus() + let handlerChecker = TestHandlerChecker() + + let token = await eventBus.subscribe(TestEvent.self) { event in + await handlerChecker.handlerCalled(with: event) + } + + await eventBus.unsubscribe(token: token) + + await eventBus.publish(TestEvent(id: 2, value: "after unsubscribe")) + + // Give some time for the event to be processed + try await Task.sleep(for: .milliseconds(100)) + + let callCount = await handlerChecker.callCount + #expect(callCount == 0) + } + + @Test func testMultipleSubscribers() async throws { + let eventBus = EventBus() + let handler1Checker = TestHandlerChecker() + let handler2Checker = TestHandlerChecker() + + let token1 = await eventBus.subscribe(TestEvent.self) { event in + await handler1Checker.handlerCalled(with: event) + } + let token2 = await eventBus.subscribe(TestEvent.self) { event in + await handler2Checker.handlerCalled(with: event) + } + + await eventBus.publish(TestEvent(id: 3, value: "multi-subscriber test")) + + try await Task.sleep(for: .milliseconds(100)) + + let callCount1 = await handler1Checker.callCount + let callCount2 = await handler2Checker.callCount + #expect(callCount1 == 1) + #expect(callCount2 == 1) + + await eventBus.unsubscribe(token: token1) + await eventBus.unsubscribe(token: token2) + } + + @Test func testSubscribingToDifferentEvents() async throws { + let eventBus = EventBus() + let testEventHandler = TestHandlerChecker() + let anotherTestEventHandler = TestHandlerChecker() + + let token1 = await eventBus.subscribe(TestEvent.self) { event in + await testEventHandler.handlerCalled(with: event) + } + let token2 = await eventBus.subscribe(AnotherTestEvent.self) { event in + await anotherTestEventHandler.handlerCalled(with: event) + } + + await eventBus.publish(TestEvent(id: 4, value: "first event")) + await eventBus.publish(AnotherTestEvent(name: "second event")) + + try await Task.sleep(for: .milliseconds(100)) + + let testEventCallCount = await testEventHandler.callCount + let anotherEventCallCount = await anotherTestEventHandler.callCount + #expect(testEventCallCount == 1) + #expect(anotherEventCallCount == 1) + + await eventBus.unsubscribe(token: token1) + await eventBus.unsubscribe(token: token2) + } + + @Test func testUnsubscribeOneOfMany() async throws { + let eventBus = EventBus() + let handler1Checker = TestHandlerChecker() + let handler2Checker = TestHandlerChecker() + + let token1 = await eventBus.subscribe(TestEvent.self) { event in + await handler1Checker.handlerCalled(with: event) + } + let token2 = await eventBus.subscribe(TestEvent.self) { event in + await handler2Checker.handlerCalled(with: event) + } + + await eventBus.unsubscribe(token: token1) + + await eventBus.publish(TestEvent(id: 5, value: "after one unsubscribe")) + + try await Task.sleep(for: .milliseconds(100)) + + let callCount1 = await handler1Checker.callCount + let callCount2 = await handler2Checker.callCount + #expect(callCount1 == 0) + #expect(callCount2 == 1) + + await eventBus.unsubscribe(token: token2) + } + + // MARK: - Middleware Tests + + private struct EventModifyingMiddleware: MiddlewareProtocol { + func handle(_ event: T, next: @escaping MiddlewareHandler) async throws { + if let testEvent = event as? TestEvent { + let modifiedEvent = TestEvent(id: testEvent.id, value: "modified") + try await next(modifiedEvent as! T) + } else { + try await next(event) + } + } + } + + @Test func testEventMiddleware() async throws { + let eventBus = EventBus(eventMiddleware: Middleware(EventModifyingMiddleware())) + let handlerChecker = TestHandlerChecker() + + let token = await eventBus.subscribe(TestEvent.self) { event in + await handlerChecker.handlerCalled(with: event) + } + + await eventBus.publish(TestEvent(id: 6, value: "original")) + + try await Task.sleep(for: .milliseconds(100)) + + let capturedEvent = await handlerChecker.capturedValues.first + #expect(capturedEvent?.value == "modified") + + await eventBus.unsubscribe(token: token) + } + + private actor HandlerCountingMiddleware: MiddlewareProtocol { + private(set) var counter = 0 + func handle(_ event: T, next: @escaping MiddlewareHandler) async throws { + counter += 1 + try await next(event) + } + } + + @Test func testHandlerMiddleware() async throws { + let countingMiddleware = HandlerCountingMiddleware() + let eventBus = EventBus(handlerMiddleware: Middleware(countingMiddleware)) + let handler1Checker = TestHandlerChecker() + let handler2Checker = TestHandlerChecker() + + let token1 = await eventBus.subscribe(TestEvent.self) { event in + await handler1Checker.handlerCalled(with: event) + } + let token2 = await eventBus.subscribe(TestEvent.self) { event in + await handler2Checker.handlerCalled(with: event) + } + + await eventBus.publish(TestEvent(id: 7, value: "handler middleware test")) + + try await Task.sleep(for: .milliseconds(100)) + + let count = await countingMiddleware.counter + #expect(count == 2) + let callCount1 = await handler1Checker.callCount + let callCount2 = await handler2Checker.callCount + #expect(callCount1 == 1) + #expect(callCount2 == 1) + + await eventBus.unsubscribe(token: token1) + await eventBus.unsubscribe(token: token2) + } + + // MARK: - EventSubscriptions Helper Tests + + @Test func testEventSubscriptionsDeinit() async throws { + let eventBus = EventBus() + let handlerChecker = TestHandlerChecker() + + var subscriptions: EventSubscriptions? = EventSubscriptions(eventBus: eventBus) + await subscriptions!.subscribe(TestEvent.self, id: "test") { event in + await handlerChecker.handlerCalled(with: event) + } + + // Deinitialize EventSubscriptions + subscriptions = nil + + // Allow time for deinit task to run + try await Task.sleep(for: .milliseconds(100)) + + await eventBus.publish(TestEvent(id: 8, value: "after deinit")) + + try await Task.sleep(for: .milliseconds(100)) + + let callCount = await handlerChecker.callCount + #expect(callCount == 0) + } + + @Test func testEventSubscriptionsExplicitUnsubscribe() async throws { + let eventBus = EventBus() + let subscriptions = EventSubscriptions(eventBus: eventBus) + let handlerChecker = TestHandlerChecker() + + let token = await subscriptions.subscribe(TestEvent.self, id: "test") { event in + await handlerChecker.handlerCalled(with: event) + } + + await subscriptions.unsubscribe(token: token) + + await eventBus.publish(TestEvent(id: 9, value: "after explicit unsubscribe")) + + try await Task.sleep(for: .milliseconds(100)) + + let callCount = await handlerChecker.callCount + #expect(callCount == 0) + } +}