/**
 * 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.hadoop.mapreduce.v2.app.webapp;

import static org.mockito.Mockito.*;

import java.io.IOException;
import java.util.Iterator;

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.JobACL;
import org.apache.hadoop.mapreduce.v2.api.records.JobId;
import org.apache.hadoop.mapreduce.v2.api.records.TaskId;
import org.apache.hadoop.mapreduce.v2.app.AppContext;
import org.apache.hadoop.mapreduce.v2.app.job.Job;
import org.apache.hadoop.mapreduce.v2.app.job.Task;
import org.apache.hadoop.mapreduce.v2.util.MRApps;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.webapp.Controller.RequestContext;
import org.apache.hadoop.yarn.webapp.MimeType;
import org.apache.hadoop.yarn.webapp.ResponseInfo;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

public class TestAppController {

  private AppControllerForTest appController;
  private RequestContext ctx;
  private Job job;
  private static final String taskId = "task_01_01_m_01";

  @Before
  public void setUp() throws IOException {
    AppContext context = mock(AppContext.class);
    when(context.getApplicationID()).thenReturn(
        ApplicationId.newInstance(0, 0));
    when(context.getApplicationName()).thenReturn("AppName");
    when(context.getUser()).thenReturn("User");
    when(context.getStartTime()).thenReturn(System.currentTimeMillis());
    job = mock(Job.class);
    Task task = mock(Task.class);

    when(job.getTask(any(TaskId.class))).thenReturn(task);
    when(job.loadConfFile()).thenReturn(new Configuration());
    when(job.getConfFile()).thenReturn(new Path("/"));

    JobId jobID = MRApps.toJobID("job_01_01");
    when(context.getJob(jobID)).thenReturn(job);
    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(true);

    App app = new App(context);
    Configuration configuration = new Configuration();
    ctx = mock(RequestContext.class);

    appController = new AppControllerForTest(app, configuration, ctx);
    appController.getProperty().put(AMParams.JOB_ID, "job_01_01");
    appController.getProperty().put(AMParams.TASK_ID, taskId);

  }

  /**
   * test bad request should be status 400...
   */
  @Test
  public void testBadRequest() {
    String message = "test string";
    appController.badRequest(message);
    verifyExpectations(message);
  }

  @Test
  public void testBadRequestWithNullMessage() {
    // It should not throw NullPointerException
    appController.badRequest(null);
    verifyExpectations(StringUtils.EMPTY);
  }

  private void verifyExpectations(String message) {
    verify(ctx).setStatus(400);
    assertEquals("application_0_0000", appController.getProperty()
        .get("app.id"));
    assertNotNull(appController.getProperty().get("rm.web"));
    assertEquals("Bad request: " + message,
        appController.getProperty().get("title"));
  }

  /**
   * Test the method 'info'.
   */
  @Test
  public void testInfo() {

    appController.info();
    Iterator<ResponseInfo.Item> iterator = appController.getResponseInfo()
        .iterator();
    ResponseInfo.Item item = iterator.next();
    assertEquals("Application ID:", item.key);
    assertEquals("application_0_0000", item.value);
    item = iterator.next();
    assertEquals("Application Name:", item.key);
    assertEquals("AppName", item.value);
    item = iterator.next();
    assertEquals("User:", item.key);
    assertEquals("User", item.value);

    item = iterator.next();
    assertEquals("Started on:", item.key);
    item = iterator.next();
    assertEquals("Elasped: ", item.key);

  }

  /**
   *  Test method 'job'. Should print message about error or set JobPage class for rendering
   */
  @Test
  public void testGetJob() {
    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(false);

    appController.job();
    verify(appController.response()).setContentType(MimeType.TEXT);
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01",
        appController.getData());
    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(true);

    appController.getProperty().remove(AMParams.JOB_ID);
    appController.job();
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01Bad Request: Missing job ID",
        appController.getData());

    appController.getProperty().put(AMParams.JOB_ID, "job_01_01");
    appController.job();
    assertEquals(JobPage.class, appController.getClazz());
  }

  /**
   *  Test method 'jobCounters'. Should print message about error or set CountersPage class for rendering
   */
  @Test
  public void testGetJobCounters() {

    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(false);

    appController.jobCounters();
    verify(appController.response()).setContentType(MimeType.TEXT);
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01",
        appController.getData());
    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(true);

    appController.getProperty().remove(AMParams.JOB_ID);
    appController.jobCounters();
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01Bad Request: Missing job ID",
        appController.getData());

    appController.getProperty().put(AMParams.JOB_ID, "job_01_01");
    appController.jobCounters();
    assertEquals(CountersPage.class, appController.getClazz());
  }

  /**
   *  Test method 'taskCounters'. Should print message about error or set CountersPage class for rendering
   */
  @Test
  public void testGetTaskCounters() {

    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(false);

    appController.taskCounters();
    verify(appController.response()).setContentType(MimeType.TEXT);
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01",
        appController.getData());

    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(true);

    appController.getProperty().remove(AMParams.TASK_ID);
    appController.taskCounters();
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01missing task ID",
        appController.getData());

    appController.getProperty().put(AMParams.TASK_ID, taskId);
    appController.taskCounters();
    assertEquals(CountersPage.class, appController.getClazz());
  }
  /**
   *  Test method 'singleJobCounter'. Should set SingleCounterPage class for rendering
   */

  @Test
  public void testGetSingleJobCounter() throws IOException {
    appController.singleJobCounter();
    assertEquals(SingleCounterPage.class, appController.getClazz());
  }

  /**
   *  Test method 'singleTaskCounter'. Should set SingleCounterPage class for rendering
   */
  @Test
  public void testGetSingleTaskCounter() throws IOException {
    appController.singleTaskCounter();
    assertEquals(SingleCounterPage.class, appController.getClazz());
    assertNotNull(appController.getProperty().get(AppController.COUNTER_GROUP));
    assertNotNull(appController.getProperty().get(AppController.COUNTER_NAME));
  }
  /**
   *  Test method 'tasks'. Should set TasksPage class for rendering
   */

  @Test
  public void testTasks() {
 
    appController.tasks();
 
    assertEquals(TasksPage.class, appController.getClazz());
  }
  /**
   *  Test method 'task'. Should set TaskPage class for rendering and information for title
   */
  @Test
  public void testTask() {
 
    appController.task();
    assertEquals("Attempts for " + taskId ,
        appController.getProperty().get("title"));

    assertEquals(TaskPage.class, appController.getClazz());
  }

  /**
   *   Test method 'conf'. Should set JobConfPage class for rendering
   */
  @Test
  public void testConfiguration() {
 
    appController.conf();

    assertEquals(JobConfPage.class, appController.getClazz());
  }

  /**
   * Test downloadConf request handling.
   */
  @Test
  public void testDownloadConfiguration() {
    appController.downloadConf();
    String jobConfXml = appController.getData();
    assertTrue("Error downloading the job configuration file.",
        !jobConfXml.contains("Error"));
  }

  /**
   *   Test method 'conf'. Should set AttemptsPage class for rendering or print information about error
   */
  @Test
  public void testAttempts() {

    appController.getProperty().remove(AMParams.TASK_TYPE);

    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(false);

    appController.attempts();
    verify(appController.response()).setContentType(MimeType.TEXT);
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01",
        appController.getData());

    when(job.checkAccess(any(UserGroupInformation.class), any(JobACL.class)))
        .thenReturn(true);

    appController.getProperty().remove(AMParams.TASK_ID);
    appController.attempts();
    assertEquals(
        "Access denied: User user does not have permission to view job job_01_01",
        appController.getData());

    appController.getProperty().put(AMParams.TASK_ID, taskId);
    appController.attempts();
    assertEquals("Bad request: missing task-type.", appController.getProperty()
        .get("title"));
    appController.getProperty().put(AMParams.TASK_TYPE, "m");

    appController.attempts();
    assertEquals("Bad request: missing attempt-state.", appController
        .getProperty().get("title"));
    appController.getProperty().put(AMParams.ATTEMPT_STATE, "State");

    appController.attempts();

    assertEquals(AttemptsPage.class, appController.getClazz());
  }

}
