package com.atlassian.stash.internal.crowd;

import org.hibernate.CacheMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class TransactionManagerBatchProcessorTest {

    @InjectMocks
    private TransactionManagerBatchProcessor batchProcessor;
    @Mock
    private Session session;
    @Mock
    private SessionFactory sessionFactory;
    @Mock
    private BatchTransaction transaction;
    @Mock
    private BatchTransactionSupplier transactionSupplier;

    @Before
    public void setup() {
        when(sessionFactory.getCurrentSession()).thenReturn(session);
        when(transactionSupplier.get()).thenReturn(transaction);
    }

    @Test
    public void testAfterProcessBatch() {
        batchProcessor.beforeProcessBatch();
        verifyTransactionStarted();

        batchProcessor.afterProcessBatch();
        verifyTransactionCommitted();
    }

    @Test(expected = IllegalStateException.class)
    public void testAfterProcessBatchWhileNotInTx() {
        batchProcessor.afterProcessBatch();
    }

    @Test
    public void testAfterProcessCollection() {
        batchProcessor.afterProcessCollection();

        verifyZeroInteractions(sessionFactory, transactionSupplier, transaction, session);
    }

    @Test
    public void testAfterProcessIndividual() {
        batchProcessor.beforeProcessIndividual();
        verifyTransactionStarted();

        batchProcessor.afterProcessIndividual();
        verifyTransactionCommitted();
    }

    @Test(expected = IllegalStateException.class)
    public void testAfterProcessIndividualWhileNotInTx() {
        batchProcessor.afterProcessIndividual();
    }

    @Test
    public void testBeforeProcessCollection() {
        batchProcessor.beforeProcessCollection();

        verifyZeroInteractions(sessionFactory, transactionSupplier, transaction, session);
    }

    @Test
    public void testBeforeProcessBatch() {
        batchProcessor.beforeProcessBatch();

        verifyTransactionStarted();
        verifyNoMoreInteractions(transactionSupplier, sessionFactory, session, transaction);
    }

    @Test(expected = IllegalStateException.class)
    public void testBeforeProcessIndividualWhileInTx() {
        batchProcessor.beforeProcessIndividual();
        batchProcessor.beforeProcessIndividual();
    }

    @Test
    public void testBeforeProcessIndividual() {
        batchProcessor.beforeProcessIndividual();

        verifyTransactionStarted();
        verifyNoMoreInteractions(transactionSupplier, sessionFactory, session, transaction);
    }

    @Test(expected = IllegalStateException.class)
    public void testBeforeProcessBatchWhileInTx() {
        batchProcessor.beforeProcessBatch();
        batchProcessor.beforeProcessBatch();
    }

    @Test
    public void testGetSession() {
        assertSame(session, batchProcessor.getSession());

        verify(sessionFactory).getCurrentSession();
        verifyNoMoreInteractions(session, sessionFactory);
    }

    @Test
    public void testRollbackProcessBatch() {
        batchProcessor.beforeProcessBatch();
        verifyTransactionStarted();

        batchProcessor.rollbackProcessBatch();
        verifyTransactionRolledBack();
    }

    @Test(expected = IllegalStateException.class)
    public void testRollbackProcessBatchWhileNotInTx() {
        batchProcessor.afterProcessBatch();
    }

    @Test
    public void testRollbackProcessIndividual() {
        batchProcessor.beforeProcessIndividual();
        verifyTransactionStarted();

        batchProcessor.rollbackProcessIndividual();
        verifyTransactionRolledBack();
    }

    @Test(expected = IllegalStateException.class)
    public void testRollbackProcessIndividualWhileNotInTx() {
        batchProcessor.afterProcessIndividual();
    }

    private void verifyTransactionCommitted() {
        verify(transaction).commit();
    }

    private void verifyTransactionRolledBack() {
        verify(transaction).rollback();
    }

    private void verifyTransactionStarted() {
        verify(transactionSupplier).get();
        verify(sessionFactory).getCurrentSession();
        verify(session).setCacheMode(eq(CacheMode.IGNORE));
    }
}
