/*
 * 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.
 */

package org.apache.ambari.server.api.handlers;

import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.ambari.server.api.query.Query;
import org.apache.ambari.server.api.query.render.DefaultRenderer;
import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.api.services.RequestBody;
import org.apache.ambari.server.api.services.Result;
import org.apache.ambari.server.api.services.ResultStatus;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.TemporalInfo;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.security.authorization.AuthorizationException;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.Test;

/**
 * Unit tests for ReadHandler.
 */
public class ReadHandlerTest {

  @Test
  public void testHandleRequest__InvalidField() {
    Request request = createNiceMock(Request.class);
    ResourceInstance resource = createNiceMock(ResourceInstance.class);
    Query query = createStrictMock(Query.class);

    Map<String, TemporalInfo> mapPartialResponseFields = new HashMap<>();
    mapPartialResponseFields.put("foo/bar", null);
    Renderer renderer = new DefaultRenderer();

    expect(request.getResource()).andReturn(resource);
    expect(request.getFields()).andReturn(mapPartialResponseFields);
    expect(request.getRenderer()).andReturn(renderer);
    expect(resource.getQuery()).andReturn(query);

    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);
    query.addProperty("foo/bar", null);
    expectLastCall().andThrow(new IllegalArgumentException("testMsg"));

    replay(request, resource, query);

    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);

    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
    assertEquals("testMsg", result.getStatus().getMessage());

    verify(request, resource, query);
  }

  @Test
  public void testHandleRequest__OK() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    Predicate predicate = createMock(Predicate.class);
    Result result = createStrictMock(Result.class);
    RequestBody body = createStrictMock(RequestBody.class);
    Renderer renderer = new DefaultRenderer();
    Capture<ResultStatus> resultStatusCapture = EasyMock.newCapture();

    Map<String, String> requestInfoProperties = Collections.singletonMap("directive", "value");

    Map<String, TemporalInfo> mapPartialResponseFields = new HashMap<>();
    mapPartialResponseFields.put("foo", null);
    mapPartialResponseFields.put("bar/c", null);
    mapPartialResponseFields.put("bar/d/e", null);
    mapPartialResponseFields.put("category/", null);
    //expectations
    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(body);
    expect(request.getFields()).andReturn(mapPartialResponseFields);

    expect(body.getRequestInfoProperties()).andReturn(requestInfoProperties);

    query.setRequestInfoProps(requestInfoProperties);
    query.addProperty("foo", null);
    query.addProperty("bar/c", null);
    query.addProperty("bar/d/e", null);
    query.addProperty("category/", null);

    expect(request.getQueryPredicate()).andReturn(predicate);
    query.setUserPredicate(predicate);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);
    expect(query.execute()).andReturn(result);
    result.setResultStatus(capture(resultStatusCapture));

    replay(request, resource, body, query, predicate, result);

    //test
    ReadHandler handler = new ReadHandler();
    assertSame(result, handler.handleRequest(request));
    assertEquals(ResultStatus.STATUS.OK, resultStatusCapture.getValue().getStatus());
    verify(request, resource, body, query, predicate, result);
  }

  @Test
  public void testHandleRequest__SystemException() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    Predicate predicate = createMock(Predicate.class);
    Renderer renderer = new DefaultRenderer();

    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(null);
    expect(request.getFields()).andReturn(Collections.emptyMap());

    expect(request.getQueryPredicate()).andReturn(predicate);
    query.setUserPredicate(predicate);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);
    SystemException systemException = new SystemException("testMsg", new RuntimeException());
    expect(query.execute()).andThrow(systemException);

    replay(request, resource, query, predicate);

    //test
    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);
    assertEquals(ResultStatus.STATUS.SERVER_ERROR, result.getStatus().getStatus());
    assertEquals(systemException.getMessage(), result.getStatus().getMessage());
    verify(request, resource, query, predicate);
  }

  @Test
  public void testHandleRequest__NoSuchParentResourceException() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    Predicate predicate = createMock(Predicate.class);
    NoSuchParentResourceException exception = new NoSuchParentResourceException("exceptionMsg", new RuntimeException());
    Renderer renderer = new DefaultRenderer();

    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(null);
    expect(request.getFields()).andReturn(Collections.emptyMap());

    expect(request.getQueryPredicate()).andReturn(predicate);
    query.setUserPredicate(predicate);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);

    expect(query.execute()).andThrow(exception);

    replay(request, resource, query, predicate);

    //test
    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);
    assertEquals(ResultStatus.STATUS.NOT_FOUND, result.getStatus().getStatus());
    assertEquals("exceptionMsg", result.getStatus().getMessage());
    verify(request, resource, query, predicate);
  }

  @Test
  public void testHandleRequest__UnsupportedPropertyException() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    Predicate predicate = createMock(Predicate.class);
    Renderer renderer = new DefaultRenderer();
    UnsupportedPropertyException exception = new UnsupportedPropertyException(
        Resource.Type.Cluster, Collections.singleton("foo"));

    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(null);
    expect(request.getFields()).andReturn(Collections.emptyMap());

    expect(request.getQueryPredicate()).andReturn(predicate);
    query.setUserPredicate(predicate);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);

    expect(query.execute()).andThrow(exception);

    replay(request, resource, query, predicate);

    //test
    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);
    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
    assertEquals(exception.getMessage(), result.getStatus().getMessage());
    verify(request, resource, query, predicate);
  }

  @Test
  public void testHandleRequest__NoSuchResourceException_OK() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    Predicate predicate = createMock(Predicate.class);
    NoSuchResourceException exception = new NoSuchResourceException("msg", new RuntimeException());
    Renderer renderer = new DefaultRenderer();

    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(null);
    expect(request.getFields()).andReturn(Collections.emptyMap());

    expect(request.getQueryPredicate()).andReturn(predicate).anyTimes();
    query.setUserPredicate(predicate);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);

    expect(query.execute()).andThrow(exception);

    replay(request, resource, query, predicate);

    //test
    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);
    // ok because this is a query that returned no rows
    assertEquals(ResultStatus.STATUS.OK, result.getStatus().getStatus());
    verify(request, resource, query, predicate);
  }

  @Test
  public void testHandleRequest__NoSuchResourceException_NOT_FOUND() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    NoSuchResourceException exception = new NoSuchResourceException("msg", new RuntimeException());
    Renderer renderer = new DefaultRenderer();

    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(null);
    expect(request.getFields()).andReturn(Collections.emptyMap());

    expect(request.getQueryPredicate()).andReturn(null).anyTimes();
    query.setUserPredicate(null);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);

    expect(query.execute()).andThrow(exception);

    replay(request, resource, query);

    //test
    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);
    // not a query, so not found
    assertEquals(ResultStatus.STATUS.NOT_FOUND, result.getStatus().getStatus());
    assertEquals(exception.getMessage(), result.getStatus().getMessage());
    verify(request, resource, query);
  }

  @Test
  public void testHandleRequest__AuthorizationException() throws Exception {
    Request request = createStrictMock(Request.class);
    ResourceInstance resource = createStrictMock(ResourceInstance.class);
    Query query = createMock(Query.class);
    Predicate predicate = createMock(Predicate.class);
    Renderer renderer = new DefaultRenderer();

    expect(request.getResource()).andReturn(resource);
    expect(resource.getQuery()).andReturn(query);

    expect(request.getPageRequest()).andReturn(null);
    expect(request.getSortRequest()).andReturn(null);
    expect(request.getRenderer()).andReturn(renderer);
    expect(request.getBody()).andReturn(null);
    expect(request.getFields()).andReturn(Collections.emptyMap());

    expect(request.getQueryPredicate()).andReturn(predicate);
    query.setUserPredicate(predicate);
    query.setPageRequest(null);
    query.setSortRequest(null);
    query.setRenderer(renderer);
    AuthorizationException authorizationException = new AuthorizationException("testMsg");
    expect(query.execute()).andThrow(authorizationException);

    replay(request, resource, query, predicate);

    //test
    ReadHandler handler = new ReadHandler();
    Result result = handler.handleRequest(request);
    assertEquals(ResultStatus.STATUS.FORBIDDEN, result.getStatus().getStatus());
    assertEquals(authorizationException.getMessage(), result.getStatus().getMessage());
    verify(request, resource, query, predicate);
  }
}
