/*
 * -\-\-
 * Spotify Apollo API Implementations
 * --
 * Copyright (C) 2013 - 2015 Spotify AB
 * --
 * Licensed 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 com.spotify.apollo.request;

import com.spotify.apollo.Request;
import com.spotify.apollo.RequestContext;
import com.spotify.apollo.Response;
import com.spotify.apollo.dispatch.Endpoint;
import com.spotify.apollo.dispatch.EndpointInfo;
import com.spotify.apollo.environment.IncomingRequestAwareClient;
import com.spotify.apollo.route.Rule;
import com.spotify.apollo.route.RuleMatch;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.function.BiConsumer;

import okio.ByteString;

import static com.spotify.apollo.Response.forStatus;
import static com.spotify.apollo.Status.INTERNAL_SERVER_ERROR;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class RequestHandlerImplTest {

  @Mock RequestRunnableFactory requestFactory;
  @Mock EndpointRunnableFactory endpointFactory;

  @Mock RequestRunnable requestRunnable;
  @Mock Runnable runnable;

  @Mock OngoingRequest ongoingRequest;
  @Mock RuleMatch<Endpoint> match;
  @Mock Endpoint endpoint;
  @Mock EndpointInfo info;

  @Captor ArgumentCaptor<BiConsumer<OngoingRequest, RuleMatch<Endpoint>>> continuationCaptor;

  RequestHandlerImpl requestHandler;

  @Before
  public void setUp() throws Exception {
    IncomingRequestAwareClient client = new NoopClient();

    when(ongoingRequest.request()).thenReturn(Request.forUri("http://foo"));
    when(requestFactory.create(any())).thenReturn(requestRunnable);
    when(endpointFactory.create(eq(ongoingRequest), any(RequestContext.class), eq(endpoint)))
        .thenReturn(runnable);

    when(match.getRule()).thenReturn(Rule.fromUri("http://foo", "GET", endpoint));
    when(endpoint.info()).thenReturn(info);
    when(info.getName()).thenReturn("foo");

    requestHandler = new RequestHandlerImpl(requestFactory, endpointFactory, client);
  }

  @Test
  public void shouldRunRequestRunnable() throws Exception {
    requestHandler.handle(ongoingRequest);

    verify(requestRunnable).run(any());
  }

  @Test
  public void shouldRunEndpointRunnable() throws Exception {
    requestHandler.handle(ongoingRequest);

    verify(requestRunnable).run(continuationCaptor.capture());

    continuationCaptor.getValue()
        .accept(ongoingRequest, match);

    verify(endpointFactory).create(eq(ongoingRequest), any(RequestContext.class), eq(endpoint));
    verify(runnable).run();
  }

  @Test
  public void shouldReplySafelyForExceptions() throws Exception {
    doThrow(new NullPointerException("expected")).when(requestRunnable).run(any());

    requestHandler.handle(ongoingRequest);

    verify(ongoingRequest).reply(forStatus(INTERNAL_SERVER_ERROR));
  }

  private static class NoopClient implements IncomingRequestAwareClient {

    @Override
    public CompletionStage<Response<ByteString>> send(Request request, Optional<Request> incoming) {
      throw new UnsupportedOperationException();
    }
  }
}

@@ -1526,16 +1526,99 @@
 _ERROR;%0A
+import static org.hamcrest.Matchers.is;%0Aimport static org.junit.Assert.assertThat;%0A
 import s
@@ -1649,16 +1649,16 @@
 rs.any;%0A
-
 import s
@@ -1888,16 +1888,73 @@
 Test %7B%0A%0A
+  private static final long ARRIVAL_TIME_NANOS = 4711L;%0A%0A
   @Mock 
@@ -2333,16 +2333,79 @@
 nCaptor;
+%0A  @Captor ArgumentCaptor%3CRequestContext%3E requestContextCaptor;
 %0A%0A  Requ
@@ -2542,24 +2542,100 @@
 pClient();%0A%0A
+    when(ongoingRequest.arrivalTimeNanos()).thenReturn(ARRIVAL_TIME_NANOS);%0A
     when(ong
@@ -2811,37 +2811,33 @@
 ngoingRequest), 
-any(R
+r
 equestContext.cl
@@ -2825,38 +2825,47 @@
 , requestContext
-.class
+Captor.capture(
 ), eq(endpoint))
@@ -3895,32 +3895,32 @@
 goingRequest);%0A%0A
-
     verify(ongoi
@@ -3976,16 +3976,432 @@
 );%0A  %7D%0A%0A
+  @Test%0A  public void shouldSetRequestContextArrivalTime() throws Exception %7B%0A    requestHandler.handle(ongoingRequest);%0A%0A    verify(requestRunnable).run(continuationCaptor.capture());%0A%0A    continuationCaptor.getValue()%0A        .accept(ongoingRequest, match);%0A%0A    final RequestContext requestContext = requestContextCaptor.getValue();%0A    assertThat(requestContext.arrivalTimeNanos(), is(ARRIVAL_TIME_NANOS));%0A  %7D%0A%0A
   privat

add arrival time test