/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2012 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id: $
 * --------------------------------------------------------------------------
 */


package org.ow2.jonas.jndi.interceptors.impl.datasource;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;
import javax.transaction.TransactionManager;

import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.ow2.jonas.jndi.checker.api.IResourceChecker;
import org.ow2.jonas.jndi.checker.api.IResourceCheckerInfo;
import org.ow2.jonas.jndi.checker.api.IResourceCheckerManager;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

/**
 * A {@code DatasourceWrapperTestCase} is ...
 *
 * @author Guillaume Sauthier
 */
public class DatasourceWrapperTestCase {

    @Mock
    private DataSource delegate;

    @Mock
    private IResourceCheckerManager manager;

    @Mock
    private TransactionManager transactionManager;

    @Mock
    private Connection connection;

    @Mock
    private IResourceCheckerInfo info;

    @BeforeMethod
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetConnectionEnlistsTheResourceAndReturnsAProxiedConnection() throws Exception {
        when(delegate.getConnection()).thenReturn(connection);

        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);

        Connection proxy = wrapper.getConnection();

        verify(manager).enlistResource(any(IResourceChecker.class));

        proxy.commit();
        verify(connection).commit();

    }

    @Test
    public void testGetConnectionWithUsernamePasswordEnlistsTheResourceAndReturnsAProxiedConnection() throws Exception {
        when(delegate.getConnection("john", "doe")).thenReturn(connection);

        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);

        Connection proxy = wrapper.getConnection("john", "doe");

        verify(manager).enlistResource(any(IResourceChecker.class));

        proxy.commit();
        verify(connection).commit();

    }

    @Test
    public void testThatResourceCheckerActuallyClosesTheConnectionWhenForced() throws Exception {
        when(delegate.getConnection()).thenReturn(connection);

        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.setForceClose(true);

        wrapper.getConnection();

        ArgumentCaptor<IResourceChecker> captor = ArgumentCaptor.forClass(IResourceChecker.class);
        verify(manager).enlistResource(captor.capture());

        captor.getValue().detect(info);
        verify(connection).close();
        verify(manager).delistResource(captor.getValue());

    }

    @Test
    public void testThatResourceCheckerDoesNotClosesTheConnectionWhenNotForcedTo() throws Exception {
        when(delegate.getConnection()).thenReturn(connection);

        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.setForceClose(false);

        wrapper.getConnection();

        ArgumentCaptor<IResourceChecker> captor = ArgumentCaptor.forClass(IResourceChecker.class);
        verify(manager).enlistResource(captor.capture());

        captor.getValue().detect(info);
        verifyZeroInteractions(connection);
        verify(manager).delistResource(captor.getValue());

    }

    @Test
    public void testGetLogWriterIsDelegated() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.getLogWriter();
        verify(delegate).getLogWriter();
    }

    @Test
    public void testSetLogWriter() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.setLogWriter(null);
        verify(delegate).setLogWriter(null);
    }

    @Test
    public void testSetLoginTimeout() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.setLoginTimeout(10);
        verify(delegate).setLoginTimeout(10);
    }

    @Test
    public void testGetLoginTimeout() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.getLoginTimeout();
        verify(delegate).getLoginTimeout();
    }

    @Test
    public void testGetMapperName() throws Exception {
        MockDataSource ds = newDelegate();
        DatasourceWrapper wrapper = new DatasourceWrapper(ds, manager, transactionManager);
        assertEquals(wrapper.getMapperName(), "UnderTest");
    }

    private MockDataSource newDelegate() {
        return new MockDataSource();
    }

    @Test
    public void testEqualsSameObject() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        assertTrue(wrapper.equals(wrapper));
    }

    @Test
    public void testEqualsSameUnderlyingObject() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        DatasourceWrapper wrapper2 = new DatasourceWrapper(delegate, manager, transactionManager);
        assertTrue(wrapper.equals(wrapper2));
    }

    @Test
    public void testEqualsDifferentUnderlyingObjects() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        DatasourceWrapper wrapper2 = new DatasourceWrapper(mock(DataSource.class), manager, transactionManager);
        assertFalse(wrapper.equals(wrapper2));
    }

    @Test
    public void testHashCode() throws Exception {

    }

    @Test
    public void testIsWrapperFor() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.isWrapperFor(Integer.class);
        verify(delegate).isWrapperFor(Integer.class);
    }

    @Test
    public void testUnwrap() throws Exception {
        DatasourceWrapper wrapper = new DatasourceWrapper(delegate, manager, transactionManager);
        wrapper.unwrap(Integer.class);
        verify(delegate).unwrap(Integer.class);

    }

    private class MockDataSource implements DataSource {

        boolean getMapperNameCalled = false;

        public String getMapperName() {
            getMapperNameCalled = true;
            return "UnderTest";
        }

        public Connection getConnection() throws SQLException {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        public Connection getConnection(String s, String s1) throws SQLException {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        public PrintWriter getLogWriter() throws SQLException {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        public void setLogWriter(PrintWriter printWriter) throws SQLException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void setLoginTimeout(int i) throws SQLException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public int getLoginTimeout() throws SQLException {
            return 0;  //To change body of implemented methods use File | Settings | File Templates.
        }

        public <T> T unwrap(Class<T> tClass) throws SQLException {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        public boolean isWrapperFor(Class<?> aClass) throws SQLException {
            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }
    }
}
