diff --git a/example/example.dart b/example/example.dart index 7125b00..7814f0c 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:dart_mediator/mediator.dart'; +import 'package:meta/meta.dart'; Future main() async { final mediator = Mediator.create( @@ -15,17 +16,21 @@ Future main() async { mediator.requests.register(MyCommandHandler()); // Subscribe to the count event. + mediator.events.on().subscribeFactory(createCountEventHandler); + mediator.events .on() - .map((event) => event.count) .distinct() + .map((event) => event.count) .subscribeFunction( - (count) => print('[CountEvent handler] received count: $count'), - ); + (count) { + // Only distinct count events will get to this point. + // LoggingEventObserver will still see the event. + print('[CountEvent Handler] received distinct count: $count'); + }, + ); - mediator.events.on().subscribeFunction( - (count) => print('[Other Event Handler] received: $count'), - ); + print('--- Query Example ---'); const getUserQuery = GetUserByIdQuery(123); @@ -35,35 +40,59 @@ Future main() async { print('Got $getUserQuery response: $resp'); - print('---'); + print('\n--- Command Example ---'); const order66Command = MyCommand('Order 66'); - print('Sending command $order66Command'); + print('Sending $order66Command'); await mediator.requests.send(order66Command); - print('Command $order66Command completed'); + print('$order66Command completed'); - print('---'); + print('\n--- Events Example ---'); const countEvent = CountEvent(123); + // Event will be handled by 2 event handlers. await mediator.events.dispatch(countEvent); + // Event will only be handled by 1 event handler (distinct). await mediator.events.dispatch(countEvent); print('done'); } +@immutable class CountEvent implements DomainEvent { final int count; const CountEvent(this.count); @override String toString() => 'CountEvent(count: $count)'; + + @override + int get hashCode => Object.hash(runtimeType, count); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is CountEvent && + other.count == count); + } } +class CountEventHandler implements EventHandler { + @override + FutureOr handle(CountEvent event) { + final count = event.count; + print('[CountEvent Handler] received count: $count'); + } +} + +CountEventHandler createCountEventHandler() => CountEventHandler(); + class MyCommand implements Command { final String command; const MyCommand(this.command); @@ -75,9 +104,16 @@ class MyCommand implements Command { class MyCommandHandler implements CommandHandler { @override Future handle(MyCommand request) async { - print('[MyCommandHandler] Executing "$request"'); - await Future.delayed(const Duration(milliseconds: 500)); - print('[MyCommandHandler] "$request" completed'); + final command = request.command; + print('[MyCommandHandler] Execute $command'); + { + await Future.delayed(const Duration(milliseconds: 300)); + for (var i = 0; i < 3; i++) { + print('[MyCommandHandler] pew'); + await Future.delayed(const Duration(milliseconds: 300)); + } + } + print('[MyCommandHandler] $request executed!'); } } @@ -92,7 +128,7 @@ class GetUserByIdQuery implements Query { class GetUserByIdQueryHandler implements QueryHandler { @override Future handle(GetUserByIdQuery request) async { - print('[GetUserByIdQueryHandler] handeling $request'); + print('[GetUserByIdQueryHandler] handling $request'); final user = await getUserByIdAsync(request.userId); print('[GetUserByIdQueryHandler] got $user'); return user; @@ -136,7 +172,9 @@ class LoggingEventObserver implements EventObserver { void onHandled( TEvent event, EventHandler handler, - ) {} + ) { + print('[LoggingEventObserver] onHandled $event handled by $handler'); + } } class User { diff --git a/lib/src/event/handler/event_handler.dart b/lib/src/event/handler/event_handler.dart index 342e112..82a8df9 100644 --- a/lib/src/event/handler/event_handler.dart +++ b/lib/src/event/handler/event_handler.dart @@ -1,19 +1,32 @@ import 'dart:async'; +import 'package:meta/meta.dart'; + /// Factory to create a [EventHandler]. typedef EventHandlerFactory = EventHandler Function(); /// Handler for [TEvent]. abstract interface class EventHandler { - /// Function based event handler + /// Function based [EventHandler]. + /// + /// Each event the underlying [handler] will be executed. const factory EventHandler.function( FutureOr Function(TEvent event) handler, ) = _FunctionEventHandler; + /// Factory based [EventHandler] + /// + /// Each event the underlying [factory] will be instantiated and used + /// to handle the [TEvent]. + const factory EventHandler.factory( + EventHandlerFactory factory, + ) = _FactoryEventHandler; + /// Handles the given [event]. FutureOr handle(TEvent event); } +@immutable class _FunctionEventHandler implements EventHandler { final FutureOr Function(T event) handler; @@ -21,4 +34,39 @@ class _FunctionEventHandler implements EventHandler { @override FutureOr handle(T event) => handler(event); + + @override + int get hashCode => Object.hash(runtimeType, handler); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _FunctionEventHandler && + other.handler == handler); + } +} + +@immutable +class _FactoryEventHandler implements EventHandler { + final EventHandlerFactory factory; + + const _FactoryEventHandler(this.factory); + + @override + FutureOr handle(T event) { + final handler = factory(); + return handler.handle(event); + } + + @override + int get hashCode => Object.hash(runtimeType, factory); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _FactoryEventHandler && + other.factory == factory); + } } diff --git a/lib/src/event/handler/event_handler_store.dart b/lib/src/event/handler/event_handler_store.dart index 47520b5..6bbe924 100644 --- a/lib/src/event/handler/event_handler_store.dart +++ b/lib/src/event/handler/event_handler_store.dart @@ -1,8 +1,9 @@ +import 'dart:collection'; + import 'package:dart_mediator/src/event/handler/event_handler.dart'; class EventHandlerStore { final _handlers = >{}; - final _handlerFactories = >{}; /// Registers the [handler] to a given [TEvent]. void register(EventHandler handler) { @@ -16,18 +17,6 @@ class EventHandlerStore { handlers.add(handler); } - /// Registers the [factory] to a given [TEvent]. - void registerFactory(EventHandlerFactory factory) { - final factories = _getHandlerFactoriesFor(); - - assert( - !factories.contains(factory), - 'registerFactory<$TEvent> was called with an already registered factory', - ); - - factories.add(factory); - } - /// Unregisters the given [handler]. void unregister(EventHandler handler) { final handlers = _getHandlersFor(); @@ -40,28 +29,11 @@ class EventHandlerStore { handlers.remove(handler); } - /// Unregisters the given [factory]. - void unregisterFactory(EventHandlerFactory factory) { - final factories = _getHandlerFactoriesFor(); - - assert( - factories.contains(factory), - 'unregisterFactory<$TEvent> was called for a factory that was never registered', - ); - - factories.remove(factory); - } - /// Returns all registered [EventHandler]'s for [TEvent]. Set> getHandlersFor() { final handlers = _getHandlersFor(); - final factories = - _getHandlerFactoriesFor().map((factory) => factory()); - return { - ...handlers, - ...factories, - }; + return UnmodifiableSetView(handlers); } Set> _getHandlersFor() { @@ -72,13 +44,4 @@ class EventHandlerStore { return handlers; } - - Set> _getHandlerFactoriesFor() { - final factories = _handlerFactories.putIfAbsent( - TEvent, - () => >{}, - ) as Set>; - - return factories; - } } diff --git a/lib/src/event/subscription_builder/event_subscription_builder.dart b/lib/src/event/subscription_builder/event_subscription_builder.dart index c0d8ba4..cecb0aa 100644 --- a/lib/src/event/subscription_builder/event_subscription_builder.dart +++ b/lib/src/event/subscription_builder/event_subscription_builder.dart @@ -105,16 +105,9 @@ abstract class EventSubscriptionBuilder { /// This finalizes the builder and applies all the steps /// before subscribing. EventSubscription subscribe(EventHandler handler); - - /// Subscribes to the given [factory]. - /// - /// This finalizes the builder and applies all the steps - /// before subscribing. - EventSubscription subscribeFactory(EventHandlerFactory factory); } -extension EventSubscriptionBuilderFunctionExtension - on EventSubscriptionBuilder { +extension EventSubscriptionBuilderExtensions on EventSubscriptionBuilder { /// Subscribes to the given [handler]. /// /// This finalizes the builder and applies all the steps @@ -124,6 +117,18 @@ extension EventSubscriptionBuilderFunctionExtension ) { return subscribe(EventHandler.function(handler)); } + + /// Subscribes to the given [factory]. + /// + /// This finalizes the builder and applies all the steps + /// before subscribing. + /// + /// This factory will be resolved into an actual [EventHandler] at request time. + EventSubscription subscribeFactory( + EventHandlerFactory factory, + ) { + return subscribe(EventHandler.factory(factory)); + } } class _EventSubscriptionBuilder extends EventSubscriptionBuilder { @@ -141,17 +146,6 @@ class _EventSubscriptionBuilder extends EventSubscriptionBuilder { return subscription; } - - @override - EventSubscription subscribeFactory(EventHandlerFactory factory) { - final subscription = EventSubscription( - () => _store.unregisterFactory(factory), - ); - - _store.registerFactory(factory); - - return subscription; - } } /// Base for implementing custom [EventSubscriptionBuilder]. @@ -177,12 +171,4 @@ abstract class BaseEventSubscriptionBuilder EventSubscription subscribe(EventHandler handler) { return parent.subscribe(createHandler(handler)); } - - @override - EventSubscription subscribeFactory(EventHandlerFactory factory) { - return parent.subscribeFactory(() { - final handler = factory(); - return createHandler(handler); - }); - } } diff --git a/lib/src/request/handler/request_handler.dart b/lib/src/request/handler/request_handler.dart index 7842926..7cfe5bb 100644 --- a/lib/src/request/handler/request_handler.dart +++ b/lib/src/request/handler/request_handler.dart @@ -11,11 +11,16 @@ typedef RequestHandlerFactory> @internal abstract interface class RequestHandler> { - /// Function based request handler + /// Function based [RequestHandler]. const factory RequestHandler.function( FutureOr Function(TRequest) handler, ) = _FunctionRequestHandler; + /// Factory based [RequestHandler]. + const factory RequestHandler.factory( + RequestHandlerFactory factory, + ) = _FactoryRequestHandler; + /// Handles the given [request]. FutureOr handle(TRequest request); } @@ -29,6 +34,7 @@ abstract interface class QueryHandler> implements RequestHandler {} +@immutable class _FunctionRequestHandler> implements RequestHandler { final FutureOr Function(TRequest) handler; @@ -37,4 +43,40 @@ class _FunctionRequestHandler> @override FutureOr handle(TRequest request) => handler(request); + + @override + int get hashCode => Object.hash(runtimeType, handler); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _FunctionRequestHandler && + other.handler == handler); + } +} + +@immutable +class _FactoryRequestHandler> + implements RequestHandler { + final RequestHandlerFactory factory; + + const _FactoryRequestHandler(this.factory); + + @override + FutureOr handle(TRequest request) { + final handler = factory(); + return handler.handle(request); + } + + @override + int get hashCode => Object.hash(runtimeType, factory); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _FactoryRequestHandler && + other.factory == factory); + } } diff --git a/lib/src/request/handler/request_handler_store.dart b/lib/src/request/handler/request_handler_store.dart index da367eb..80a317e 100644 --- a/lib/src/request/handler/request_handler_store.dart +++ b/lib/src/request/handler/request_handler_store.dart @@ -3,7 +3,6 @@ import 'package:dart_mediator/src/request/handler/request_handler.dart'; class RequestHandlerStore { final _handlers = {}; - final _handlerFactories = {}; /// Registers the [handler] to a given [TRequest]. void register>( @@ -14,31 +13,9 @@ class RequestHandlerStore { 'register<$TResponse, $TRequest> was called with an already registered handler', ); - assert( - !_handlerFactories.containsKey(TRequest), - 'register<$TRequest> was called with an already registered factory', - ); - _handlers[TRequest] = handler; } - /// Registers the [factory] to a given [TRequest]. - void registerFactory>( - RequestHandlerFactory factory, - ) { - assert( - !_handlers.containsKey(TRequest), - 'registerFactory<$TResponse, $TRequest> was called with an already registered handler', - ); - - assert( - !_handlerFactories.containsKey(TRequest), - 'registerFactory<$TRequest> was called with an already registered factory', - ); - - _handlerFactories[TRequest] = factory; - } - /// Unregisters the given [handler]. void unregister>( RequestHandler handler, @@ -56,8 +33,7 @@ class RequestHandlerStore { Request request, ) { final requestType = request.runtimeType; - final handler = - _handlers[requestType] ?? _handlerFactories[requestType]?.call(); + final handler = _handlers[requestType]; assert( handler != null, diff --git a/lib/src/request/pipeline/pipeline_behavior.dart b/lib/src/request/pipeline/pipeline_behavior.dart index e56824c..0ef0d50 100644 --- a/lib/src/request/pipeline/pipeline_behavior.dart +++ b/lib/src/request/pipeline/pipeline_behavior.dart @@ -1,11 +1,30 @@ import 'dart:async'; +import 'package:meta/meta.dart'; + /// Represents the continuation for the next task to execute in the pipeline. typedef RequestHandlerDelegate = FutureOr Function(); +/// Factory to create a [PipelineBehavior]. +typedef PipelineBehaviorFactory + = PipelineBehavior Function(); + +typedef PipelineHandler = FutureOr Function( + TRequest, RequestHandlerDelegate); + /// Pipeline behavior to surround the inner handler. /// Implementations add additional behavior and await the next delegate. abstract interface class PipelineBehavior { + /// Function based [PipelineBehavior]. + const factory PipelineBehavior.function( + PipelineHandler handler, + ) = _FunctionPipelineBehavior; + + /// Factory based [PipelineBehavior]. + const factory PipelineBehavior.factory( + PipelineBehaviorFactory factory, + ) = _FactoryPipelineBehavior; + /// Pipeline handler for [request]. /// /// Perform any additional behavior and await the [next] delegate. @@ -14,3 +33,57 @@ abstract interface class PipelineBehavior { RequestHandlerDelegate next, ); } + +@immutable +class _FunctionPipelineBehavior + implements PipelineBehavior { + final PipelineHandler handler; + + const _FunctionPipelineBehavior(this.handler); + + @override + FutureOr handle( + TRequest request, + RequestHandlerDelegate next, + ) => + handler(request, next); + + @override + int get hashCode => Object.hash(runtimeType, handler); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _FunctionPipelineBehavior && + other.handler == handler); + } +} + +@immutable +class _FactoryPipelineBehavior + implements PipelineBehavior { + final PipelineBehaviorFactory factory; + + const _FactoryPipelineBehavior(this.factory); + + @override + FutureOr handle( + TRequest request, + RequestHandlerDelegate next, + ) { + final behavior = factory(); + return behavior.handle(request, next); + } + + @override + int get hashCode => Object.hash(runtimeType, factory); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _FactoryPipelineBehavior && + other.factory == factory); + } +} diff --git a/lib/src/request/pipeline/pipeline_behavior_store.dart b/lib/src/request/pipeline/pipeline_behavior_store.dart index 725bf41..07a2635 100644 --- a/lib/src/request/pipeline/pipeline_behavior_store.dart +++ b/lib/src/request/pipeline/pipeline_behavior_store.dart @@ -4,9 +4,7 @@ import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart'; class PipelineBehaviorStore implements PipelineConfigurator { final _handlers = >{}; - final _handlerFactories = >{}; final _genericHandlers = {}; - final _genericHandlerFactories = {}; @override void register>( @@ -20,19 +18,6 @@ class PipelineBehaviorStore implements PipelineConfigurator { handlers.add(behavior); } - @override - void registerFactory>( - PipelineBehaviorFactory factory, - ) { - final handlers = _handlerFactories.putIfAbsent( - TRequest, - () => [], - ); - - handlers.add(factory); - } - @override void registerGeneric( PipelineBehavior behavior, @@ -41,26 +26,28 @@ class PipelineBehaviorStore implements PipelineConfigurator { } @override - void registerGenericFactory( - PipelineBehaviorFactory factory, + void unregister>( + PipelineBehavior behavior, ) { - _genericHandlerFactories.add(factory); - } + final handlers = _handlers[TRequest]; - @override - void unregister(PipelineBehavior behavior) { - for (final handlers in _handlers.values) { - handlers.remove(behavior); - } - _genericHandlers.remove(behavior); + assert( + handlers != null, + 'unregister<$TResponse, $TRequest> was called for a behavior that was never registered', + ); + + handlers!.remove(behavior); } @override - void unregisterFactory(PipelineBehaviorFactory factory) { - for (final handlers in _handlerFactories.values) { - handlers.remove(factory); - } - _genericHandlerFactories.remove(factory); + void unregisterGeneric(PipelineBehavior behavior) { + assert( + _genericHandlers.contains(behavior), + 'unregisterGeneric was called for a behavior that was never registered', + ); + + _genericHandlers.remove(behavior); } /// Returns all [PipelineBehavior]'s that match. @@ -69,19 +56,11 @@ class PipelineBehaviorStore implements PipelineConfigurator { ) { final requestType = request.runtimeType; - final handlerFactories = - _handlerFactories[requestType]?.map((factory) => factory()); - - final genericFactories = - _genericHandlerFactories.map((factory) => factory()); - final handlers = _handlers[requestType]; return [ if (handlers != null) ...handlers, - if (handlerFactories != null) ...handlerFactories, ..._genericHandlers, - ...genericFactories, ]; } } diff --git a/lib/src/request/pipeline/pipeline_configurator.dart b/lib/src/request/pipeline/pipeline_configurator.dart index 90b8bec..5f6a9dd 100644 --- a/lib/src/request/pipeline/pipeline_configurator.dart +++ b/lib/src/request/pipeline/pipeline_configurator.dart @@ -1,9 +1,4 @@ -import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart'; -import 'package:dart_mediator/src/request/request.dart'; - -/// Factory to create a [PipelineBehavior]. -typedef PipelineBehaviorFactory - = PipelineBehavior Function(); +import 'package:dart_mediator/request_manager.dart'; abstract interface class PipelineConfigurator { /// Registers the [behavior]. @@ -14,15 +9,6 @@ abstract interface class PipelineConfigurator { PipelineBehavior behavior, ); - /// Registers the [factory]. - /// - /// When using a generic [PipelineBehavior] the [registerGenericFactory] should - /// be used instead. - void registerFactory>( - PipelineBehaviorFactory factory, - ); - /// Registers the generic [behavior]. /// /// Note, this should only be used when [register] is not possible. @@ -30,16 +16,65 @@ abstract interface class PipelineConfigurator { PipelineBehavior behavior, ); - /// Registers the generic [factory]. - /// - /// Note, this should only be used when [registerFactory] is not possible. - void registerGenericFactory( - PipelineBehaviorFactory factory, + /// Unregisters the given [behavior]. + void unregister>( + PipelineBehavior behavior, ); - /// Unregisters the given [behavior]. - void unregister(PipelineBehavior behavior); + /// Unregisters the generic [behavior]. + void unregisterGeneric(PipelineBehavior behavior); +} - /// Unregisters the given [factory]. - void unregisterFactory(PipelineBehaviorFactory factory); +extension PipelineConfiguratorExtensions on PipelineConfigurator { + /// Registers the given [handler]. + /// + /// This will create a function based [PipelineBehavior]. + /// + /// See [PipelineBehavior.function]. + void registerFunction>( + PipelineHandler handler, + ) { + register(PipelineBehavior.function(handler)); + } + + /// Registers the given [factory]. + /// + /// This will create a factory based [PipelineBehavior]. This factory will be + /// resolved into an actual [PipelineBehavior] at request time. + /// + /// See [PipelineBehavior.factory]. + void registerFactory>( + PipelineBehaviorFactory factory, + ) { + register(PipelineBehavior.factory(factory)); + } + + /// Registers the given generic [handler]. + /// + /// This will create a function based [PipelineBehavior]. + /// + /// + /// See [registerGeneric]. + /// See [PipelineBehavior.function]. + void registerGenericFunction( + PipelineHandler handler, + ) { + registerGeneric(PipelineBehavior.function(handler)); + } + + /// Registers the given generic [factory]. + /// + /// This will create a factory based [PipelineBehavior]. This factory will be + /// resolved into an actual [PipelineBehavior] at request time. + /// + /// See [registerGeneric]. + /// See [PipelineBehavior.factory]. + void registerGenericFactory( + PipelineBehaviorFactory factory, + ) { + registerGeneric(PipelineBehavior.factory(factory)); + } } diff --git a/lib/src/request/request_manager.dart b/lib/src/request/request_manager.dart index de48fcf..5a28e5d 100644 --- a/lib/src/request/request_manager.dart +++ b/lib/src/request/request_manager.dart @@ -43,13 +43,6 @@ class RequestManager { _requestHandlerStore.register(handler); } - /// Registers the request [factory] for the given [TRequest]. - void registerFactory>( - RequestHandlerFactory factory, - ) { - _requestHandlerStore.registerFactory(factory); - } - /// Sends a [request] to a single [RequestHandler]. /// /// Make sure the [RequestHandler] is [register]ed before calling this method. @@ -99,4 +92,16 @@ extension RequestManagerExtensions on RequestManager { ) { register(RequestHandler.function(handler)); } + + /// Registers the given [factory]. + /// + /// This will create a factory based request handler. This factory will be + /// resolved into an actual [RequestHandler] at request time. + /// + /// See [RequestHandler.factory]. + void registerFactory>( + RequestHandlerFactory factory, + ) { + register(RequestHandler.factory(factory)); + } } diff --git a/test/integration/choreography_test.dart b/test/integration/choreography_test.dart index 79c561f..ab312d0 100644 --- a/test/integration/choreography_test.dart +++ b/test/integration/choreography_test.dart @@ -51,6 +51,33 @@ void main() { 'keyboard': 9, }); }); + + test('it does not place the failed order', () async { + // Requests + mediator.requests.register(PlaceOrderCommandHandler()); + mediator.requests.register(GetInventoryQueryHandler()); + mediator.requests.pipeline.register(PlaceOrderValidationBehavior()); + mediator.requests.pipeline.registerGeneric(LoggingBehavior()); + + // Events + mediator.events + .on() + .subscribe(OrderPlacedEventHandler()); + mediator.events + .on() + .subscribe(InventoryAdjustedEventHandler()); + + // Start flow + await expectLater( + mediator.requests.send( + const PlaceOrderCommand( + '123', + {'mouse': 20, 'keyboard': 10}, + ), + ), + throwsStateError, + ); + }); }); }); } @@ -78,7 +105,7 @@ class LoggingBehavior implements PipelineBehavior { @override FutureOr handle(request, RequestHandlerDelegate next) async { try { - print('$LoggingBehavior: Handeling $request'); + print('$LoggingBehavior: Handling $request'); return await next(); } finally { print('$LoggingBehavior: $request completed'); diff --git a/test/integration/event_test.dart b/test/integration/event_test.dart index c715fc0..1912401 100644 --- a/test/integration/event_test.dart +++ b/test/integration/event_test.dart @@ -98,6 +98,13 @@ void main() { .where((event) => event > 10) .subscribeFactory(factory); + final eventHandler = _CollectingEventSubscriber(); + + mediator.events + .on() + .where((event) => event.count > 10) + .subscribe(eventHandler); + final events = Iterable.generate(20, (index) => index); for (final event in events.skip(10)) { @@ -109,7 +116,19 @@ void main() { expect(handler1, expected); expect(handler2, expected); expect(handler3, expected); + expect(eventHandler.events, expected); }); }); }); } + +class _CollectingEventSubscriber implements EventHandler { + final events = []; + + @override + void handle(DomainIntEvent event) { + if (event.count > 10) { + events.add(event.count); + } + } +} diff --git a/test/integration/request_test.dart b/test/integration/request_test.dart index 591d55b..363d704 100644 --- a/test/integration/request_test.dart +++ b/test/integration/request_test.dart @@ -22,7 +22,7 @@ void main() { }); group('requests', () { - test('it handles the request', () async { + test('it handles the normal request', () async { mediator.requests.register(GetDataQueryHandler()); final data = await mediator.requests.send(const GetDataQuery(123)); @@ -30,22 +30,38 @@ void main() { expect(data, '123'); }); + test('it handles the function request', () async { + mediator.requests.registerFunction(GetDataQueryHandler().handle); + + final data = await mediator.requests.send(const GetDataQuery(123)); + + expect(data, '123'); + }); + + test('it handles the factory request', () async { + mediator.requests.registerFactory(() => GetDataQueryHandler()); + + final data = await mediator.requests.send(const GetDataQuery(123)); + + expect(data, '123'); + }); + test('it handles the request with pipeline', () async { var pipeline = false; - final behavior = WrappingBehavior( - () { - print('pipeline callback'); - return pipeline = true; - }, - ); mediator.requests.register(GetDataQueryHandler()); - mediator.requests.pipeline.registerGeneric(behavior); - mediator.requests.pipeline.registerGeneric(DelayBehavior()); + mediator.requests.pipeline.registerGenericFunction((req, next) { + pipeline = true; + return next(); + }); + mediator.requests.pipeline.registerGenericFactory( + () => DelayBehavior(), + ); - await mediator.requests.send(const GetDataQuery(123)); + final data = await mediator.requests.send(const GetDataQuery(123)); expect(pipeline, isTrue); + expect(data, '123'); }); }); }); diff --git a/test/test_data.dart b/test/test_data.dart index 109ab47..0cb4d6b 100644 --- a/test/test_data.dart +++ b/test/test_data.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:dart_mediator/contracts.dart'; import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart'; +import 'package:meta/meta.dart'; +@immutable class DomainIntEvent implements DomainEvent { final int count; @@ -13,22 +15,34 @@ class DomainIntEvent implements DomainEvent { }) { return DomainIntEvent(count); } + + @override + int get hashCode => Object.hash(runtimeType, count); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is DomainIntEvent && + other.count == count); + } } +@immutable class GetDataQuery implements Query { final int id; const GetDataQuery(this.id); -} -class WrappingBehavior implements PipelineBehavior { - final Function() callback; - WrappingBehavior(this.callback); + @override + int get hashCode => Object.hash(runtimeType, id); @override - FutureOr handle(request, RequestHandlerDelegate next) { - callback(); - return next(); + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is GetDataQuery && + other.id == id); } } diff --git a/test/unit/event/dispatch/concurrent_dispatch_test.dart b/test/unit/event/dispatch/concurrent_dispatch_test.dart index 916c2fd..dabc937 100644 --- a/test/unit/event/dispatch/concurrent_dispatch_test.dart +++ b/test/unit/event/dispatch/concurrent_dispatch_test.dart @@ -71,9 +71,12 @@ void main() { count = newCount; } - final handlerA = EventHandler.function(handle); - final handlerB = EventHandler.function(handle); - final handlerC = EventHandler.function(handle); + final handlerA = + EventHandler.function((e) => handle(e)); + final handlerB = + EventHandler.function((e) => handle(e)); + final handlerC = + EventHandler.function((e) => handle(e)); final handlers = {handlerA, handlerB, handlerC}; const event = DomainIntEvent(1); diff --git a/test/unit/event/dispatch/sequential_dispatch_test.dart b/test/unit/event/dispatch/sequential_dispatch_test.dart index 3d0912f..514e911 100644 --- a/test/unit/event/dispatch/sequential_dispatch_test.dart +++ b/test/unit/event/dispatch/sequential_dispatch_test.dart @@ -71,9 +71,12 @@ void main() { count = newCount; } - final handlerA = EventHandler.function(handle); - final handlerB = EventHandler.function(handle); - final handlerC = EventHandler.function(handle); + final handlerA = + EventHandler.function((e) => handle(e)); + final handlerB = + EventHandler.function((e) => handle(e)); + final handlerC = + EventHandler.function((e) => handle(e)); final handlers = {handlerA, handlerB, handlerC}; const event = DomainIntEvent(1); diff --git a/test/unit/event/event_handler/event_handler_store_test.dart b/test/unit/event/event_handler/event_handler_store_test.dart index 7790e27..8a3072f 100644 --- a/test/unit/event/event_handler/event_handler_store_test.dart +++ b/test/unit/event/event_handler/event_handler_store_test.dart @@ -30,26 +30,6 @@ void main() { }); }); - group('registerFactory', () { - test('it registers the factory', () { - expect( - () => - eventHandlerStore.registerFactory(() => MockEventHandler()), - returnsNormally, - ); - }); - - test('it throws when registering the same handler multiple times', () { - MockEventHandler factory() => MockEventHandler(); - - eventHandlerStore.registerFactory(factory); - expect( - () => eventHandlerStore.registerFactory(factory), - throwsAssertionError, - ); - }); - }); - group('unregister', () { test('it unregisters the event handler', () { final handler = MockEventHandler(); @@ -75,43 +55,15 @@ void main() { }); }); - group('unregisterFactory', () { - test('it unregisters the factory', () { - MockEventHandler factory() => MockEventHandler(); - - eventHandlerStore.registerFactory(factory); - - expect( - () => eventHandlerStore.unregisterFactory(factory), - returnsNormally, - ); - }); - - test('it throws when unregistering the same factory multiple times', () { - MockEventHandler factory() => MockEventHandler(); - - eventHandlerStore.registerFactory(factory); - eventHandlerStore.unregisterFactory(factory); - - expect( - () => eventHandlerStore.unregisterFactory(factory), - throwsAssertionError, - ); - }); - }); - group('getHandlersFor{TEvent}', () { test('it returns the registered handlers', () { final handler = MockEventHandler(); - final factoryHandler = MockEventHandler(); - MockEventHandler factory() => factoryHandler; eventHandlerStore.register(handler); - eventHandlerStore.registerFactory(factory); expect( eventHandlerStore.getHandlersFor(), - {handler, factoryHandler}, + {handler}, ); }); }); diff --git a/test/unit/event/event_handler/event_handler_test.dart b/test/unit/event/event_handler/event_handler_test.dart new file mode 100644 index 0000000..e671093 --- /dev/null +++ b/test/unit/event/event_handler/event_handler_test.dart @@ -0,0 +1,60 @@ +import 'package:dart_mediator/event_manager.dart'; +import 'package:dart_mediator/src/event/handler/event_handler.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; + +import '../../../mocks.dart'; + +void main() { + group('EventHandler', () { + group('function', () { + group('handle', () { + test('it handles the event', () { + var handled = false; + final handler = EventHandler.function((event) { + handled = true; + }); + + handler.handle(123); + + expect(handled, isTrue); + }); + }); + + test('is immutable', () { + void handler(int a) {} + + final a = EventHandler.function(handler); + final b = EventHandler.function(handler); + + expect(a, b); + expect(a == b, isTrue); + expect(a.hashCode == b.hashCode, isTrue); + }); + }); + + group('factory', () { + group('handle', () { + test('it handles the event', () { + final mockEventHandler = MockEventHandler(); + final handler = EventHandler.factory(() => mockEventHandler); + + handler.handle(123); + + verify(() => mockEventHandler.handle(123)); + }); + }); + + test('is immutable', () { + EventHandler factory() => MockEventHandler(); + + final a = EventHandler.factory(factory); + final b = EventHandler.factory(factory); + + expect(a, b); + expect(a == b, isTrue); + expect(a.hashCode == b.hashCode, isTrue); + }); + }); + }); +} diff --git a/test/unit/event/event_subscription_builder/event_subscription_builder_test.dart b/test/unit/event/event_subscription_builder/event_subscription_builder_test.dart index f0ea21b..fa66f76 100644 --- a/test/unit/event/event_subscription_builder/event_subscription_builder_test.dart +++ b/test/unit/event/event_subscription_builder/event_subscription_builder_test.dart @@ -94,14 +94,15 @@ void main() { }); group('subscribeFactory', () { - EventHandler handlerFactory() => - EventHandler.function((event) {}); + EventHandler handlerFactory() => MockEventHandler(); + + final factoryHandler = EventHandler.factory(handlerFactory); test('it subscribes the handler', () { EventSubscriptionBuilder.create(mockEventHandlerStore) .subscribeFactory(handlerFactory); - verify(() => mockEventHandlerStore.registerFactory(handlerFactory)); + verify(() => mockEventHandlerStore.register(factoryHandler)); }); test('it return a subscription', () { @@ -123,7 +124,7 @@ void main() { subscription.cancel(); - verify(() => mockEventHandlerStore.unregisterFactory(handlerFactory)); + verify(() => mockEventHandlerStore.unregister(factoryHandler)); }); }); }); diff --git a/test/unit/request/pipeline_behavior/pipeline_behavior_store_test.dart b/test/unit/request/pipeline_behavior/pipeline_behavior_store_test.dart index 775f0c4..817090d 100644 --- a/test/unit/request/pipeline_behavior/pipeline_behavior_store_test.dart +++ b/test/unit/request/pipeline_behavior/pipeline_behavior_store_test.dart @@ -1,4 +1,4 @@ -import 'package:dart_mediator/src/request/pipeline/pipeline_behavior_store.dart'; +import 'package:dart_mediator/request_manager.dart'; import 'package:test/test.dart'; import '../../../mocks.dart'; @@ -29,18 +29,51 @@ void main() { }); }); + group('registerGenericFunction', () { + final behavior = MockPipelineBehavior(); + + test('it registers the generic function behavior', () { + expect( + () => pipelineBehaviorStore.registerGenericFunction(behavior.handle), + returnsNormally, + ); + + expect( + pipelineBehaviorStore.getPipelines(mockRequest), + [PipelineBehavior.function(behavior.handle)], + ); + }); + }); + group('registerFactory', () { - final mockBehavior = MockPipelineBehavior>(); + MockPipelineBehavior> factory() => + MockPipelineBehavior(); - test('it registers the factory handler', () { + test('it registers the factory behavior', () { expect( - () => pipelineBehaviorStore.registerFactory(() => mockBehavior), + () => pipelineBehaviorStore.registerFactory(factory), returnsNormally, ); expect( pipelineBehaviorStore.getPipelines(mockRequest), - [mockBehavior], + [PipelineBehavior.factory(factory)], + ); + }); + }); + + group('registerFunction', () { + final behavior = MockPipelineBehavior>(); + + test('it registers the function behavior', () { + expect( + () => pipelineBehaviorStore.registerFunction(behavior.handle), + returnsNormally, + ); + + expect( + pipelineBehaviorStore.getPipelines(mockRequest), + [PipelineBehavior.function(behavior.handle)], ); }); }); @@ -62,25 +95,23 @@ void main() { }); group('registerGenericFactory', () { - final mockBehavior = MockPipelineBehavior(); + MockPipelineBehavior factory() => MockPipelineBehavior(); test('it registers the handler', () { expect( - () => - pipelineBehaviorStore.registerGenericFactory(() => mockBehavior), + () => pipelineBehaviorStore.registerGenericFactory(factory), returnsNormally, ); expect( pipelineBehaviorStore.getPipelines(mockRequest), - [mockBehavior], + [PipelineBehavior.factory(factory)], ); }); }); group('unregister', () { final mockBehavior = MockPipelineBehavior>(); - final mockGenericBehavior = MockPipelineBehavior>(); test('it unregisters the behavior', () { pipelineBehaviorStore.register(mockBehavior); @@ -95,11 +126,24 @@ void main() { [], ); }); - test('it unregisters the generic behavior', () { - pipelineBehaviorStore.registerGeneric(mockGenericBehavior); + + test('it throws when behavior does not exist', () { + expect( + () => pipelineBehaviorStore.unregister(mockBehavior), + throwsAssertionError, + ); + }); + }); + + group('unregisterFunction', () { + final behavior = MockPipelineBehavior>(); + + test('it unregisters the behavior', () { + pipelineBehaviorStore.registerFunction(behavior.handle); expect( - () => pipelineBehaviorStore.unregister(mockGenericBehavior), + () => pipelineBehaviorStore + .unregister(PipelineBehavior.function(behavior.handle)), returnsNormally, ); @@ -113,15 +157,13 @@ void main() { group('unregisterFactory', () { MockPipelineBehavior> mockBehaviorFactory() => MockPipelineBehavior>(); - MockPipelineBehavior> - mockGenericBehaviorFactory() => - MockPipelineBehavior>(); test('it unregisters the behavior', () { pipelineBehaviorStore.registerFactory(mockBehaviorFactory); expect( - () => pipelineBehaviorStore.unregisterFactory(mockBehaviorFactory), + () => pipelineBehaviorStore + .unregister(PipelineBehavior.factory(mockBehaviorFactory)), returnsNormally, ); @@ -130,13 +172,16 @@ void main() { [], ); }); - test('it unregisters the generic behavior', () { - pipelineBehaviorStore - .registerGenericFactory(mockGenericBehaviorFactory); + }); + + group('unregisterGeneric', () { + final mockGenericBehavior = MockPipelineBehavior(); + + test('it unregisters the behavior', () { + pipelineBehaviorStore.registerGeneric(mockGenericBehavior); expect( - () => pipelineBehaviorStore - .unregisterFactory(mockGenericBehaviorFactory), + () => pipelineBehaviorStore.unregisterGeneric(mockGenericBehavior), returnsNormally, ); @@ -145,6 +190,13 @@ void main() { [], ); }); + + test('it throws when behavior does not exist', () { + expect( + () => pipelineBehaviorStore.unregisterGeneric(mockGenericBehavior), + throwsAssertionError, + ); + }); }); group('getPipelines', () { @@ -161,15 +213,26 @@ void main() { MockPipelineBehavior logBehaviorFactory() => logBehavior; pipelineBehaviorStore.register(correctBehavior); + pipelineBehaviorStore.registerFunction(correctBehavior.handle); pipelineBehaviorStore.registerFactory(correctFactory); pipelineBehaviorStore.registerGeneric(logBehavior); + pipelineBehaviorStore.registerGenericFunction(logBehavior.handle); pipelineBehaviorStore.registerGenericFactory(logBehaviorFactory); + + // Will not be returned in the getPipelines call. pipelineBehaviorStore.register(incorrectBehavior); pipelineBehaviorStore.registerFactory(incorrectFactory); expect( pipelineBehaviorStore.getPipelines(mockRequest), - [correctBehavior, correctBehavior, logBehavior, logBehavior], + [ + correctBehavior, + PipelineBehavior.function(correctBehavior.handle), + PipelineBehavior.factory(correctFactory), + logBehavior, + PipelineBehavior.function(logBehavior.handle), + PipelineBehavior.factory(logBehaviorFactory), + ], ); }); }); diff --git a/test/unit/request/pipeline_behavior/pipeline_behavior_test.dart b/test/unit/request/pipeline_behavior/pipeline_behavior_test.dart new file mode 100644 index 0000000..93abbec --- /dev/null +++ b/test/unit/request/pipeline_behavior/pipeline_behavior_test.dart @@ -0,0 +1,77 @@ +import 'dart:async'; + +import 'package:dart_mediator/request_manager.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; + +import '../../../mocks.dart'; + +void main() { + group('RequestHandler', () { + group('function', () { + group('handle', () { + test('it handles the request', () { + var handled = false; + final mockRequest = MockRequest(); + final handler = + PipelineBehavior>.function((req, next) { + handled = true; + return next(); + }); + + expect(handler.handle(mockRequest, () => 123), 123); + expect(handled, isTrue); + }); + }); + + test('is immutable', () { + FutureOr handler( + Request req, + RequestHandlerDelegate next, + ) { + return next(); + } + + final a = PipelineBehavior.function(handler); + final b = PipelineBehavior.function(handler); + + expect(a, b); + expect(a == b, isTrue); + expect(a.hashCode == b.hashCode, isTrue); + }); + }); + + group('factory', () { + group('handle', () { + test('it handles the request', () { + final mockPipeline = MockPipelineBehavior>(); + PipelineBehavior> factory() => mockPipeline; + + final handler = + PipelineBehavior>.factory(factory); + + int next() => 123; + final request = MockRequest(); + + when(() => mockPipeline.handle(request, next)).thenReturn(123); + + handler.handle(request, next); + + verify(() => mockPipeline.handle(request, next)); + }); + }); + + test('is immutable', () { + PipelineBehavior> factory() => + MockPipelineBehavior(); + + final a = PipelineBehavior.factory(factory); + final b = PipelineBehavior.factory(factory); + + expect(a, b); + expect(a == b, isTrue); + expect(a.hashCode == b.hashCode, isTrue); + }); + }); + }); +} diff --git a/test/unit/request/request_handler/request_handler_store_test.dart b/test/unit/request/request_handler/request_handler_store_test.dart index ecf7cc1..79e768b 100644 --- a/test/unit/request/request_handler/request_handler_store_test.dart +++ b/test/unit/request/request_handler/request_handler_store_test.dart @@ -1,4 +1,3 @@ -import 'package:dart_mediator/src/request/handler/request_handler.dart'; import 'package:dart_mediator/src/request/handler/request_handler_store.dart'; import 'package:test/test.dart'; @@ -8,9 +7,6 @@ void main() { group('RequestHandlerStore', () { late RequestHandlerStore requestHandlerStore; - RequestHandler> handlerFactory() => - MockRequestHandler>(); - final mockRequestHandler = MockRequestHandler>(); setUp(() { @@ -32,48 +28,12 @@ void main() { throwsAssertionError, ); }); - - test('it throws when a factory for this type was already registered', () { - requestHandlerStore.registerFactory(handlerFactory); - - expect( - () => requestHandlerStore.register(mockRequestHandler), - throwsAssertionError, - ); - }); - }); - - group('registerFactory', () { - test('it registers the handler', () { - expect( - () => requestHandlerStore.registerFactory(handlerFactory), - returnsNormally, - ); - }); - - test('it throws when registering the same factory multiple times', () { - requestHandlerStore.registerFactory(handlerFactory); - - expect( - () => requestHandlerStore.registerFactory(handlerFactory), - throwsAssertionError, - ); - }); - - test('it throws when a handler for this type was already registered', () { - requestHandlerStore.register(mockRequestHandler); - - expect( - () => requestHandlerStore.registerFactory(handlerFactory), - throwsAssertionError, - ); - }); }); group('unregister', () { final mockRequestHandler = MockRequestHandler>(); - test('it unsubscribes to the event', () { + test('it unregisters the handler', () { requestHandlerStore.register(mockRequestHandler); expect( @@ -125,19 +85,6 @@ void main() { correctHandler, ); }); - - test('it returns the request handler factory', () { - final mockHandler = MockRequestHandler>(); - MockRequestHandler> correctHandlerFactory() => - mockHandler; - - requestHandlerStore.registerFactory(correctHandlerFactory); - - expect( - requestHandlerStore.getHandlerFor(MockRequest()), - mockHandler, - ); - }); }); }); } diff --git a/test/unit/request/request_handler/request_handler_test.dart b/test/unit/request/request_handler/request_handler_test.dart index a9fb126..9ba0949 100644 --- a/test/unit/request/request_handler/request_handler_test.dart +++ b/test/unit/request/request_handler/request_handler_test.dart @@ -1,23 +1,66 @@ import 'package:dart_mediator/src/request/handler/request_handler.dart'; +import 'package:dart_mediator/src/request/request.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; import '../../../mocks.dart'; void main() { group('RequestHandler', () { - setUp(() {}); - - group('handle', () { - test('it handles the request', () { - var handled = false; - final mockRequest = MockRequest(); - final handler = RequestHandler>.function((req) { - handled = true; - return 123; + group('function', () { + group('handle', () { + test('it handles the request', () { + var handled = false; + final mockRequest = MockRequest(); + final handler = RequestHandler>.function((req) { + handled = true; + return 123; + }); + + expect(handler.handle(mockRequest), 123); + expect(handled, isTrue); }); + }); + + test('is immutable', () { + int handle(MockRequest req) => 123; + + final a = RequestHandler.function(handle); + final b = RequestHandler.function(handle); + + expect(a, b); + expect(a == b, isTrue); + expect(a.hashCode == b.hashCode, isTrue); + }); + }); + + group('factory', () { + group('handle', () { + test('it handles the request', () { + final mockRequest = MockRequest(); + final mockHandler = MockRequestHandler>(); + + when(() => mockHandler.handle(mockRequest)).thenReturn(123); + + MockRequestHandler> factory() => mockHandler; + + final handler = RequestHandler.factory(factory); + + expect(handler.handle(mockRequest), 123); + + verify(() => mockHandler.handle(mockRequest)); + }); + }); + + test('is immutable', () { + RequestHandler> factory() => MockRequestHandler(); + + final a = RequestHandler.factory(factory); + final b = RequestHandler.factory(factory); - expect(handler.handle(mockRequest), 123); - expect(handled, isTrue); + expect(a, b); + expect(a == b, isTrue); + expect(a.hashCode == b.hashCode, isTrue); }); }); }); diff --git a/test/unit/request/requests_manager_test.dart b/test/unit/request/requests_manager_test.dart index 4bfbc57..5ddcc8f 100644 --- a/test/unit/request/requests_manager_test.dart +++ b/test/unit/request/requests_manager_test.dart @@ -1,3 +1,4 @@ +import 'package:dart_mediator/src/request/handler/request_handler.dart'; import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart'; import 'package:dart_mediator/src/request/request_manager.dart'; import 'package:mocktail/mocktail.dart'; @@ -50,29 +51,34 @@ void main() { group('registerFactory', () { test('it registers the handler', () { - MockRequestHandler> - mockRequestHandlerFactory() => - MockRequestHandler>(); + MockRequestHandler> factory() => + MockRequestHandler(); requestsManager.registerFactory>( - mockRequestHandlerFactory, + factory, ); verify( - () => mockRequestHandlerStore - .registerFactory(mockRequestHandlerFactory), + () => mockRequestHandlerStore.register>( + RequestHandler.factory(factory), + ), ); }); }); group('registerFunction', () { test('it registers the handler', () { - requestsManager.registerFunction>( - (request) async => '123', - ); + String handle(MockRequest req) { + return '123'; + } + + requestsManager.registerFunction>(handle); - verify(() => mockRequestHandlerStore - .register>(any())); + verify( + () => mockRequestHandlerStore.register>( + RequestHandler.function(handle), + ), + ); }); });