/*
 * Copyright 2017, TeamDev Ltd. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server.storage.jdbc;

import com.google.protobuf.Message;
import io.spine.server.storage.jdbc.IdColumn;
import org.junit.Test;
import io.spine.server.entity.AbstractEntity;
import io.spine.server.storage.jdbc.DatabaseException;
import io.spine.test.entity.ProjectId;

import java.sql.PreparedStatement;
import java.sql.SQLException;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static io.spine.server.storage.jdbc.Sql.Type.BIGINT;
import static io.spine.server.storage.jdbc.Sql.Type.INT;
import static io.spine.server.storage.jdbc.Sql.Type.VARCHAR_255;

/**
 * @author Dmytro Dashenkov
 */
public class IdColumnShould {

    private final String ID = "id";

    @Test
    public void have_bigint_impl() {
        final IdColumn<?> column = IdColumn.newInstance(LongIdEntity.class, ID);
        assertEquals(BIGINT, column.getSqlType());
    }

    @Test
    public void have_int_impl() {
        final IdColumn<?> column = IdColumn.newInstance(IntIdEntity.class, ID);
        assertEquals(INT, column.getSqlType());
    }

    @Test
    public void have_varchar255_impl() {
        final IdColumn<?> column = IdColumn.newInstance(StringIdEntity.class, ID);
        assertEquals(VARCHAR_255, column.getSqlType());
    }

    @Test
    public void cast_message_IDs_to_string() {
        final IdColumn<?> column = IdColumn.newInstance(MessageIdEntity.class, ID);
        assertEquals(VARCHAR_255, column.getSqlType());
    }

    @Test(expected = DatabaseException.class)
    public void throw_DatabaseException_on_fail_to_set_int_id() throws SQLException {
        final IdColumn<Integer> column = IdColumn.newInstance(IntIdEntity.class, ID);
        column.setId(1, 1, faultyStatement());
    }

    @Test(expected = DatabaseException.class)
    public void throw_DatabaseException_on_fail_to_set_long_id() throws SQLException {
        final IdColumn<Long> column = IdColumn.newInstance(LongIdEntity.class, ID);
        column.setId(1, 1L, faultyStatement());
    }

    @Test(expected = DatabaseException.class)
    public void throw_DatabaseException_on_fail_to_set_string_id() throws SQLException {
        final IdColumn<String> column = IdColumn.newInstance(StringIdEntity.class, ID);
        column.setId(1, "bazinga!", faultyStatement());
    }

    @Test(expected = DatabaseException.class)
    public void throw_DatabaseException_on_fail_to_set_message_id() throws SQLException {
        final IdColumn<ProjectId> column = IdColumn.newInstance(MessageIdEntity.class, ID);
        column.setId(1, ProjectId.getDefaultInstance(), faultyStatement());
    }

    @Test
    public void store_column_name() {
        final IdColumn<String> column = IdColumn.newInstance(StringIdEntity.class, ID);
        assertEquals(ID, column.getColumnName());
    }

    private static PreparedStatement faultyStatement() throws SQLException {
        final PreparedStatement statement = mock(PreparedStatement.class);
        final Exception exception = new SQLException("Faulty statement causes failures");
        doThrow(exception).when(statement).setString(anyInt(), anyString());
        doThrow(exception).when(statement).setLong(anyInt(), anyLong());
        doThrow(exception).when(statement).setInt(anyInt(), anyInt());
        return statement;
    }

    private static class LongIdEntity extends AbstractEntity<Long, Message> {
        protected LongIdEntity(Long id) {
            super(id);
        }
    }

    private static class IntIdEntity extends AbstractEntity<Integer, Message> {
        protected IntIdEntity(Integer id) {
            super(id);
        }
    }

    private static class StringIdEntity extends AbstractEntity<String, Message> {
        protected StringIdEntity(String id) {
            super(id);
        }
    }

    private static class MessageIdEntity extends AbstractEntity<ProjectId, Message> {
        protected MessageIdEntity(ProjectId id) {
            super(id);
        }
    }
}

@@ -1112,153 +1112,29 @@
 ver.
-storage.jdbc.IdColumn;%0Aimport org.junit.Test;%0Aimport io.spine.server.entity.AbstractEntity;%0Aimport io.spine.server.storage.jdbc.DatabaseException
+entity.AbstractEntity
 ;%0Aim
@@ -1169,16 +1169,39 @@
 ojectId;
+%0Aimport org.junit.Test;
 %0A%0Aimport
@@ -1278,37 +1278,305 @@
 tic 
-org.junit.Assert.assertEquals
+io.spine.server.storage.jdbc.Sql.Type.BIGINT;%0Aimport static io.spine.server.storage.jdbc.Sql.Type.INT;%0Aimport static io.spine.server.storage.jdbc.Sql.Type.VARCHAR_255;%0Aimport static org.junit.Assert.assertEquals;%0Aimport static org.junit.Assert.assertSame;%0Aimport static org.junit.Assert.assertTrue
 ;%0Aim
@@ -1816,190 +1816,8 @@
 ock;
-%0Aimport static io.spine.server.storage.jdbc.Sql.Type.BIGINT;%0Aimport static io.spine.server.storage.jdbc.Sql.Type.INT;%0Aimport static io.spine.server.storage.jdbc.Sql.Type.VARCHAR_255;
 %0A%0A/*
@@ -1893,16 +1893,23 @@
 private 
+static 
 final St
@@ -2096,32 +2096,86 @@
 .getSqlType());%0A
+        assertSame(Long.class, column.getJavaType());%0A
     %7D%0A%0A    @Test
@@ -2329,32 +2329,89 @@
 .getSqlType());%0A
+        assertSame(Integer.class, column.getJavaType());%0A
     %7D%0A%0A    @Test
@@ -2583,32 +2583,88 @@
 .getSqlType());%0A
+        assertSame(String.class, column.getJavaType());%0A
     %7D%0A%0A    @Test
@@ -2843,32 +2843,106 @@
 .getSqlType());%0A
+        assertTrue(Message.class.isAssignableFrom(column.getJavaType()));%0A
     %7D%0A%0A    @Test

Add some more checks for the IdColumn impls.