package com.atlassian.stash.internal.ao;

import com.atlassian.stash.internal.spring.TransactionSynchronizer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.transaction.support.TransactionSynchronization;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class SpringTransactionSynchronisationManagerTest {

    @Mock
    private Runnable callback;
    @InjectMocks
    private SpringTransactionSynchronisationManager manager;
    @Captor
    private ArgumentCaptor<TransactionSynchronization> synchronizationCaptor;
    @Mock
    private TransactionSynchronizer synchronizer;

    @Test
    public void testIsActiveSynchronisedTransaction() {
        when(synchronizer.isAvailable()).thenReturn(true);

        assertTrue(manager.isActiveSynchronisedTransaction());

        verify(synchronizer).isAvailable();
    }

    @Test
    public void testRunOnRollBack() throws Exception {
        when(synchronizer.register(isNotNull(TransactionSynchronization.class))).thenReturn(true);

        assertTrue(manager.runOnRollBack(callback));
        verify(synchronizer).register(synchronizationCaptor.capture());

        TransactionSynchronization sync = synchronizationCaptor.getValue();
        sync.afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
        verifyZeroInteractions(callback);
        sync.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
        verifyZeroInteractions(callback);
        sync.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
        verify(callback).run();
    }

    @Test
    public void testRunOnRollBackWhenSynchronizationNotAvailable() throws Exception {
        assertFalse(manager.runOnRollBack(callback));
    }

    @Test
    public void testRunOnSuccessfulCommit() throws Exception {
        when(synchronizer.register(isNotNull(TransactionSynchronization.class))).thenReturn(true);

        assertTrue(manager.runOnSuccessfulCommit(callback));
        verify(synchronizer).register(synchronizationCaptor.capture());

        TransactionSynchronization sync = synchronizationCaptor.getValue();
        sync.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
        verifyZeroInteractions(callback);
        sync.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
        verifyZeroInteractions(callback);
        sync.afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
        verify(callback).run();
    }

    @Test
    public void testRunOnSuccessfulCommitWhenSynchronizationNotAvailable() throws Exception {
        assertFalse(manager.runOnSuccessfulCommit(callback));
    }
}
