/*
 * Copyright 2018 Confluent Inc.
 *
 * Licensed under the Confluent Community 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://www.confluent.io/confluent-community-license
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 */
package io.confluent.kafkarest.unit;

import org.easymock.EasyMock;
import org.junit.Test;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;

import io.confluent.kafkarest.BinaryConsumerState;
import io.confluent.kafkarest.TestUtils;
import io.confluent.kafkarest.Versions;
import io.confluent.kafkarest.entities.ConsumerInstanceConfig;
import io.confluent.kafkarest.entities.CreateConsumerInstanceResponse;
import io.confluent.rest.RestConfigException;
import io.confluent.rest.exceptions.RestNotFoundException;

import static io.confluent.kafkarest.TestUtils.assertErrorResponse;
import static io.confluent.kafkarest.TestUtils.assertOKResponse;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

// This test evaluates non-consume functionality for ConsumerResource, exercising functionality
// that isn't really dependent on the EmbeddedFormat, e.g. invalid requests, commit offsets,
// delete consumer. It ends up using the BINARY default since it is sometimes required to make
// those operations possible, but the operations shouldn't be affected by it. See
// ConsumerResourceBinaryTest and ConsumerResourceAvroTest for format-specific tests.
public class ConsumerResourceTest extends AbstractConsumerResourceTest {

  public ConsumerResourceTest() throws RestConfigException {
    super();
  }

  @Test
  public void testCreateInstanceRequestsNewInstance() {
    for (TestUtils.RequestMediaType mediatype : TestUtils.V1_ACCEPT_MEDIATYPES) {
      for (String requestMediatype : TestUtils.V1_REQUEST_ENTITY_TYPES) {
        expectCreateGroup(new ConsumerInstanceConfig());
        EasyMock.replay(consumerManager);

        Response response = request("/consumers/" + groupName, mediatype.header)
            .post(Entity.entity(null, requestMediatype));
        assertOKResponse(response, mediatype.expected);
        final CreateConsumerInstanceResponse ciResponse =
                TestUtils.tryReadEntityOrLog(response, CreateConsumerInstanceResponse.class);
        assertEquals(instanceId, ciResponse.getInstanceId());
        assertThat(ciResponse.getBaseUri(),
                   allOf(startsWith("http://"), containsString(instancePath)));

        EasyMock.verify(consumerManager);
        EasyMock.reset(consumerManager);
      }
    }
  }

  @Test
  public void testCreateInstanceWithConfig() {
    ConsumerInstanceConfig config = new ConsumerInstanceConfig();
    config.setId("testid");

    for (TestUtils.RequestMediaType mediatype : TestUtils.V1_ACCEPT_MEDIATYPES) {
      for (String requestMediatype : TestUtils.V1_REQUEST_ENTITY_TYPES) {
        expectCreateGroup(config);
        EasyMock.replay(consumerManager);

        Response response = request("/consumers/" + groupName, mediatype.header)
            .post(Entity.entity(config, requestMediatype));
        assertOKResponse(response, mediatype.expected);
        final CreateConsumerInstanceResponse ciResponse =
                TestUtils.tryReadEntityOrLog(response, CreateConsumerInstanceResponse.class);
        assertEquals(instanceId, ciResponse.getInstanceId());
        assertThat(ciResponse.getBaseUri(),
                   allOf(startsWith("http://"), containsString(instancePath)));

        EasyMock.verify(consumerManager);
        EasyMock.reset(consumerManager);
      }
    }
  }

  @Test
  public void testInvalidInstanceOrTopic() {
    for (TestUtils.RequestMediaType mediatype : TestUtils.V1_ACCEPT_MEDIATYPES) {
      for (String requestMediatype : TestUtils.V1_REQUEST_ENTITY_TYPES) {
        // Trying to access either an invalid consumer instance or a missing topic should trigger
        // an error
        expectCreateGroup(new ConsumerInstanceConfig());
        expectReadTopic(topicName, BinaryConsumerState.class, null,
                        new RestNotFoundException(not_found_message, 1000));
        EasyMock.replay(consumerManager);

        Response response = request("/consumers/" + groupName, mediatype.header)
            .post(Entity.entity(null, requestMediatype));
        assertOKResponse(response, mediatype.expected);
        final CreateConsumerInstanceResponse createResponse =
                TestUtils.tryReadEntityOrLog(response, CreateConsumerInstanceResponse.class);

        final Response
            readResponse =
            request(instanceBasePath(createResponse) + "/topics/" + topicName, mediatype.header)
                .get();
        // Most specific default is different when retrieving embedded data
        String expectedMediatype
            = mediatype.header != null ? mediatype.expected : Versions.KAFKA_V1_JSON_BINARY;
        assertErrorResponse(Response.Status.NOT_FOUND, readResponse,
                            1000, not_found_message, expectedMediatype);

        EasyMock.verify(consumerManager);
        EasyMock.reset(consumerManager);
      }
    }
  }

  @Test
  public void testDeleteInstance() {
    for (TestUtils.RequestMediaType mediatype : TestUtils.V1_ACCEPT_MEDIATYPES) {
      for (String requestMediatype : TestUtils.V1_REQUEST_ENTITY_TYPES) {
        expectCreateGroup(new ConsumerInstanceConfig());
        expectDeleteGroup(false);
        EasyMock.replay(consumerManager);

        Response response = request("/consumers/" + groupName, mediatype.header)
            .post(Entity.entity(null, requestMediatype));
        assertOKResponse(response, mediatype.expected);
        final CreateConsumerInstanceResponse createResponse =
                TestUtils.tryReadEntityOrLog(response, CreateConsumerInstanceResponse.class);

        final Response
            deleteResponse =
            request(instanceBasePath(createResponse), mediatype.header).delete();
        assertErrorResponse(Response.Status.NO_CONTENT, deleteResponse,
                            0, null, mediatype.expected);

        EasyMock.verify(consumerManager);
        EasyMock.reset(consumerManager);
      }
    }
  }

  @Test
  public void testDeleteInvalidInstance() {
    for (TestUtils.RequestMediaType mediatype : TestUtils.V1_ACCEPT_MEDIATYPES) {
      expectDeleteGroup(true);
      EasyMock.replay(consumerManager);

      final Response
          deleteResponse =
          request("/consumers/" + groupName + "/instances/" + instanceId, mediatype.header)
              .delete();
      assertErrorResponse(Response.Status.NOT_FOUND, deleteResponse,
                          1000, not_found_message, mediatype.expected);

      EasyMock.verify(consumerManager);
      EasyMock.reset(consumerManager);
    }
  }
}
