@Test
  public void whenPostCommitTaskFails_exceptionThrown() {
    Transaction tx = new AbstractTransactionImpl();
    tx.start();

    RuntimeException exception = new RuntimeException();

    TransactionLifecycleListener listener = mock(TransactionLifecycleListener.class);
    doThrow(exception).when(listener).notify(tx, TransactionLifecycleEvent.PostCommit);

    tx.registerLifecycleListener(listener);

    try {
      tx.commit();
      fail();
    } catch (RuntimeException found) {
      assertSame(exception, found);
    }

    assertIsCommitted(tx);
    verify(listener, times(1)).notify(tx, TransactionLifecycleEvent.PreCommit);
    verify(listener, times(1)).notify(tx, TransactionLifecycleEvent.PostCommit);
    verify(listener, times(0)).notify(tx, TransactionLifecycleEvent.PreAbort);
    verify(listener, times(0)).notify(tx, TransactionLifecycleEvent.PostAbort);
  }
  @Test
  public void whenCommitted_thenIgnore() {
    Transaction tx = new AbstractTransactionImpl(clock);
    tx.commit();

    long version = clock.getVersion();
    tx.commit();
    assertIsCommitted(tx);
    assertEquals(version, clock.getVersion());
  }
  @Test
  public void whenActive_listenersAreNotified() {
    Transaction tx = new AbstractTransactionImpl(clock);
    tx.start();

    TransactionLifecycleListener listener = mock(TransactionLifecycleListener.class);
    tx.registerLifecycleListener(listener);
    tx.commit();

    assertIsCommitted(tx);
    verify(listener, times(1)).notify(tx, TransactionLifecycleEvent.PreCommit);
    verify(listener, times(1)).notify(tx, TransactionLifecycleEvent.PostCommit);
    verify(listener, times(0)).notify(tx, TransactionLifecycleEvent.PreAbort);
    verify(listener, times(0)).notify(tx, TransactionLifecycleEvent.PostAbort);
  }
  @Test
  public void whenAborted_thenDeadTransactionException() {
    Transaction tx = new AbstractTransactionImpl(clock);
    tx.abort();

    long version = clock.getVersion();
    try {
      tx.commit();
      fail();
    } catch (DeadTransactionException ex) {
    }

    assertIsAborted(tx);
    assertEquals(version, clock.getVersion());
  }