Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix events not using correct type for dispatching #25

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,29 +149,29 @@ class LoggingBehavior implements PipelineBehavior {

class LoggingEventObserver implements EventObserver {
@override
void onDispatch<TEvent extends DomainEvent>(
void onDispatch<TEvent>(
TEvent event,
Set<EventHandler<TEvent>> handlers,
Set<EventHandler> handlers,
) {
print(
'[LoggingEventObserver] onDispatch "$event" with ${handlers.length} handlers',
);
}

@override
void onError<TEvent extends DomainEvent>(
void onError<TEvent>(
TEvent event,
EventHandler<TEvent> handler,
EventHandler handler,
Object error,
StackTrace stackTrace,
) {
print('[LoggingEventObserver] onError $event -> $handler ($error)');
}

@override
void onHandled<TEvent extends DomainEvent>(
void onHandled<TEvent>(
TEvent event,
EventHandler<TEvent> handler,
EventHandler handler,
) {
print('[LoggingEventObserver] onHandled $event handled by $handler');
}
Expand Down
3 changes: 1 addition & 2 deletions lib/src/event/dispatch/concurrent_strategy.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:dart_mediator/src/event/dispatch/dispatch_strategy.dart';
import 'package:dart_mediator/src/event/event.dart';
import 'package:dart_mediator/src/event/handler/event_handler.dart';
import 'package:dart_mediator/src/event/observer/event_observer.dart';

Expand All @@ -12,7 +11,7 @@ class ConcurrentDispatchStrategy implements DispatchStrategy {
const ConcurrentDispatchStrategy();

@override
Future<void> execute<TEvent extends DomainEvent>(
Future<void> execute<TEvent>(
Set<EventHandler<TEvent>> handlers,
TEvent event,
List<EventObserver> observers,
Expand Down
3 changes: 1 addition & 2 deletions lib/src/event/dispatch/dispatch_strategy.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:dart_mediator/src/event/event.dart';
import 'package:dart_mediator/src/event/handler/event_handler.dart';
import 'package:dart_mediator/src/event/dispatch/concurrent_strategy.dart';
import 'package:dart_mediator/src/event/dispatch/sequential_strategy.dart';
Expand All @@ -18,7 +17,7 @@ abstract interface class DispatchStrategy {

/// Executes the given strategy by applying the [event] to the given
/// [handlers].
Future<void> execute<TEvent extends DomainEvent>(
Future<void> execute<TEvent>(
Set<EventHandler<TEvent>> handlers,
TEvent event,
List<EventObserver> observers,
Expand Down
3 changes: 1 addition & 2 deletions lib/src/event/dispatch/sequential_strategy.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:dart_mediator/src/event/dispatch/dispatch_strategy.dart';
import 'package:dart_mediator/src/event/event.dart';
import 'package:dart_mediator/src/event/handler/event_handler.dart';
import 'package:dart_mediator/src/event/observer/event_observer.dart';

Expand All @@ -14,7 +13,7 @@ class SequentialDispatchStrategy implements DispatchStrategy {
const SequentialDispatchStrategy();

@override
Future<void> execute<TEvent extends DomainEvent>(
Future<void> execute<TEvent>(
Set<EventHandler<TEvent>> handlers,
TEvent event,
List<EventObserver> observers,
Expand Down
10 changes: 9 additions & 1 deletion lib/src/event/event_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,15 @@ class EventManager {
TEvent event, [
DispatchStrategy? dispatchStrategy,
]) async {
final handlers = _eventHandlerStore.getHandlersFor<TEvent>();
late final eventRuntimeType = event.runtimeType;

final handlers = {
// Add handlers for TEvent.
..._eventHandlerStore.getHandlersFor(TEvent),
// If a base type was used, add handlers for the runtime type.
if (eventRuntimeType != TEvent)
..._eventHandlerStore.getHandlersFor(eventRuntimeType)
};

for (final observer in _observers) {
observer.onDispatch(event, handlers);
Expand Down
20 changes: 10 additions & 10 deletions lib/src/event/handler/event_handler_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ class EventHandlerStore {

/// Registers the [handler] to a given [TEvent].
void register<TEvent>(EventHandler<TEvent> handler) {
final handlers = _getHandlersFor<TEvent>();
final handlers = _getHandlersFor(TEvent);

assert(
!handlers.contains(handler),
'register<$TEvent> was called with an already registered handler',
);

// When the store is being modified, create a new copy.
_handlers[TEvent] = <EventHandler<TEvent>>{
_handlers[TEvent] = <EventHandler>{
...handlers,
handler,
};
}

/// Unregisters the given [handler].
void unregister<TEvent>(EventHandler<TEvent> handler) {
final handlers = _getHandlersFor<TEvent>();
final handlers = _getHandlersFor(TEvent);

assert(
handlers.contains(handler),
Expand All @@ -36,18 +36,18 @@ class EventHandlerStore {
_handlers[TEvent] = update;
}

/// Returns all registered [EventHandler]'s for [TEvent].
Set<EventHandler<TEvent>> getHandlersFor<TEvent>() {
final handlers = _getHandlersFor<TEvent>();
/// Returns all registered [EventHandler]'s for [eventType].
Set<EventHandler> getHandlersFor(Type eventType) {
final handlers = _getHandlersFor(eventType);

return UnmodifiableSetView(handlers);
}

Set<EventHandler<TEvent>> _getHandlersFor<TEvent>() {
Set<EventHandler> _getHandlersFor(Type eventType) {
final handlers = _handlers.putIfAbsent(
TEvent,
() => <EventHandler<TEvent>>{},
) as Set<EventHandler<TEvent>>;
eventType,
() => <EventHandler>{},
);

return handlers;
}
Expand Down
12 changes: 6 additions & 6 deletions lib/src/event/observer/event_observer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ abstract interface class EventObserver {
/// When the [event] is dispatched.
///
/// [handlers] will be executed based on the [DispatchStrategy].
void onDispatch<TEvent extends DomainEvent>(
void onDispatch<TEvent>(
TEvent event,
Set<EventHandler<TEvent>> handlers,
Set<EventHandler> handlers,
);

/// When the [event] is handled by the [handler].
void onHandled<TEvent extends DomainEvent>(
void onHandled<TEvent>(
TEvent event,
EventHandler<TEvent> handler,
EventHandler handler,
);

/// When the [event] is failed by the [handler] the [error] contains the
/// exception object.
void onError<TEvent extends DomainEvent>(
void onError<TEvent>(
TEvent event,
EventHandler<TEvent> handler,
EventHandler handler,
Object error,
StackTrace stackTrace,
);
Expand Down
12 changes: 6 additions & 6 deletions test/integration/choreography_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -220,19 +220,19 @@ class InventoryAdjustedEventHandler

class LoggingEventObserver implements EventObserver {
@override
void onDispatch<TEvent extends DomainEvent>(
void onDispatch<TEvent>(
TEvent event,
Set<EventHandler<TEvent>> handlers,
Set<EventHandler> handlers,
) {
print(
'$LoggingEventObserver: onDispatch $event with ${handlers.length} handlers',
);
}

@override
void onError<TEvent extends DomainEvent>(
void onError<TEvent>(
TEvent event,
EventHandler<TEvent> handler,
EventHandler handler,
Object error,
StackTrace stackTrace,
) {
Expand All @@ -241,9 +241,9 @@ class LoggingEventObserver implements EventObserver {
}

@override
void onHandled<TEvent extends DomainEvent>(
void onHandled<TEvent>(
TEvent event,
EventHandler<TEvent> handler,
EventHandler handler,
) {
print('$LoggingEventObserver: onHandled $event -> ${handler.runtimeType}');
}
Expand Down
31 changes: 31 additions & 0 deletions test/integration/event_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,41 @@ void main() {
expect(handler3, expected);
expect(eventHandler.events, expected);
});

test('it uses instance and compile event type to dispatch', () async {
late final bool concreteEventHandled;
late final bool baseEventHandled;

mediator.events
.on<BaseEvent>()
.subscribeFunction((e) => baseEventHandled = true);

mediator.events
.on<ConcreteEvent>()
.subscribeFunction((e) => concreteEventHandled = true);

await mediator.events.dispatchBaseEvent(const BaseEvent.concrete());

expect(
baseEventHandled,
isTrue,
reason: 'Compile type should be used to dispatch events',
);

expect(
concreteEventHandled,
isTrue,
reason: 'Instance type should be used to dispatch events',
);
});
});
});
}

extension _BaseEventExtension on EventManager {
Future<void> dispatchBaseEvent(BaseEvent event) => dispatch(event);
}

class _CollectingEventSubscriber implements EventHandler<DomainIntEvent> {
final events = <int>[];

Expand Down
8 changes: 8 additions & 0 deletions test/test_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,11 @@ class EventS implements DomainEvent {
@override
String toString() => 'EventS(s: $s)';
}

class BaseEvent implements DomainEvent {
const factory BaseEvent.concrete() = ConcreteEvent;
}

class ConcreteEvent implements BaseEvent {
const ConcreteEvent();
}
16 changes: 12 additions & 4 deletions test/unit/event/event_handler/event_handler_store_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:dart_mediator/src/event/handler/event_handler_store.dart';
import 'package:test/test.dart';

import '../../../mocks.dart';
import '../../../test_data.dart';

void main() {
group('EventHandlerStore', () {
Expand Down Expand Up @@ -57,13 +58,20 @@ void main() {

group('getHandlersFor{TEvent}', () {
test('it returns the registered handlers', () {
final handler = MockEventHandler<int>();
final baseHandler = MockEventHandler<BaseEvent>();
final concreteHandler = MockEventHandler<ConcreteEvent>();

eventHandlerStore.register(handler);
eventHandlerStore.register(baseHandler);
eventHandlerStore.register(concreteHandler);

expect(
eventHandlerStore.getHandlersFor(BaseEvent),
{baseHandler},
);

expect(
eventHandlerStore.getHandlersFor<int>(),
{handler},
eventHandlerStore.getHandlersFor(ConcreteEvent),
{concreteHandler},
);
});
});
Expand Down
46 changes: 38 additions & 8 deletions test/unit/event/event_manager_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:dart_mediator/src/event/event_manager.dart';
import 'package:dart_mediator/src/event/handler/event_handler.dart';
import 'package:dart_mediator/src/event/subscription_builder/event_subscription_builder.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
Expand Down Expand Up @@ -42,13 +43,13 @@ void main() {
const event = DomainIntEvent(123);

test('it executes the dispatch strategy', () async {
final handlers = {MockEventHandler<DomainIntEvent>()};
final Set<EventHandler> handlers = {MockEventHandler<DomainIntEvent>()};

when(() => mockEventHandlerStore.getHandlersFor<DomainIntEvent>())
when(() => mockEventHandlerStore.getHandlersFor(DomainIntEvent))
.thenReturn(handlers);

when(() => mockDispatchStrategy.execute<DomainIntEvent>(
any(), any(), any())).thenAnswer((_) => Future.value());
when(() => mockDispatchStrategy.execute(any(), any(), any()))
.thenAnswer((_) => Future.value());

await eventManager.dispatch(event);

Expand All @@ -57,18 +58,47 @@ void main() {
});

test('it calls onDispatch', () async {
final handlers = {MockEventHandler<DomainIntEvent>()};
final Set<EventHandler> handlers = {MockEventHandler<DomainIntEvent>()};

when(() => mockEventHandlerStore.getHandlersFor<DomainIntEvent>())
when(() => mockEventHandlerStore.getHandlersFor(DomainIntEvent))
.thenReturn(handlers);

when(() => mockDispatchStrategy.execute<DomainIntEvent>(
any(), any(), any())).thenAnswer((_) => Future.value());
when(() => mockDispatchStrategy.execute(any(), any(), any()))
.thenAnswer((_) => Future.value());

await eventManager.dispatch(event);

verify(() => mockEventObserver.onDispatch(event, handlers));
});

test('it gets both runtime and compile time handlers', () async {
final Set<MockEventHandler> baseHandlers = {
MockEventHandler<BaseEvent>()
};

final Set<MockEventHandler> concreteHandlers = {
MockEventHandler<ConcreteEvent>()
};

final combined = {...baseHandlers, ...concreteHandlers};

when(() => mockEventHandlerStore.getHandlersFor(BaseEvent))
.thenReturn(baseHandlers);

when(() => mockEventHandlerStore.getHandlersFor(ConcreteEvent))
.thenReturn(concreteHandlers);

when(() => mockDispatchStrategy.execute(any(), any(), any()))
.thenAnswer((_) => Future.value());

const event = BaseEvent.concrete();

await eventManager.dispatch(event);

verify(() => mockEventObserver.onDispatch(event, combined));
verify(() =>
mockDispatchStrategy.execute(combined, event, [mockEventObserver]));
});
});
});
}
Loading