
package io.werval.runtime;

import com.jayway.restassured.response.Response;
import io.werval.api.Error;
import io.werval.api.context.CurrentContext;
import io.werval.api.outcomes.Outcome;
import io.werval.runtime.routes.RoutesParserProvider;
import io.werval.test.WervalHttpRule;
import java.io.IOException;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;

import static com.jayway.restassured.RestAssured.expect;
import static io.werval.api.http.Headers.Names.CONNECTION;
import static io.werval.api.http.Headers.Names.X_WERVAL_REQUEST_ID;
import static io.werval.api.http.Headers.Values.CLOSE;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;

/**
 * Assert that errors in Application code triggers the right code paths.
 */
public class OnApplicationErrorTest
{
    public static class Ctrl
    {
        public Outcome success()
        {
            return CurrentContext.outcomes().ok().build();
        }

        public Outcome internalServerError()
        {
            return CurrentContext.outcomes().internalServerError().build();
        }

        public Outcome exception()
            throws IOException
        {
            throw new IOException( "Wow an exception!", new RuntimeException( "This is a crash" ) );
        }
    }

    @Rule
    public final WervalHttpRule werval = new WervalHttpRule( new RoutesParserProvider(
        "GET /success io.werval.runtime.OnApplicationErrorTest$Ctrl.success\n"
        + "GET /internalServerError io.werval.runtime.OnApplicationErrorTest$Ctrl.internalServerError\n"
        + "GET /exception io.werval.runtime.OnApplicationErrorTest$Ctrl.exception" )
    );

    @Test
    public void given_200_then_no_error()
        throws IOException
    {
        expect().
            statusCode( 200 ).
            header( X_WERVAL_REQUEST_ID, notNullValue() ).
            when().
            get( "/success" );
        assertThat( werval.application().errors().count(), is( 0 ) );
    }

    @Test
    public void given_500_from_controller_then_no_error()
        throws IOException
    {
        expect().
            statusCode( 500 ).
            header( X_WERVAL_REQUEST_ID, notNullValue() ).
            header( CONNECTION, CLOSE ).
            when().
            get( "/internalServerError" );
        assertThat( werval.application().errors().count(), is( 0 ) );
    }

    @Test
    public void given_exception_in_controller_then_500_and_error_recorded()
        throws IOException
    {
        Response response = expect().
            statusCode( 500 ).
            header( CONNECTION, CLOSE ).
            when().
            get( "/exception" );

        assertThat( werval.application().errors().count(), is( 1 ) );

        String requestId = response.header( X_WERVAL_REQUEST_ID );
        assertThat( requestId, notNullValue() );

        List<Error> requestErrors = werval.application().errors().ofRequest( requestId );
        assertThat( requestErrors.size(), is( 1 ) );

        Error requestError = requestErrors.get( 0 );
        assertThat( requestError.cause().getClass().getName(), equalTo( IOException.class.getName() ) );
        assertThat( requestError.cause().getMessage(), equalTo( "Wow an exception!" ) );
        assertThat( requestError.cause().getCause().getClass().getName(), equalTo( RuntimeException.class.getName() ) );
        assertThat( requestError.cause().getCause().getMessage(), equalTo( "This is a crash" ) );
    }
}
