package org.apache.torque.util;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;

import org.apache.torque.BaseTestCase;
import org.apache.torque.Torque;
import org.apache.torque.criteria.Criteria;
import org.apache.torque.map.ColumnMap;
import org.apache.torque.map.DatabaseMap;
import org.apache.torque.map.TableMap;
import org.apache.torque.om.mapper.CompositeMapper;
import org.apache.torque.om.mapper.IntegerMapper;
import org.apache.torque.om.mapper.StringMapper;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

/**
 * Tests the class BasePeerImpl.
 *
 * @version $Id: BasePeerImplTest.java 1448414 2013-02-20 21:06:35Z tfischer $
 */
public class BasePeerImplTest extends BaseTestCase
{
    /** System under test. */
    private BasePeerImpl<?> basePeerImpl;

    /** Mock db connection. */
    @Mock
    private Connection connection;

    /** Mock prepared statement. */
    @Mock
    private PreparedStatement preparedStatement;

    /** Mock result set. */
    @Mock
    private ResultSet resultSet;

    @Override
    public void setUp() throws Exception
    {
        basePeerImpl = new BasePeerImpl<Object>();
        super.setUp();
        basePeerImpl.setTableMap(tableMap);
        basePeerImpl.setDatabaseName("databaseName");
        MockitoAnnotations.initMocks(this);
        when(connection.prepareStatement((String) any()))
                .thenReturn(preparedStatement);
        when(preparedStatement.executeQuery()).thenReturn(resultSet);
        when(preparedStatement.executeUpdate()).thenReturn(1);
    }

    /**
     * Check that the fetch size gets set on the prepared statement when
     * set in the criteria.
     *
     * @throws Exception if the test fails.
     */
    public void testDoSelectAll() throws Exception
    {
        Criteria criteria = new Criteria();
        criteria.addSelectColumn(stringColumnMap);
        criteria.addSelectColumn(integerColumnMap);
        // single result "fd", 23 found
        when(resultSet.next()).thenReturn(true, false);
        when(resultSet.getString(1)).thenReturn("fd");
        when(resultSet.getInt(2)).thenReturn(23);

        CompositeMapper mapper = new CompositeMapper();
        mapper.addMapper(new StringMapper(), 0);
        mapper.addMapper(new IntegerMapper(), 1);

        List<List<Object>> result = basePeerImpl.doSelect(
                criteria, mapper, connection);

        // verify mock (verification order not relevant)
        verify(connection).prepareStatement(
                "SELECT TABLE.COLUMN1, TABLE.COLUMN4 FROM TABLE");
        verify(preparedStatement).executeQuery();
        verify(preparedStatement).close();
        verify(resultSet, times(2)).next();
        verify(resultSet).getString(1);
        verify(resultSet).getInt(2);
        verify(resultSet).close();
        verifyNoMoreInteractions(connection, preparedStatement, resultSet);
        // verify result
        assertEquals(1, result.size()); // one row
        assertEquals(2, result.get(0).size()); // two columns
        assertEquals("fd", result.get(0).get(0));
        assertEquals(23, result.get(0).get(1));
    }

    /**
     * Check that the fetch size gets set on the prepared statement when
     * set in the criteria.
     *
     * @throws Exception if the test fails.
     */
    public void testDoSelectAllFetchSize() throws Exception
    {
        Criteria criteria = new Criteria();
        criteria.setFetchSize(13);
        // no results found
        when(resultSet.next()).thenReturn(false);

        basePeerImpl.doSelect(criteria, new IntegerMapper(), connection);
        verify(preparedStatement).setFetchSize(13);
    }

    public void testDeleteWithQualifiedTableName()
            throws Exception
    {
        // prepare
        DatabaseMap fullyQualifiedDatatabaseMap
            = Torque.getDatabaseMap("fullyQualifiedDatatabaseMap");
        TableMap tableMap = new TableMap(
                "schema.fully_qualified_table",
                fullyQualifiedDatatabaseMap);
        ColumnMap columnMap = new ColumnMap("column", tableMap);
        basePeerImpl.setTableMap(tableMap);
        Criteria criteria = new Criteria();
        criteria.where(columnMap, 42);

        // execute
        int deleteCount = basePeerImpl.doDelete(criteria, connection);

        // verify mock
        verify(connection).prepareStatement(
                "DELETE FROM schema.fully_qualified_table "
                + "WHERE schema.fully_qualified_table.column=?");
        verify(preparedStatement).setInt(1, 42);
        verify(preparedStatement).executeUpdate();
        verify(preparedStatement).close();
        verifyNoMoreInteractions(connection, preparedStatement, resultSet);

        // verify result
        assertEquals(1, deleteCount);
    }

}
