@Test
  public void testLoadEventsWithDecorators() {
    UUID identifier = UUID.randomUUID();
    SpyEventPreprocessor decorator1 = new SpyEventPreprocessor();
    SpyEventPreprocessor decorator2 = new SpyEventPreprocessor();
    testSubject.setEventStreamDecorators(Arrays.asList(decorator1, decorator2));
    when(mockEventStore.readEvents("test", identifier))
        .thenReturn(
            new SimpleDomainEventStream(
                new GenericDomainEventMessage<String>(
                    identifier, (long) 1, "Mock contents", MetaData.emptyInstance()),
                new GenericDomainEventMessage<String>(
                    identifier, (long) 2, "Mock contents", MetaData.emptyInstance()),
                new GenericDomainEventMessage<String>(
                    identifier, (long) 3, "Mock contents", MetaData.emptyInstance())));
    TestAggregate aggregate = testSubject.load(identifier);
    // loading them in...
    InOrder inOrder = Mockito.inOrder(decorator1.lastSpy, decorator2.lastSpy);
    inOrder.verify(decorator2.lastSpy).next();
    inOrder.verify(decorator1.lastSpy).next();

    inOrder.verify(decorator2.lastSpy).next();
    inOrder.verify(decorator1.lastSpy).next();

    inOrder.verify(decorator2.lastSpy).next();
    inOrder.verify(decorator1.lastSpy).next();
    aggregate.apply(new StubDomainEvent());
    aggregate.apply(new StubDomainEvent());
  }
  @SuppressWarnings("unchecked")
  @Test
  public void testCallbackInvokedBeforeUnitOfWorkCleanup() throws Throwable {
    CommandHandlerInterceptor mockInterceptor = mock(CommandHandlerInterceptor.class);
    ExecutorService customExecutor = Executors.newCachedThreadPool();
    testSubject =
        new DisruptorCommandBus<StubAggregate>(
            new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
            inMemoryEventStore,
            eventBus,
            new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY),
            new DisruptorConfiguration()
                .setInvokerInterceptors(Arrays.asList(mockInterceptor))
                .setClaimStrategy(new SingleThreadedClaimStrategy(8))
                .setWaitStrategy(new SleepingWaitStrategy())
                .setExecutor(customExecutor));
    testSubject.subscribe(StubCommand.class, stubHandler);
    stubHandler.setRepository(testSubject);
    final UnitOfWorkListener mockUnitOfWorkListener = mock(UnitOfWorkListener.class);
    when(mockUnitOfWorkListener.onEventRegistered(any(EventMessage.class)))
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                return invocation.getArguments()[0];
              }
            });
    when(mockInterceptor.handle(
            any(CommandMessage.class), any(UnitOfWork.class), any(InterceptorChain.class)))
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                ((UnitOfWork) invocation.getArguments()[1])
                    .registerListener(mockUnitOfWorkListener);
                return ((InterceptorChain) invocation.getArguments()[2]).proceed();
              }
            });
    CommandMessage<StubCommand> command =
        new GenericCommandMessage<StubCommand>(
            new StubCommand(aggregateIdentifier),
            singletonMap(TARGET_AGGREGATE_PROPERTY, (Object) aggregateIdentifier));
    CommandCallback mockCallback = mock(CommandCallback.class);
    testSubject.dispatch(command, mockCallback);

    testSubject.stop();
    assertFalse(customExecutor.awaitTermination(250, TimeUnit.MILLISECONDS));
    customExecutor.shutdown();
    assertTrue(customExecutor.awaitTermination(5, TimeUnit.SECONDS));
    InOrder inOrder = inOrder(mockInterceptor, mockUnitOfWorkListener, mockCallback);
    inOrder
        .verify(mockInterceptor)
        .handle(any(CommandMessage.class), any(UnitOfWork.class), any(InterceptorChain.class));
    inOrder.verify(mockUnitOfWorkListener).onPrepareCommit(any(Set.class), any(List.class));
    inOrder.verify(mockUnitOfWorkListener).afterCommit();
    inOrder.verify(mockUnitOfWorkListener).onCleanup();

    verify(mockCallback).onSuccess(any());
  }
  @Test
  public void testSaveEventsWithDecorators() {
    SpyEventPreprocessor decorator1 = new SpyEventPreprocessor();
    SpyEventPreprocessor decorator2 = new SpyEventPreprocessor();
    testSubject.setEventStreamDecorators(Arrays.asList(decorator1, decorator2));
    testSubject.setEventStore(
        new EventStore() {
          @Override
          public void appendEvents(String type, DomainEventStream events) {
            while (events.hasNext()) {
              events.next();
            }
          }

          @Override
          public DomainEventStream readEvents(String type, Object identifier) {
            return mockEventStore.readEvents(type, identifier);
          }
        });
    UUID identifier = UUID.randomUUID();
    when(mockEventStore.readEvents("test", identifier))
        .thenReturn(
            new SimpleDomainEventStream(
                new GenericDomainEventMessage<String>(
                    identifier, (long) 3, "Mock contents", MetaData.emptyInstance())));
    TestAggregate aggregate = testSubject.load(identifier);
    aggregate.apply(new StubDomainEvent());
    aggregate.apply(new StubDomainEvent());

    CurrentUnitOfWork.commit();

    InOrder inOrder = Mockito.inOrder(decorator1.lastSpy, decorator2.lastSpy);
    inOrder.verify(decorator1.lastSpy).next();
    inOrder.verify(decorator2.lastSpy).next();

    inOrder.verify(decorator1.lastSpy).next();
    inOrder.verify(decorator2.lastSpy).next();
  }