/* * Copyright (c) 2001-2017, Inversoft Inc., All Rights Reserved * * 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 org.primeframework.mvc; import java.io.File; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDate; import java.time.ZonedDateTime; import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import org.example.domain.UserField; import org.primeframework.mvc.action.config.ActionConfigurationProvider; import org.primeframework.mvc.container.ContainerResolver; import org.primeframework.mvc.parameter.convert.ConverterProvider; import org.primeframework.mvc.parameter.convert.GlobalConverter; import org.primeframework.mvc.parameter.el.ExpressionEvaluator; import org.primeframework.mvc.parameter.el.MissingPropertyExpressionException; import org.primeframework.mvc.test.RequestSimulator; import org.primeframework.mvc.util.URIBuilder; import org.testng.annotations.Test; import com.codahale.metrics.Meter; import com.codahale.metrics.Timer; import com.google.inject.Key; import com.google.inject.TypeLiteral; import freemarker.template.Configuration; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertSame; /** * This class tests the MVC from a high level perspective. * * @author Brian Pontarelli */ public class GlobalTest extends PrimeBaseTest { @Test public void get() throws Exception { simulator.test("/user/edit") .get() .assertStatusCode(200) .assertBodyFile(Paths.get("src/test/resources/html/edit.html")); } @Test public void get_JSONView() throws Exception { simulator.test("/views/entry/api") .get() .assertStatusCode(200) .assertJSONFile(Paths.get("src/test/resources/json/views/entry/entry-api.json")); simulator.test("/views/entry/export") .get() .assertStatusCode(200) .assertJSONFile(Paths.get("src/test/resources/json/views/entry/entry-export.json")); } @Test public void get_developmentExceptions() throws Exception { // Bad annotation @Action("{id}") it should be @Action("{uuid}") simulator.test("/invalid-api/42") .expectException(MissingPropertyExpressionException.class) .get() .assertStatusCode(500); // Bad parameter (i.e. /invalid-api?bad-param=42 simulator.test("/invalid-api") .withParameter("bad-param", "42") .expectException(MissingPropertyExpressionException.class) .get() .assertStatusCode(500); } @Test public void get_expressionEvaluatorSkippedUsesRequest() throws Exception { // Tests that the expression evaluator safely gets skipped while looking for values and Prime then checks the // HttpServletRequest and finds the value simulator.test("/value-in-request") .get() .assertBodyContains("baz") .assertRequestContainsAttribute("bar", "baz"); } @Test public void get_fullFormWithAllAttributes() throws Exception { simulator.test("/user/full-form") .get() .assertBodyFile(Paths.get("src/test/resources/html/full-form.html")); } @Test public void get_jwtAuthorized() throws Exception { test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", true) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkifQ.qHdut1UR4-2FSAvh7U3YdeRR5r5boVqjIGQ16Ztp894") .get() .assertStatusCode(200)); } @Test public void get_jwtDisabledJwtAuthentication() throws Exception { // Send in a JWT Authorization header when the Action has JWT disabled. Should always get a 401. When a JWT is provided, the action expects JWT to be enabled. test.simulate(() -> simulator.test("/jwt-authorized-disabled") .withParameter("authorized", true) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkifQ.qHdut1UR4-2FSAvh7U3YdeRR5r5boVqjIGQ16Ztp894") .get() .assertStatusCode(401)); } @Test public void get_jwtExpired() throws Exception { test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", true) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NDUxMDA3MzF9.K18gIegEBfxgj8rU4D2WDh3CzEmRUmy8qBS7SWAcG9w") .get() .assertStatusCode(401)); } @Test public void get_jwtInvalidSignature() throws Exception { test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", true) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkifQ.aaabbbcccddd") .get() .assertStatusCode(401)); } @Test public void get_jwtMissingAuthorizeHeader() throws Exception { test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", true) .get() .assertStatusCode(401)); } @Test public void get_jwtNotAuthorized() throws Exception { test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", false) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkifQ.qHdut1UR4-2FSAvh7U3YdeRR5r5boVqjIGQ16Ztp894") .get() .assertStatusCode(401)); } @Test public void get_jwtNotBefore() throws Exception { // Validating the JWT registered claim 'nbf' (Not Before). The JWT is validly signed, but it is instructed not to be valid before some point in the future. Expecting a 401. test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", true) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjQ2MzIzOTY2NjV9.mRvvyJXvDD8RQ_PM1TadZdZNYXRa9CjOx62Tk866538") .get() .assertStatusCode(401)); } @Test public void get_metrics() throws Exception { simulator.test("/user/full-form") .get() .assertBodyFile(Paths.get("src/test/resources/html/full-form.html")); Map<String, Timer> timers = metricRegistry.getTimers(); assertEquals(timers.get("prime-mvc.[/user/full-form].requests").getCount(), 1); } @Test public void get_metricsErrors() throws Exception { simulator.test("/execute-method-throws-exception") .expectException(IllegalArgumentException.class) .get() .assertStatusCode(500); Map<String, Timer> timers = metricRegistry.getTimers(); assertEquals(timers.get("prime-mvc.[/execute-method-throws-exception].requests").getCount(), 1); Map<String, Meter> meters = metricRegistry.getMeters(); assertEquals(meters.get("prime-mvc.[/execute-method-throws-exception].errors").getCount(), 1); } @Test public void get_nonFormFields() throws Exception { simulator.test("/user/details-fields") .get() .assertBodyFile(Paths.get("src/test/resources/html/details-fields.html")); } @Test public void head() throws Exception { simulator.test("/head") .head() .assertStatusCode(200) .assertBodyIsEmpty(); } @Test public void head_jwtAuthorized() throws Exception { // This test will pass if we call the JWT authorize method or not.... test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", true) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkifQ.qHdut1UR4-2FSAvh7U3YdeRR5r5boVqjIGQ16Ztp894") .head() .assertStatusCode(200)); } @Test public void head_jwtNotAuthorized() throws Exception { test.simulate(() -> simulator.test("/jwt-authorized") .withParameter("authorized", false) .withHeader("Authorization", "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkifQ.qHdut1UR4-2FSAvh7U3YdeRR5r5boVqjIGQ16Ztp894") .head() .assertStatusCode(401)); } @Test public void notImplemented() throws Exception { simulator.test("/not-implemented") .get() .assertStatusCode(501); simulator.test("/not-implemented") .post() .assertStatusCode(501); simulator.test("/not-implemented") .put() .assertStatusCode(501); simulator.test("/not-implemented") .delete() .assertStatusCode(501); // head is implemented simulator.test("/not-implemented") .head() .assertStatusCode(200); } @Test public void post() throws Exception { simulator.test("/post") .post() .assertStatusCode(200) .assertBodyContains("Brian Pontarelli", "35", "Broomfield", "CO"); } @Test public void post_JSONWithActual() throws Exception { Path json = Paths.get("src/test/resources/json/api-jsonWithActual-post.json"); simulator.test("/api") .withJSONFile(json) .post() .assertJSONFileWithActual(UserField.class, Paths.get("src/test/resources/json/api-jsonWithActual-post-response.json")); } @Test public void post_apiJSONBothWays() throws Exception { Path json = Paths.get("src/test/resources/json/api-jsonBothWays-post.json"); simulator.test("/api") .withJSONFile(json) .post() .assertJSONFile(json); } @Test public void post_binary() throws Exception { test.simulate(() -> simulator.test("/binary") .withParameter("expected", "Hello World") .withBody("Hello World") .withContentType("application/octet-stream") .post() .assertStatusCode(200)); } @Test public void post_scopeStorage() throws Exception { // Tests that the expression evaluator safely gets skipped while looking for values and Prime then checks the // HttpServletRequest and finds the value simulator.test("/scope-storage") .post(); assertNotNull(simulator.session.getAttribute("sessionObject")); } @Test public void singletons() throws Exception { assertSingleton(simulator, ActionConfigurationProvider.class); assertSingleton(simulator, Configuration.class); assertSingleton(simulator, ResourceBundle.Control.class); assertSingleton(simulator, ResourceBundle.Control.class); assertSingleton(simulator, ContainerResolver.class); assertSingleton(simulator, ConverterProvider.class); assertSingleton(simulator, ExpressionEvaluator.class); assertSingleton(simulator, URIBuilder.class); assertSingletonConverter(simulator, Boolean.class); assertSingletonConverter(simulator, boolean.class); assertSingletonConverter(simulator, Character.class); assertSingletonConverter(simulator, char.class); assertSingletonConverter(simulator, Number.class); assertSingletonConverter(simulator, int.class); assertSingletonConverter(simulator, long.class); assertSingletonConverter(simulator, double.class); assertSingletonConverter(simulator, float.class); assertSingletonConverter(simulator, BigDecimal.class); assertSingletonConverter(simulator, BigInteger.class); assertSingletonConverter(simulator, Collection.class); assertSingletonConverter(simulator, ZonedDateTime.class); assertSingletonConverter(simulator, Enum.class); assertSingletonConverter(simulator, File.class); assertSingletonConverter(simulator, LocalDate.class); assertSingletonConverter(simulator, Locale.class); assertSingletonConverter(simulator, String.class); } private void assertSingleton(RequestSimulator simulator, Class<?> type) { assertSame(simulator.injector.getInstance(type), simulator.injector.getInstance(type)); } private void assertSingletonConverter(RequestSimulator simulator, Class<?> type) { Map<Class<?>, GlobalConverter> converters = simulator.injector.getInstance(Key.get(new TypeLiteral<Map<Class<?>, GlobalConverter>>() { })); assertSame(converters.get(type), converters.get(type)); } }