/* * Copyright (c) 2014-2015 the original author or authors * * 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 io.werval.runtime; import com.jayway.restassured.response.Response; import io.werval.api.Application; import io.werval.api.Global; import io.werval.api.Plugin; import io.werval.api.context.Context; import io.werval.api.filters.Filter; import io.werval.api.filters.FilterChain; import io.werval.api.filters.FilterWith; import io.werval.api.http.RequestHeader; import io.werval.api.outcomes.Outcome; import io.werval.api.outcomes.Outcomes; import io.werval.runtime.routes.RoutesParserProvider; import io.werval.test.WervalHttpTest; import io.werval.test.util.Slf4jRule; import io.werval.util.Stacktraces; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.junit.ClassRule; import org.junit.Test; import static com.jayway.awaitility.Awaitility.await; import static com.jayway.restassured.RestAssured.expect; import static io.werval.api.context.CurrentContext.outcomes; 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 io.werval.api.http.Headers.Values.KEEP_ALIVE; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertThat; public class OnGlobalErrorTest { @ClassRule public static final Slf4jRule SLF4J = new Slf4jRule() { { record( Level.WARN ); recordForType( ApplicationInstance.class ); } }; public static class TestFilter implements Filter<Void> { @Override public CompletableFuture<Outcome> filter( FilterChain chain, Context context, Optional<Void> annotation ) { return chain.next( context ); } } public static class TestController { @FilterWith( TestFilter.class ) public Outcome action() { return outcomes().ok().build(); } public Outcome error() { throw new RuntimeException( "Error in Controller action" ); } } // // d8888 888 d8b 888 d8b // d88888 888 Y8P 888 Y8P // d88P888 888 888 // d88P 888 .d8888b 888888 888 888 888 8888b. 888888 888 .d88b. 88888b. // d88P 888 d88P" 888 888 888 888 "88b 888 888 d88""88b 888 "88b // d88P 888 888 888 888 Y88 88P .d888888 888 888 888 888 888 888 // d8888888888 Y88b. Y88b. 888 Y8bd8P 888 888 Y88b. 888 Y88..88P 888 888 // d88P 888 "Y8888P "Y888 888 Y88P "Y888888 "Y888 888 "Y88P" 888 888 // public static class OnActivate extends Global { @Override public void onActivate( Application application ) { throw new RuntimeException( "onActivate" ); } } @Test( expected = RuntimeException.class ) public void onActivate() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_onActivate.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } public static class BeforeHttpBind extends Global { @Override public void beforeHttpBind( Application application ) { throw new RuntimeException( "beforeHttpBind" ); } } @Test( expected = RuntimeException.class ) public void beforeHttpBind() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_beforeHttpBind.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } public static class AfterHttpBind extends Global { @Override public void afterHttpBind( Application application ) { throw new RuntimeException( "afterHttpBind" ); } } @Test( expected = RuntimeException.class ) public void afterHttpBind() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_afterHttpBind.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } // // 8888888b. d8b 888 d8b // 888 Y88b Y8P 888 Y8P // 888 888 888 // 888 d88P 8888b. .d8888b .d8888b 888 888 888 8888b. 888888 888 .d88b. 88888b. // 8888888P" "88b 88K 88K 888 888 888 "88b 888 888 d88""88b 888 "88b // 888 .d888888 "Y8888b. "Y8888b. 888 Y88 88P .d888888 888 888 888 888 888 888 // 888 888 888 X88 X88 888 Y8bd8P 888 888 Y88b. 888 Y88..88P 888 888 // 888 "Y888888 88888P' 88888P' 888 Y88P "Y888888 "Y888 888 "Y88P" 888 888 // public static class OnPassivate extends Global { @Override public void onPassivate( Application application ) { throw new RuntimeException( "onPassivate" ); } } @Test public void onPassivate() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_onPassivate.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } public static class BeforeHttpUnbind extends Global { @Override public void beforeHttpUnbind( Application application ) { throw new RuntimeException( "beforeHttpUnbind" ); } } @Test public void beforeHttpUnbind() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_beforeHttpUnbind.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } public static class AfterHttpUnbind extends Global { @Override public void afterHttpUnbind( Application application ) { throw new RuntimeException( "afterHttpUnbind" ); } } @Test public void afterHttpUnbind() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_afterHttpUnbind.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } // // 8888888b. 888 d8b // 888 Y88b 888 Y8P // 888 888 888 // 888 d88P 888 888 888 .d88b. 888 88888b. .d8888b // 8888888P" 888 888 888 d88P"88b 888 888 "88b 88K // 888 888 888 888 888 888 888 888 888 "Y8888b. // 888 888 Y88b 888 Y88b 888 888 888 888 X88 // 888 888 "Y88888 "Y88888 888 888 888 88888P' // 888 // Y8b d88P // "Y88P" // public static class ExtraPlugins extends Global { @Override public List<Plugin<?>> extraPlugins() { throw new RuntimeException( "extraPlugins" ); } } @Test( expected = RuntimeException.class ) public void extraPlugins() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_extraPlugins.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } public static class GetPluginInstance extends Global { @Override public <T> T getPluginInstance( Application application, Class<T> pluginType ) { throw new RuntimeException( "getPluginInstance" ); } } @Test( expected = RuntimeException.class ) public void getPluginInstance() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_getPluginInstance.conf" ); try { werval.beforeEachTestMethod(); } finally { werval.afterEachTestMethod(); } } // // 8888888 888 d8b 888 d8b // 888 888 Y8P 888 Y8P // 888 888 888 // 888 88888b. .d8888b 888888 8888b. 88888b. .d8888b 888 8888b. 888888 888 .d88b. 88888b. // 888 888 "88b 88K 888 "88b 888 "88b d88P" 888 "88b 888 888 d88""88b 888 "88b // 888 888 888 "Y8888b. 888 .d888888 888 888 888 888 .d888888 888 888 888 888 888 888 // 888 888 888 X88 Y88b. 888 888 888 888 Y88b. 888 888 888 Y88b. 888 Y88..88P 888 888 // 8888888 888 888 88888P' "Y888 "Y888888 888 888 "Y8888P 888 "Y888888 "Y888 888 "Y88P" 888 888 // public static class GetFilterInstance extends Global { @Override public <T> T getFilterInstance( Application application, Class<T> filterType ) { throw new RuntimeException( "getFilterInstance" ); } } @Test public void getFilterInstance() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_getFilterInstance.conf", new RoutesParserProvider( "GET / io.werval.runtime.OnGlobalErrorTest$TestController.action" ) ); try { werval.beforeEachTestMethod(); Response response = expect() .statusCode( 500 ) .header( X_WERVAL_REQUEST_ID, notNullValue() ) .header( CONNECTION, CLOSE ) .when() .get( "/" ); assertThat( werval.application().errors().count(), is( 1 ) ); assertThat( werval.application().errors().lastOfRequest( response.header( X_WERVAL_REQUEST_ID ) ).get().message(), equalTo( "getFilterInstance" ) ); } finally { werval.afterEachTestMethod(); } } public static class GetControllerInstance extends Global { @Override public <T> T getControllerInstance( Application application, Class<T> controllerType ) { throw new RuntimeException( "getControllerInstance" ); } } @Test public void getControllerInstance() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_getControllerInstance.conf", new RoutesParserProvider( "GET / io.werval.runtime.OnGlobalErrorTest$TestController.action" ) ); try { werval.beforeEachTestMethod(); Response response = expect() .statusCode( 500 ) .header( X_WERVAL_REQUEST_ID, notNullValue() ) .header( CONNECTION, CLOSE ) .when() .get( "/" ); assertThat( werval.application().errors().count(), is( 1 ) ); assertThat( werval.application().errors().lastOfRequest( response.header( X_WERVAL_REQUEST_ID ) ).get().message(), equalTo( "getControllerInstance" ) ); } finally { werval.afterEachTestMethod(); } } // // 8888888 888 d8b // 888 888 Y8P // 888 888 // 888 88888b. 888 888 .d88b. .d8888b 8888b. 888888 888 .d88b. 88888b. // 888 888 "88b 888 888 d88""88b d88P" "88b 888 888 d88""88b 888 "88b // 888 888 888 Y88 88P 888 888 888 .d888888 888 888 888 888 888 888 // 888 888 888 Y8bd8P Y88..88P Y88b. 888 888 Y88b. 888 Y88..88P 888 888 // 8888888 888 888 Y88P "Y88P" "Y8888P "Y888888 "Y888 888 "Y88P" 888 888 // public static class InvokeControllerMethod extends Global { @Override public Outcome invokeControllerMethod( Context context, Object controller ) { throw new RuntimeException( "invokeControllerMethod" ); } } @Test public void invokeControllerMethod() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_invokeControllerMethod.conf", new RoutesParserProvider( "GET / io.werval.runtime.OnGlobalErrorTest$TestController.action" ) ); try { werval.beforeEachTestMethod(); Response response = expect() .statusCode( 500 ) .header( X_WERVAL_REQUEST_ID, notNullValue() ) .header( CONNECTION, CLOSE ) .when() .get( "/" ); assertThat( werval.application().errors().count(), is( 1 ) ); assertThat( werval.application().errors().lastOfRequest( response.header( X_WERVAL_REQUEST_ID ) ).get().message(), equalTo( "invokeControllerMethod" ) ); } finally { werval.afterEachTestMethod(); } } // // .d8888b. 888 888 d8b // d88P Y88b 888 888 Y8P // 888 888 888 888 // 888 .d88b. 88888b.d88b. 88888b. 888 .d88b. 888888 888 .d88b. 88888b. // 888 d88""88b 888 "888 "88b 888 "88b 888 d8P Y8b 888 888 d88""88b 888 "88b // 888 888 888 888 888 888 888 888 888 888 88888888 888 888 888 888 888 888 // Y88b d88P Y88..88P 888 888 888 888 d88P 888 Y8b. Y88b. 888 Y88..88P 888 888 // "Y8888P" "Y88P" 888 888 888 88888P" 888 "Y8888 "Y888 888 "Y88P" 888 888 // 888 // 888 // 888 // public static class OnHttpRequestComplete extends Global { @Override public void onHttpRequestComplete( Application application, RequestHeader requestHeader ) { throw new RuntimeException( "onHttpRequestComplete" ); } } @Test public void onHttpRequestComplete() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_onHttpRequestComplete.conf", new RoutesParserProvider( "GET / io.werval.runtime.OnGlobalErrorTest$TestController.action" ) ); try { werval.beforeEachTestMethod(); expect() .statusCode( 200 ) .header( X_WERVAL_REQUEST_ID, notNullValue() ) .header( CONNECTION, KEEP_ALIVE ) .when() .get( "/" ); await().until( () -> SLF4J.contains( "onHttpRequestComplete" ) ); assertThat( werval.application().errors().count(), is( 0 ) ); } finally { werval.afterEachTestMethod(); } } // // 8888888888 // 888 // 888 // 8888888 888d888 888d888 .d88b. 888d888 .d8888b // 888 888P" 888P" d88""88b 888P" 88K // 888 888 888 888 888 888 "Y8888b. // 888 888 888 Y88..88P 888 X88 // 8888888888 888 888 "Y88P" 888 88888P' // public static class GetRootCause extends Global { @Override public Throwable getRootCause( Throwable throwable ) { throw new RuntimeException( "getRootCause" ); } } @Test public void getRootCause() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_getRootCause.conf", new RoutesParserProvider( "GET / io.werval.runtime.OnGlobalErrorTest$TestController.error" ) ); try { werval.beforeEachTestMethod(); expect() .statusCode( 500 ) .header( X_WERVAL_REQUEST_ID, notNullValue() ) .header( CONNECTION, CLOSE ) .when() .get( "/" ); assertThat( werval.application().errors().count(), is( 1 ) ); assertThat( Stacktraces.containsMessage( "getRootCause" ).test( werval.application().errors().last().get().cause() ), is( true ) ); } finally { werval.afterEachTestMethod(); } } public static class OnRequestError extends Global { @Override public Outcome onRequestError( Application application, RequestHeader request, Outcomes outcomes, Throwable cause ) { throw new RuntimeException( "onRequestError" ); } } @Test public void onRequestError() { WervalHttpTest werval = new WervalHttpTest( "global-errors-test_onRequestError.conf", new RoutesParserProvider( "GET / io.werval.runtime.OnGlobalErrorTest$TestController.error" ) ); try { werval.beforeEachTestMethod(); expect() .statusCode( 500 ) .header( X_WERVAL_REQUEST_ID, notNullValue() ) .header( CONNECTION, CLOSE ) .when() .get( "/" ); assertThat( werval.application().errors().count(), is( 1 ) ); assertThat( Stacktraces.containsMessage( "onRequestError" ).test( werval.application().errors().last().get().cause() ), is( true ) ); } finally { werval.afterEachTestMethod(); } } }