/**
 * Copyright 2017 Palantir Technologies
 *
 * Licensed under the BSD-3 License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/BSD-3-Clause
 *
 * 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.
 */
package com.palantir.atlasdb.transaction.impl;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.palantir.atlasdb.keyvalue.api.BatchColumnRangeSelection;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.ColumnRangeSelection;
import com.palantir.atlasdb.keyvalue.api.ColumnSelection;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
import com.palantir.atlasdb.keyvalue.api.TableReference;
import com.palantir.atlasdb.protos.generated.TableMetadataPersistence;
import com.palantir.common.base.Throwables;

public class ReadTransactionShould {

    private static final TableReference DUMMY_CONSERVATIVE_TABLE =
            TableReference.createWithEmptyNamespace("dummy-conservative");
    private static final TableReference DUMMY_THOROUGH_TABLE =
            TableReference.createWithEmptyNamespace("dummy-thorough");
    private static final Cell DUMMY_CELL = Cell.create("row".getBytes(), "col".getBytes());
    private static final byte[] EMPTY_BYTES = "".getBytes();

    private static final Map<String, Object[]> simpleGets = ImmutableMap.<String, Object[]>builder()
            .put("get", new Object[] {DUMMY_THOROUGH_TABLE, ImmutableSet.of(DUMMY_CELL)})
            .put("getRows", new Object[] {DUMMY_THOROUGH_TABLE, ImmutableList.of(EMPTY_BYTES), ColumnSelection.all()})
            .put("getRange", new Object[] {DUMMY_THOROUGH_TABLE, RangeRequest.all()})
            .put("getRanges", new Object[] {DUMMY_THOROUGH_TABLE, ImmutableList.of(RangeRequest.all())})
            .build();

    private ReadTransaction readTransaction;
    private AbstractTransaction delegateTransaction;

    @Before
    public void setUp() throws Exception {
        delegateTransaction = Mockito.mock(AbstractTransaction.class);
        SweepStrategyManager sweepStrategies = Mockito.mock(SweepStrategyManager.class);
        when(sweepStrategies.get()).thenReturn(ImmutableMap.of(
                DUMMY_CONSERVATIVE_TABLE,
                TableMetadataPersistence.SweepStrategy.CONSERVATIVE,
                DUMMY_THOROUGH_TABLE,
                TableMetadataPersistence.SweepStrategy.THOROUGH));
        readTransaction = new ReadTransaction(delegateTransaction, sweepStrategies);
    }

    @Test
    public void notAllowPuts() {
        checkThrowsAndNoInteraction(() -> readTransaction.put(
                DUMMY_CONSERVATIVE_TABLE,
                ImmutableMap.of(DUMMY_CELL, "value".getBytes())),
                IllegalArgumentException.class,
                "is a read only transaction");
    }

    @Test
    public void notAllowDeletes() {
        checkThrowsAndNoInteraction(() -> readTransaction.delete(
                DUMMY_CONSERVATIVE_TABLE,
                ImmutableSet.of(DUMMY_CELL)),
                IllegalArgumentException.class,
                "is a read only transaction");
    }

    @Test
    public void allowGetsOnConservativeTables() {
        ImmutableSet<Cell> cellToGet = ImmutableSet.of(DUMMY_CELL);
        readTransaction.get(DUMMY_CONSERVATIVE_TABLE, cellToGet);
        Mockito.verify(delegateTransaction, times(1)).get(eq(DUMMY_CONSERVATIVE_TABLE), eq(cellToGet));
    }

    @Test
    public void notAllowGetsOnThoroughTables() {
        ImmutableSet<Cell> cellToGet = ImmutableSet.of(DUMMY_CELL);
        checkThrowsAndNoInteraction(() -> readTransaction.get(DUMMY_THOROUGH_TABLE, cellToGet),
                IllegalStateException.class,
                "Cannot read");
    }

    @Test
    public void notAllowSimpleGets() throws IllegalAccessException {
        Method[] declaredMethods = ReadTransaction.class.getDeclaredMethods();

        for (Method m : declaredMethods) {
            if (simpleGets.containsKey(m.getName())) {
                checkThrowsAndNoInteraction(() -> {
                            try {
                                m.invoke(readTransaction, simpleGets.get(m.getName()));
                            } catch (InvocationTargetException e) {
                                Throwables.throwIfInstance(e.getCause(), IllegalStateException.class);
                            } catch (IllegalAccessException e) {
                                Throwables.throwUncheckedException(e);
                            }
                        },
                        IllegalStateException.class,
                        "Cannot read");
            }
        }
    }

    @Test
    public void notAllowColumnRangeGets() {
        checkThrowsAndNoInteraction(() -> readTransaction.getRowsColumnRange(
                DUMMY_THOROUGH_TABLE,
                ImmutableList.of(EMPTY_BYTES),
                BatchColumnRangeSelection.create(EMPTY_BYTES, EMPTY_BYTES, 1)),
                IllegalStateException.class,
                "Cannot read");
    }

    @Test
    public void notAllowColumnRangeGets2() {
        checkThrowsAndNoInteraction(() -> readTransaction.getRowsColumnRange(
                DUMMY_THOROUGH_TABLE,
                ImmutableList.of(EMPTY_BYTES),
                new ColumnRangeSelection(EMPTY_BYTES, EMPTY_BYTES),
                1),
                IllegalStateException.class,
                "Cannot read");
    }

    private void checkThrowsAndNoInteraction(Runnable thrower,
            Class<? extends Exception> exception,
            String errorMessage) {
        try {
            thrower.run();
            Assert.fail();
        } catch (Exception e) {
            Assert.assertThat(e, is(instanceOf(exception)));
            Assert.assertThat(e.getMessage(), containsString(errorMessage));
            verifyZeroInteractions(delegateTransaction);
        }
    }
}

@@ -4396,24 +4396,30 @@
 oid notAllow
+Simple
 GetsOnThorou
@@ -4432,305 +4432,8 @@
 es()
- %7B%0A        ImmutableSet%3CCell%3E cellToGet = ImmutableSet.of(DUMMY_CELL);%0A        checkThrowsAndNoInteraction(() -%3E readTransaction.get(DUMMY_THOROUGH_TABLE, cellToGet),%0A                IllegalStateException.class,%0A                %22Cannot read%22);%0A    %7D%0A%0A    @Test%0A    public void notAllowSimpleGets()
  thr
@@ -5327,32 +5327,37 @@
 ic void notAllow
+Batch
 ColumnRangeGets(
@@ -5736,17 +5736,16 @@
 angeGets
-2
 () %7B%0A   

Update test names in accordance with comments