package com.mockobjects.examples.mailinglist;

import java.sql.SQLException;
import com.mockobjects.sql.*;
import com.mockobjects.util.TestCaseMo;

public class TestMailingList extends TestCaseMo {
    public TestMailingList(String name) {
        super(name);
    }

    public final static String[] COLUMN_NAMES = new String[] {"email_address", "name"};

    public final static String EMAIL = "fred.bloggs@an.address";
    public final static String NAME = "Fred Bloggs";
    public final static String[] ONE_ROW = new String[] {EMAIL, NAME};
    public final static String[][] TWO_ROWS =
               new String[][] { ONE_ROW, new String[] {EMAIL + 2, NAME + 2}};


    private MailingList list = new MailingList();
    private MockListAction mockListAction = new MockListAction();
    private MockConnection mockConnection = new MockConnection();
    private MockPreparedStatement mockPreparedStatement = new MockPreparedStatement();
    private MockStatement mockStatement = new MockStatement();

    public void setUp() {
        mockConnection.setupAddPreparedStatement(mockPreparedStatement);
        mockConnection.setupStatement(mockStatement);
    }

    public void testAddNewMember() throws MailingListException, SQLException {
        setExpectationsForAddMember();

        list.addMember(mockConnection, EMAIL, NAME);

        verifyJDBC();
    }

    public void testAddExistingMember() throws SQLException {
        mockPreparedStatement.setupThrowExceptionOnExecute(
                new SQLException("MockStatment", "Duplicate",
                                 DatabaseConstants.UNIQUE_CONSTRAINT_VIOLATED));

        setExpectationsForAddMember();

        try {
          list.addMember(mockConnection, EMAIL, NAME);
          fail("Should have thrown exception");
        } catch (MailingListException ignored) {
        }
        verifyJDBC();
    }

    public void testPrepareStatementFailsForAdd() throws MailingListException {
        mockConnection.setupThrowExceptionOnPrepareOrCreate(new SQLException("MockConnection"));

        mockConnection.addExpectedPreparedStatementString(MailingList.INSERT_SQL);
        mockPreparedStatement.setExpectedExecuteCalls(0);
        mockPreparedStatement.setExpectedCloseCalls(0);

        try {
            list.addMember(mockConnection, EMAIL, NAME);
            fail("Should have thrown exception");
        } catch (SQLException expected) {
        }
        verifyJDBC();
    }

    public void testRemoveMember() throws MailingListException, SQLException {
        mockPreparedStatement.setupUpdateCount(1);

        setExpectationsForRemoveMember();

        list.removeMember(mockConnection, EMAIL);

        verifyJDBC();
    }

    public void testRemoveMissingMember() throws SQLException {
        mockPreparedStatement.setupUpdateCount(0);

        setExpectationsForRemoveMember();

        try {
            list.removeMember(mockConnection, EMAIL);
            fail("Should have thrown exception");
        } catch (MailingListException expected) {
        }

        verifyJDBC();
    }

    public void testListNoMembers() throws MailingListException, SQLException {
        MockMultiRowResultSet mockResultSet = makeMultiRowResultSet();

        mockResultSet.setExpectedNextCalls(1);
        mockListAction.setExpectNoMembers();
        setExpectationsForListMembers();

        list.applyToAllMembers(mockConnection, mockListAction);

        verifyJDBC();
        mockResultSet.verify();
        mockListAction.verify();
    }

    public void testListOneMember() throws MailingListException, SQLException {
        MockSingleRowResultSet mockResultSet = new MockSingleRowResultSet();
        mockStatement.setupResultSet(mockResultSet);

        mockResultSet.addExpectedNamedValues(COLUMN_NAMES, ONE_ROW);
        mockResultSet.setExpectedNextCalls(2);
        mockListAction.addExpectedMember(EMAIL, NAME);
        setExpectationsForListMembers();

        list.applyToAllMembers(mockConnection, mockListAction);

        verifyJDBC();
        mockResultSet.verify();
        mockListAction.verify();
    }

    public void testListTwoMembers() throws MailingListException, SQLException {
        MockMultiRowResultSet mockResultSet = makeMultiRowResultSet();
        mockResultSet.setupRows(TWO_ROWS);

        mockResultSet.setExpectedNextCalls(3);
        mockListAction.setExpectedMemberCount(2);

        setExpectationsForListMembers();

        list.applyToAllMembers(mockConnection, mockListAction);

        verifyJDBC();
        mockResultSet.verify();
        mockListAction.verify();
    }

    public void testListmembersFailure() throws SQLException {
        MockMultiRowResultSet mockResultSet = makeMultiRowResultSet();
        mockResultSet.setupRows(TWO_ROWS);
        mockListAction.setupThrowExceptionOnMember(new MailingListException());

        mockResultSet.setExpectedNextCalls(1);
        setExpectationsForListMembers();

        try {
            list.applyToAllMembers(mockConnection, mockListAction);
            fail("Should have thrown exception");
        } catch (MailingListException expected) {
        }
        mockResultSet.verify();
        mockListAction.verify();
    }

    public void testListResultSetFailure() throws MailingListException {
        MockMultiRowResultSet mockResultSet = makeMultiRowResultSet();
        mockResultSet.setupRows(TWO_ROWS);
        mockResultSet.setupThrowExceptionOnGet(new SQLException("Mock Exception"));

        mockResultSet.setExpectedNextCalls(1);
        mockListAction.setExpectNoMembers();
        setExpectationsForListMembers();

        try {
            list.applyToAllMembers(mockConnection, mockListAction);
            fail("Should have thrown exception");
        } catch (SQLException expected) {
        }
        mockResultSet.verify();
        mockListAction.verify();
    }

    public void testListStatementFailure() throws MailingListException {
        mockStatement.setupThrowExceptionOnExecute(new SQLException("Mock exception"));
        mockListAction.setExpectNoMembers();
        setExpectationsForListMembers();

        try {
            list.applyToAllMembers(mockConnection, mockListAction);
            fail("Should have thrown exception");
        } catch (SQLException expected) {
        }
        mockListAction.verify();
    }

    public void testListConnectionFailure() throws MailingListException {
        mockConnection.setupThrowExceptionOnPrepareOrCreate(new SQLException("Mock Exception"));

        mockConnection.setExpectedCreateStatementCalls(1);
        mockStatement.setExpectedCloseCalls(0);
        mockStatement.setExpectedExecuteCalls(0);

        mockListAction.setExpectNoMembers();

        try {
            list.applyToAllMembers(mockConnection, mockListAction);
            fail("Should have thrown exception");
        } catch (SQLException expected) {
        }
        mockListAction.verify();
    }

    private MockMultiRowResultSet makeMultiRowResultSet() {
        MockMultiRowResultSet mockResultSet = new MockMultiRowResultSet();
        mockStatement.setupResultSet(mockResultSet);
        mockResultSet.setupColumnNames(COLUMN_NAMES);
        return mockResultSet;
    }

    private void setExpectationsForListMembers() {
        mockConnection.setExpectedCreateStatementCalls(1);
        mockStatement.setExpectedQueryString(MailingList.LIST_SQL);
        mockStatement.setExpectedCloseCalls(1);
        mockStatement.setExpectedExecuteCalls(1);
    }

    private void setExpectationsForAddMember() {
        setExpectationsForPreparedStatement(MailingList.INSERT_SQL);
        mockPreparedStatement.addExpectedSetParameters(ONE_ROW);
    }

    private void setExpectationsForRemoveMember() {
        setExpectationsForPreparedStatement(MailingList.DELETE_SQL);
        mockPreparedStatement.addExpectedSetParameters(new Object[] {EMAIL});
    }

    private void setExpectationsForPreparedStatement(String sqlStatement) {
        mockConnection.addExpectedPreparedStatementString(sqlStatement);
        mockPreparedStatement.setExpectedExecuteCalls(1);
        mockPreparedStatement.setExpectedCloseCalls(1);
    }

    private void verifyJDBC() {
        mockConnection.verify();
        mockStatement.verify();
        mockPreparedStatement.verify();
    }
}
