io.vertigo.vega.webservice.WebServiceManagerTest.java Source code

Java tutorial

Introduction

Here is the source code for io.vertigo.vega.webservice.WebServiceManagerTest.java

Source

/**
 * vertigo - simple java starter
 *
 * Copyright (C) 2013-2017, KleeGroup, direction.technique@kleegroup.com (http://www.kleegroup.com)
 * KleeGroup, Centre d'affaire la Boursidiere - BP 159 - 92357 Le Plessis Robinson Cedex - France
 *
 * 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.vertigo.vega.webservice;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.http.HttpStatus;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import io.restassured.RestAssured;
import io.restassured.filter.session.SessionFilter;
import io.restassured.parsing.Parser;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import io.vertigo.app.AutoCloseableApp;
import io.vertigo.util.DateBuilder;
import io.vertigo.util.ListBuilder;
import io.vertigo.util.MapBuilder;
import io.vertigo.vega.webservice.data.MyAppConfig;

public final class WebServiceManagerTest {
    private static final String HEADER_ACCESS_TOKEN = "x-access-token";
    private static final String UTF8_TEST_STRING = "? TM '` Euro R@?";

    private final SessionFilter loggedSessionFilter = new SessionFilter();
    private final SessionFilter anonymousSessionFilter = new SessionFilter();
    private static AutoCloseableApp app;

    static {
        //RestAsssured init
        RestAssured.port = MyAppConfig.WS_PORT;
    }

    @BeforeClass
    public static void setUp() {
        app = new AutoCloseableApp(MyAppConfig.config());
    }

    @Before
    public void preTestLogin() {
        RestAssured.registerParser("plain/text", Parser.TEXT);
        RestAssured.given().filter(loggedSessionFilter).get("/test/login");
    }

    @AfterClass
    public static void tearDown() {
        if (app != null) {
            app.close();
        }
    }

    @Test
    public void testCatalog() {
        RestAssured.given().expect().body("size()", Matchers.greaterThanOrEqualTo(50)) //actually 62
                .statusCode(HttpStatus.SC_OK).when().get("/catalog");
    }

    @Test
    public void testSwaggerApi() {
        RestAssured.given().expect().body("swagger", Matchers.equalTo(2.0f)).body("info", Matchers.notNullValue())
                .body("info.size()", Matchers.equalTo(3)).body("basePath", Matchers.anything()) //can be null
                .body("paths", Matchers.notNullValue()).body("paths.size()", Matchers.greaterThanOrEqualTo(50)) //actually 57
                .body("definitions", Matchers.notNullValue())
                .body("definitions.size()", Matchers.greaterThanOrEqualTo(30)) //actually 37
                .statusCode(HttpStatus.SC_OK).log().ifValidationFails().when().get("/swaggerApi");
    }

    private static void assertStatusCode(final int expectedStatusCode, final String path) {
        RestAssured.given().expect().statusCode(expectedStatusCode).when().get(path);
    }

    @Test
    public void testSwaggerUi() {
        assertStatusCode(HttpStatus.SC_OK, "/swaggerUi");
        assertStatusCode(HttpStatus.SC_OK, "/swaggerUi/swagger-ui.min.js");
        assertStatusCode(HttpStatus.SC_OK, "/swaggerUi/css/screen.css");
        assertStatusCode(HttpStatus.SC_OK, "/swaggerUi/images/logo_small.png");
        assertStatusCode(HttpStatus.SC_OK, "/swaggerUi/images/throbber.gif");
        assertStatusCode(HttpStatus.SC_NOT_FOUND, "/swaggerUi/test404.mp4");
    }

    @Test
    public void testLogout() {
        loggedAndExpect().statusCode(HttpStatus.SC_NO_CONTENT).when().get("/test/logout");
    }

    @Test
    public void testLogin() {
        assertStatusCode(HttpStatus.SC_NO_CONTENT, "/test/login");
    }

    @Test
    public void testAnonymousTest() {
        assertStatusCode(HttpStatus.SC_OK, "/test/anonymousTest");
    }

    @Test
    public void testAuthentifiedTest() {
        loggedAndExpect().statusCode(HttpStatus.SC_OK).body("size()", Matchers.greaterThanOrEqualTo(10)).when()
                .get("/test/authentifiedTest");
    }

    @Test
    public void testUnauthentifiedTest() {
        assertStatusCode(HttpStatus.SC_UNAUTHORIZED, "/test/authentifiedTest");
    }

    @Test
    public void testTwoResultConfirm() {
        loggedAndExpect(given().param("type", "Confirm")).statusCode(HttpStatus.SC_OK)
                .body("message", Matchers.equalTo("Are you sure")).when().get("/test/twoResult");
    }

    @Test
    public void testTwoResultContact() {
        loggedAndExpect(given().param("type", "Contact")).statusCode(HttpStatus.SC_OK)
                .body("contact.conId", Matchers.equalTo(1)).when().get("/test/twoResult");
    }

    @Test
    public void docTest1() {
        loggedAndExpect().statusCode(HttpStatus.SC_OK).body("size()", Matchers.greaterThanOrEqualTo(10)).when()
                .get("/test/docTest/RtFM");
    }

    @Test
    public void docTest2() {
        loggedAndExpect().statusCode(HttpStatus.SC_FORBIDDEN).when().get("/test/docTest/myPass");
    }

    @Test
    public void docTest3() {
        loggedAndExpect().statusCode(HttpStatus.SC_FORBIDDEN).when().get("/test/docTest/");
    }

    @Test
    public void testRead1() {
        loggedAndExpect().body("conId", Matchers.equalTo(2)).statusCode(HttpStatus.SC_OK).when().get("/test/2");
    }

    @Test
    public void testRead2() {
        loggedAndExpect().body("globalErrors", Matchers.contains("Contact #30 unknown"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().get("/test/30");
    }

    @Test
    public void testExportContacts() {
        loggedAndExpect()
                .header("Content-Disposition",
                        Matchers.equalToIgnoringCase(
                                "attachment;filename=\"contacts.pdf\";filename*=UTF-8''contacts.pdf"))
                // greaterThanOrEqualTo because it depends of previously inserted elements
                .header("Content-Length", Matchers.greaterThanOrEqualTo("2572")).statusCode(HttpStatus.SC_OK).when()
                .get("/test/export/pdf/");
    }

    @Test
    public void testExportOneContact() {
        loggedAndExpect()
                .header("Content-Disposition",
                        Matchers.equalToIgnoringCase(
                                "attachment;filename=\"contact2.pdf\";filename*=UTF-8''contact2.pdf"))
                .header("Content-Length", Matchers.equalTo("1703")).statusCode(HttpStatus.SC_OK).when()
                .get("/test/export/pdf/2");
    }

    @Test
    public void testGrantAccessToken() {
        loggedAndExpect().statusCode(HttpStatus.SC_NO_CONTENT).header(HEADER_ACCESS_TOKEN, Matchers.notNullValue())
                .when().get("/test/grantAccess");
    }

    @Test
    public void testNoAccessToken() {
        loggedAndExpect().statusCode(HttpStatus.SC_FORBIDDEN).when().get("/test/limitedAccess/3");
    }

    @Test
    public void testBadAccessToken() {
        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, "badToken")).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .get("/test/limitedAccess/3");
    }

    @Test
    public void testLimitedAccessToken() {
        final String headerAccessToken = given().filter(loggedSessionFilter).get("/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, headerAccessToken)).body("conId", Matchers.equalTo(3))
                .statusCode(HttpStatus.SC_OK).when().get("/test/limitedAccess/3");
    }

    @Test
    public void testLimitedAccessToken2() {
        final String headerAccessToken = given().filter(loggedSessionFilter).get("/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, headerAccessToken)).body("conId", Matchers.equalTo(3))
                .statusCode(HttpStatus.SC_OK).when().get("/test/limitedAccess/3");

        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, headerAccessToken)).body("conId", Matchers.equalTo(3))
                .statusCode(HttpStatus.SC_OK).when().get("/test/limitedAccess/3");
    }

    @Test
    public void testAccessTokenConsume() {
        final String headerAccessToken = given().filter(loggedSessionFilter).get("/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, headerAccessToken)).body("conId", Matchers.equalTo(1))
                .statusCode(HttpStatus.SC_OK).when().get("/test/oneTimeAccess/1");
    }

    @Test
    public void testAccessTokenConsume2() {
        final String headerAccessToken = given().filter(loggedSessionFilter).get("/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, headerAccessToken)).body("conId", Matchers.equalTo(1))
                .statusCode(HttpStatus.SC_OK).when().get("/test/oneTimeAccess/1");

        loggedAndExpect(given().header(HEADER_ACCESS_TOKEN, headerAccessToken)).statusCode(HttpStatus.SC_FORBIDDEN)
                .when().get("/test/oneTimeAccess/1");
    }

    @Test
    public void testFilteredRead() {
        loggedAndExpect().body("conId", Matchers.equalTo(1)).body("honorificCode", Matchers.notNullValue())
                .body("name", Matchers.notNullValue()).body("firstName", Matchers.notNullValue())
                .body("birthday", Matchers.nullValue()) //excluded field
                .body("email", Matchers.nullValue()) //excluded field
                .statusCode(HttpStatus.SC_OK).when().get("/test/filtered/1");
    }

    @Test
    public void testAnonymousGrantAccessToken() {
        RestAssured.given().filter(anonymousSessionFilter).expect().log().ifValidationFails()
                .statusCode(HttpStatus.SC_NO_CONTENT).header(HEADER_ACCESS_TOKEN, Matchers.notNullValue()).when()
                .get("/anonymous/test/grantAccess");
    }

    @Test
    public void testAnonymousNoAccessToken() {
        RestAssured.given().filter(anonymousSessionFilter).expect().log().ifValidationFails()
                .statusCode(HttpStatus.SC_UNAUTHORIZED).when().get("/anonymous/test/limitedAccess/3");
    }

    @Test
    public void testAnonymousBadAccessToken() {
        given().header(HEADER_ACCESS_TOKEN, "badToken").filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().statusCode(HttpStatus.SC_UNAUTHORIZED).when()
                .get("/anonymous/test/limitedAccess/3");
    }

    @Test
    public void testAnonymousLimitedAccessToken() {
        final String headerAccessToken = given().filter(anonymousSessionFilter).get("/anonymous/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        given().header(HEADER_ACCESS_TOKEN, headerAccessToken).filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().body("conId", Matchers.equalTo(3)).statusCode(HttpStatus.SC_OK).when()
                .get("/anonymous/test/limitedAccess/3");
    }

    @Test
    public void testAnonymousLimitedAccessToken2() {
        final String headerAccessToken = given().filter(anonymousSessionFilter).get("/anonymous/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        given().header(HEADER_ACCESS_TOKEN, headerAccessToken).filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().body("conId", Matchers.equalTo(3)).statusCode(HttpStatus.SC_OK).when()
                .get("/anonymous/test/limitedAccess/3");

        given().header(HEADER_ACCESS_TOKEN, headerAccessToken).filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().body("conId", Matchers.equalTo(3)).statusCode(HttpStatus.SC_OK).when()
                .get("/anonymous/test/limitedAccess/3");
    }

    @Test
    public void testAnonymousAccessTokenConsume() {
        final String headerAccessToken = given().filter(anonymousSessionFilter).get("/anonymous/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        given().header(HEADER_ACCESS_TOKEN, headerAccessToken).filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().body("conId", Matchers.equalTo(1)).statusCode(HttpStatus.SC_OK).when()
                .get("/anonymous/test/oneTimeAccess/1");
    }

    @Test
    public void testAnonymousAccessTokenConsume2() {
        final String headerAccessToken = given().filter(anonymousSessionFilter).get("/anonymous/test/grantAccess")
                .header(HEADER_ACCESS_TOKEN);

        given().header(HEADER_ACCESS_TOKEN, headerAccessToken).filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().body("conId", Matchers.equalTo(1)).statusCode(HttpStatus.SC_OK).when()
                .get("/anonymous/test/oneTimeAccess/1");

        given().header(HEADER_ACCESS_TOKEN, headerAccessToken).filter(anonymousSessionFilter).expect().log()
                .ifValidationFails().statusCode(HttpStatus.SC_FORBIDDEN).when()
                .get("/anonymous/test/oneTimeAccess/1");
    }

    @Test
    public void testPostContact() throws ParseException {
        doCreateContact();
    }

    private Map<String, Object> doCreateContact() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);

        final Long conId = loggedAndExpect(given().body(newContact)).body("conId", Matchers.notNullValue())
                .statusCode(HttpStatus.SC_CREATED).when().post("/test/contact").body().path("conId");
        newContact.put("conId", conId);
        return newContact;
    }

    @Test
    public void testPostContactValidatorError() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(100L);

        loggedAndExpect(given().body(newContact)).body("fieldErrors.conId", Matchers.contains("Id must not be set"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/contact");

        final Map<String, Object> new2Contact = createDefaultContact(null);
        new2Contact.put("birthday", convertDate("24/10/2012"));

        loggedAndExpect(given().body(new2Contact))
                .body("fieldErrors.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/contact");
    }

    @Test
    public void testPostContactUserException() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);
        newContact.put("name", null);

        loggedAndExpect(given().body(newContact)).body("globalErrors", Matchers.contains("Name is mandatory"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/contact");
    }

    @Test
    public void testJsonSyntaxError() {
        final String[] testOkJson = { "{ \"firstName\" : \"test\" }", "{ firstName : \"test\" }",
                "{ 'firstName' : \"test\" }", "{\n\t\"firstName\" : \"test\"\n}" };
        final String[] testBadInterpretedJson = { "{ \"firstName\" : test\" }" };
        final String[] testBadSyntaxJson = { " \"firstName\" : \"test\" }", "{ \"firstName : \"test\" }",
                "{ firstName\" : \"test\" }", "{ \"firstName\"  \"test\" }", "{ \"firstName\" : \"test }",
                "{ \"firstName\" : \"test\" " };

        for (final String testJson : testOkJson) {
            loggedAndExpect(given().body(testJson)).body("firstName", Matchers.equalTo("test"))
                    .statusCode(HttpStatus.SC_OK).when().put("/test/contactSyntax");
        }

        for (final String testJson : testBadInterpretedJson) {
            loggedAndExpect(given().body(testJson)).body("firstName", Matchers.equalTo("test\""))
                    .statusCode(HttpStatus.SC_OK).when().put("/test/contactSyntax");
        }

        for (final String testJson : testBadSyntaxJson) {
            loggedAndExpect(given().body(testJson))
                    .body("globalErrors",
                            Matchers.contains("Error parsing param :Body:[1] on service PUT /test/contactSyntax"))
                    .statusCode(HttpStatus.SC_BAD_REQUEST).when().put("/test/contactSyntax");
        }
    }

    @Test
    public void testWsUrlError() {
        final String[] testOkJson = { "{ \"firstName\" : \"test\" }", "{ firstName : \"test\" }",
                "{ 'firstName' : \"test\" }", "{\n\t\"firstName\" : \"test\"\n}" };
        for (final String testJson : testOkJson) {
            loggedAndExpect(given().body(testJson)).body("firstName", Matchers.equalTo("test"))
                    .statusCode(HttpStatus.SC_OK).when().put("/test/contactUrl99");
        }
    }

    @Test
    public void testPutContact() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(100L);

        loggedAndExpect(given().body(newContact)).body("conId", Matchers.equalTo(100))
                .body("honorificCode", Matchers.notNullValue()).body("name", Matchers.notNullValue())
                .body("firstName", Matchers.notNullValue()).body("birthday", Matchers.notNullValue())
                .body("email", Matchers.notNullValue()).statusCode(HttpStatus.SC_OK).when().put("/test/contact");
    }

    @Test
    public void testPutContactEmptyField() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(100L);
        newContact.put("name", "");

        loggedAndExpect(given().body(newContact))
                .body("fieldErrors.name", Matchers.contains("Le champ doit tre renseign")) //autovalidation by Vega
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact");

        //----

        final Map<String, Object> newContact2 = createDefaultContact(100L);
        newContact2.put("name", null);

        loggedAndExpect(given().body(newContact2)).body("globalErrors", Matchers.contains("Name is mandatory")) //WS manual validation
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact");

        //----

        final Map<String, Object> newContact3 = createDefaultContact(100L);
        newContact3.remove("name");

        loggedAndExpect(given().body(newContact3)).body("globalErrors", Matchers.contains("Name is mandatory")) //WS manual validation
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact");
    }

    @Test
    public void testGetContactView() {
        loggedAndExpect().body("honorificCode", Matchers.notNullValue()).body("name", Matchers.notNullValue())
                .body("firstName", Matchers.notNullValue()).body("addresses.size()", Matchers.equalTo(3))
                .statusCode(HttpStatus.SC_OK).when().get("/contacts/contactView/1");
    }

    @Test
    public void testPutContactView() throws ParseException {
        final Map<String, Object> newContactView = createDefaultContact(100L);

        final List<Map<String, Object>> addresses = new ListBuilder<Map<String, Object>>()
                .add(createAddress(10L, "10, avenue Claude Vellefaux", "", "Paris", "75010", "France"))
                .add(createAddress(24L, "24, avenue General De Gaulle", "", "Paris", "75001", "France"))
                .add(createAddress(38L, "38, impasse des puits", "", "Versaille", "78000", "France")).build();

        newContactView.remove("address");
        newContactView.put("addresses", addresses);

        loggedAndExpect(given().body(newContactView)).body("honorificCode", Matchers.notNullValue())
                .body("name", Matchers.notNullValue()).body("firstName", Matchers.notNullValue())
                .body("birthday", Matchers.notNullValue()).body("email", Matchers.notNullValue())
                .body("addresses.size()", Matchers.equalTo(3)).statusCode(HttpStatus.SC_OK).when()
                .put("/contacts/contactView");
    }

    @Ignore("Not supported yet")
    @Test
    public void testPutContactViewError() throws ParseException {
        final Map<String, Object> newContactView = createDefaultContact(100L);
        final List<Map<String, Object>> addresses = new ListBuilder<Map<String, Object>>()
                .add(createAddress(10L, "10, avenue Claude Vellefaux", "", "Paris", "75010", "France"))
                .add(createAddress(24L, "24, avenue General De Gaulle", "", "Paris", "75001", "France"))
                .add(createAddress(38L,
                        "38, impasse des puits -- too long -- overrided DO_TEXT_50 length constraint -- too long -- too long",
                        "", "Versaille", "78000", "France"))
                .build();

        newContactView.remove("address");
        newContactView.put("addresses", addresses);

        loggedAndExpect(given().body(newContactView)).body("fieldErrors", Matchers.notNullValue())
                //.body("fieldErrors.addresses[2]", Matchers.notNullValue())
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/contacts/contactView");
    }

    @Test
    public void testPutContactByPath() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);

        loggedAndExpect(given().body(newContact)).body("conId", Matchers.equalTo(101))
                .body("honorificCode", Matchers.notNullValue()).body("name", Matchers.notNullValue())
                .body("firstName", Matchers.notNullValue()).body("birthday", Matchers.notNullValue())
                .body("email", Matchers.notNullValue()).statusCode(HttpStatus.SC_OK).when()
                .put("/test/contact/101");
    }

    @Test
    public void testPutContactValidatorError() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);

        loggedAndExpect(given().body(newContact)).body("fieldErrors.conId", Matchers.contains("Id is mandatory"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact");

        final Map<String, Object> new2Contact = createDefaultContact(100L);
        new2Contact.put("birthday", convertDate("24/10/2012"));

        loggedAndExpect(given().body(new2Contact))
                .body("fieldErrors.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact");
    }

    @Test
    public void testPutContactByPathValidatorError() throws ParseException {
        final Map<String, Object> new2Contact = createDefaultContact(null);
        new2Contact.put("birthday", convertDate("24/10/2012"));

        loggedAndExpect(given().body(new2Contact))
                .body("fieldErrors.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact/101");
    }

    @Test
    public void testPutContactUserException() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(100L);
        newContact.remove("name");

        loggedAndExpect(given().body(newContact)).body("globalErrors", Matchers.contains("Name is mandatory"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact");
    }

    @Test
    public void testPutContactByPathUserException() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);
        newContact.remove("name");

        loggedAndExpect(given().body(newContact)).body("globalErrors", Matchers.contains("Name is mandatory"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contact/101");
    }

    @Test
    public void testDeleteContact() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);
        loggedAndExpect(given().body(newContact)).statusCode(HttpStatus.SC_OK).when().put("/test/contact/105");

        loggedAndExpect(given().body(newContact)).statusCode(HttpStatus.SC_NO_CONTENT).when()
                .delete("/test/contact/105");
    }

    @Test
    public void testDeleteContactErrors() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);
        loggedAndExpect(given().body(newContact)).statusCode(HttpStatus.SC_OK).when().put("/test/contact/106");

        loggedAndExpect(given().body(newContact)).statusCode(HttpStatus.SC_NO_CONTENT).when()
                .delete("/test/contact/106");

        loggedAndExpect(given().body(newContact)).body("globalErrors", Matchers.contains("Contact #106 unknown"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().delete("/test/contact/106");

        loggedAndExpect(given().body(newContact))
                .body("globalErrors", Matchers.contains("You don't have enought rights"))
                .statusCode(HttpStatus.SC_FORBIDDEN).when().delete("/test/contact/2");
    }

    @Test
    public void testPostInnerBodyObject() {
        final Map<String, String> contactFrom = given().filter(loggedSessionFilter).when().get("/test/5").body()
                .as(Map.class);

        final Map<String, String> contactTo = given().filter(loggedSessionFilter).when().get("/test/6").body()
                .as(Map.class);

        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactFrom", contactFrom)
                .put("contactTo", contactTo).build();

        loggedAndExpect(given().body(fullBody)).body("size()", Matchers.equalTo(2))
                .body("get(0).conId", Matchers.equalTo(5)).body("get(0).firstName", Matchers.notNullValue())
                .body("get(1).conId", Matchers.equalTo(6)).body("get(1).firstName", Matchers.notNullValue())
                .statusCode(HttpStatus.SC_OK).when().post("/test/innerbody");
    }

    @Test
    public void testPostInnerBodyObjectFieldErrors() {
        final Map<String, String> contactFrom = given().filter(loggedSessionFilter).when().get("/test/5").body()
                .as(Map.class);
        contactFrom.put("email", "notAnEmail");

        final Map<String, String> contactTo = given().filter(loggedSessionFilter).when().get("/test/6").body()
                .as(Map.class);
        contactTo.put("firstName", "MoreThan50CharactersIsTooLongForAFirstNameInThisTestApi");

        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactFrom", contactFrom)
                .put("contactTo", contactTo).build();

        loggedAndExpect(given().body(fullBody))
                .body("objectFieldErrors.contactFrom.email", Matchers.contains("Le courriel n'est pas valide"))
                .body("objectFieldErrors.contactTo.firstName",
                        Matchers.contains("<<fr:DYNAMO_CONSTRAINT_STRINGLENGTH_EXCEEDED[50]>>"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/innerbody");
    }

    @Test
    public void testPostInnerBodyOptionalPresentObject() {
        final Map<String, String> contactFrom = given().filter(loggedSessionFilter).when().get("/test/5").body()
                .as(Map.class);

        final Map<String, String> contactTo = given().filter(loggedSessionFilter).when().get("/test/6").body()
                .as(Map.class);

        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactFrom", contactFrom)
                .put("contactTo", contactTo).build();

        loggedAndExpect(given().body(fullBody)).body("size()", Matchers.equalTo(2))
                .body("get(0).conId", Matchers.equalTo(5)).body("get(0).firstName", Matchers.notNullValue())
                .body("get(1).conId", Matchers.equalTo(6)).body("get(1).firstName", Matchers.notNullValue())
                .statusCode(HttpStatus.SC_OK).when().post("/test/innerbodyOptional");
    }

    @Test
    public void testPostInnerBodyOptionalEmptyObject() {
        final Map<String, String> contactFrom = given().filter(loggedSessionFilter).when().get("/test/5").body()
                .as(Map.class);

        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactFrom", contactFrom)
                .build();

        loggedAndExpect(given().body(fullBody)).body("size()", Matchers.equalTo(1))
                .body("get(0).conId", Matchers.equalTo(5)).body("get(0).firstName", Matchers.notNullValue())
                .statusCode(HttpStatus.SC_OK).when().post("/test/innerbodyOptional");
    }

    @Test
    public void testPostInnerBodyValidationErrors() throws ParseException {
        final Map<String, Object> contactFrom = createDefaultContact(140L);
        final Map<String, Object> contactTo = createDefaultContact(141L);

        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactFrom", contactFrom)
                .put("contactTo", contactTo).build();

        loggedAndExpect(given().body(fullBody))
                .body("objectFieldErrors.contactFrom.firstName", Matchers.contains("Process validation error"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/innerBodyValidationErrors");
    }

    @Test
    public void testPostInnerBodyLong() {
        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactId1", 6)
                .put("contactId2", 7).build();

        loggedAndExpect(given().body(fullBody)).body("size()", Matchers.equalTo(2))
                .body("get(0).conId", Matchers.equalTo(6)).body("get(0).firstName", Matchers.notNullValue())
                .body("get(1).conId", Matchers.equalTo(7)).body("get(1).firstName", Matchers.notNullValue())
                .statusCode(HttpStatus.SC_OK).when().post("/test/innerLong");
    }

    @Test
    public void testInnerBodyLongToDtList() {
        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactId1", 6)
                .put("contactId2", 7).build();

        loggedAndExpect(given().body(fullBody)).body("serverToken", Matchers.notNullValue())
                .body("value.size()", Matchers.equalTo(2)).body("value.get(0).conId", Matchers.equalTo(6))
                .body("value.get(0).firstName", Matchers.notNullValue())
                .body("value.get(1).conId", Matchers.equalTo(7))
                .body("value.get(1).firstName", Matchers.notNullValue()).statusCode(HttpStatus.SC_OK).when()
                .post("/test/innerLongToDtList");
    }

    @Test
    public void testUiContext() {
        final Map<String, Object> fullBody = new MapBuilder<String, Object>().put("contactId1", 6)
                .put("contactId2", 7).build();

        loggedAndExpect(given().body(fullBody)).body("contactFrom.name", Matchers.equalTo("Moreau"))
                .body("contactFrom.serverToken", Matchers.notNullValue())
                .body("contactTo.name", Matchers.equalTo("Lefebvre"))
                .body("contactTo.serverToken", Matchers.notNullValue()).body("testLong", Matchers.equalTo(12))
                .body("testString", Matchers.equalTo("the String test"))
                //.body("testDate", Matchers.any(Date.class)) //get a string no directly a Date can't ensured
                .body("testEscapedString", Matchers.equalTo("the EscapedString \",} test"))
                .statusCode(HttpStatus.SC_OK).when().post("/test/uiContext");
    }

    @Test
    public void testFilteredUpdateByExclude() throws ParseException {
        final Map<String, Object> contact = doGetServerSideObject();

        final Long oldConId = (Long) contact.get("conId");
        final String oldName = (String) contact.get("name");
        final String oldFirstName = (String) contact.get("firstName");
        final String oldEmail = (String) contact.get("email");
        final String newFirstName = oldFirstName + "FNTest";
        final String newEmail = "ETest." + oldEmail;

        contact.remove("conId"); //can't modify conId
        contact.remove("name"); //can't modify name
        //contact.put("conId", 1000L);
        //contact.put("name", newName);
        contact.put("firstName", newFirstName);
        contact.put("email", newEmail);

        loggedAndExpect(given().body(contact)).body("conId", Matchers.equalTo(oldConId))//not changed
                .body("honorificCode", Matchers.equalTo(contact.get("honorificCode")))
                .body("name", Matchers.equalTo(oldName)) //not changed
                .body("firstName", Matchers.equalTo(newFirstName))
                .body("birthday", Matchers.equalTo(contact.get("birthday")))
                .body("email", Matchers.equalTo(newEmail)).statusCode(HttpStatus.SC_OK).when()
                .put("/test/filtered/" + oldConId);
    }

    @Test
    public void testFilteredUpdateByExcludeErrors() throws ParseException {
        final Map<String, Object> contact = doGetServerSideObject();
        final Long oldConId = (Long) contact.get("conId");

        contact.put("conId", 1000L);//can't modify conId
        contact.put("name", "test"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filtered/" + oldConId);

        contact.put("conId", 1000L);//can't modify conId
        contact.remove("name"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filtered/" + oldConId);

        contact.remove("conId");//can't modify conId
        contact.put("name", "test"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filtered/" + oldConId);

        contact.remove("conId");//can't modify conId
        contact.remove("name"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_OK).when()
                .put("/test/filtered/" + oldConId);
    }

    @Test
    public void testFilteredUpdateByInclude() throws ParseException {
        final Map<String, Object> contact = doGetServerSideObject();

        final Long oldConId = (Long) contact.get("conId");
        final String oldName = (String) contact.get("name");
        final String oldFirstName = (String) contact.get("firstName");
        final String oldEmail = (String) contact.get("email");
        final String oldHonorificCode = (String) contact.get("honorificCode");
        final String oldBirthday = (String) contact.get("birthday");
        final String newFirstName = oldFirstName + "FNTest";
        final String newEmail = "ETest." + oldEmail;

        contact.remove("conId"); //can't modify conId
        contact.remove("name"); //can't modify name
        contact.put("firstName", newFirstName);
        contact.put("email", newEmail);
        contact.remove("honorificCode"); //can't modify conId
        contact.remove("birthday"); //can't modify name

        loggedAndExpect(given().body(contact)).body("conId", Matchers.equalTo(oldConId))//not changed
                .body("honorificCode", Matchers.equalTo(oldHonorificCode)) //not changed
                .body("name", Matchers.equalTo(oldName)) //not changed
                .body("firstName", Matchers.equalTo(newFirstName))// changed
                .body("birthday", Matchers.equalTo(oldBirthday))//not changed
                .body("email", Matchers.equalTo(newEmail))// changed
                .statusCode(HttpStatus.SC_OK).when().put("/test/filteredInclude/" + oldConId);

    }

    @Test
    public void testFilteredUpdateByIncludeErrors() throws ParseException {
        final Map<String, Object> contact = doGetServerSideObject();
        final Long oldConId = (Long) contact.get("conId");

        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.put("conId", 1000L); //can't modify conId
        contact.remove("name"); //can't modify name
        contact.remove("firstName");
        contact.remove("email");
        contact.remove("honorificCode"); //can't modify conId
        contact.remove("birthday"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.remove("conId");//can't modify conId
        contact.put("firstName", "test"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_OK).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.remove("firstName");
        contact.put("name", "test"); //can't modify name
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.remove("name"); //can't modify name
        contact.put("email", "test@test.com");
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_OK).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.put("honorificCode", "test"); //can't modify honorificCode
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.remove("honorificCode"); //can't modify honorificCode
        contact.put("birthday", convertDate("24/10/1985")); //can't modify birthday
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filteredInclude/" + oldConId);

        contact.remove("birthday"); //can't modify honorificCode
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_OK).when()
                .put("/test/filteredInclude/" + oldConId);
    }

    @Test
    public void testFilteredUpdateServerTokenErrors() throws ParseException {
        final Map<String, Object> contact = doGetServerSideObject();
        final Long oldConId = (Long) contact.get("conId");

        contact.remove("conId");//can't modify conId
        contact.remove("name"); //can't modify name

        contact.put("serverToken", "badToken");
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filtered/" + oldConId);

        contact.remove("serverToken");
        loggedAndExpect(given().body(contact)).statusCode(HttpStatus.SC_FORBIDDEN).when()
                .put("/test/filtered/" + oldConId);
    }

    @Test
    public void testPutContactTooLongField() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);
        final String newNameValue = "Here i am !!";
        newContact.put("itsatoolongaliasforfieldcontactname", newNameValue);

        loggedAndExpect(given().body(newContact)).body("conId", Matchers.equalTo(101))
                .body("name", Matchers.equalTo(newNameValue)).statusCode(HttpStatus.SC_OK).when()
                .put("/test/contactAliasName/101");
    }

    @Test
    public void testGetContactExtended() {
        loggedAndExpect().body("conId", Matchers.equalTo(1))
                .body("vanillaUnsupportedMultipleIds", Matchers.iterableWithSize(3))
                .body("vanillaUnsupportedMultipleIds", Matchers.hasItems(1, 2, 3))
                .header("content-type", Matchers.containsString("json+entity=Contact+meta"))
                .statusCode(HttpStatus.SC_OK).when().get("/test/contactExtended/1");
    }

    @Test
    public void testPutContactExtended() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(103L);
        newContact.remove("conId");
        newContact.put("vanillaUnsupportedMultipleIds", new int[] { 3, 4, 5 });

        loggedAndExpect(given().body(newContact)).body("conId", Matchers.equalTo(103))
                .body("vanillaUnsupportedMultipleIds", Matchers.iterableWithSize(3))
                .body("vanillaUnsupportedMultipleIds", Matchers.hasItems(3, 4, 5)).statusCode(HttpStatus.SC_OK)
                .when().put("/test/contactExtended/103");
    }

    @Test
    public void testPutContactExtendedValidator() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(104L);
        newContact.put("vanillaUnsupportedMultipleIds", new int[] { 3, 4, 5 });

        loggedAndExpect(given().body(newContact)).body("fieldErrors.conId", Matchers.contains("Id must not be set"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contactExtended/104");

        newContact.remove("conId");
        newContact.put("birthday", convertDate("24/10/2012"));
        loggedAndExpect(given().body(newContact))
                .body("fieldErrors.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().put("/test/contactExtended/104");
    }

    @Test
    public void testPostCharset() throws UnsupportedEncodingException {
        final String testFirstName = "Grard";
        final String testJson = "{ \"firstName\" : \"" + testFirstName + "\" }";
        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=UTF-8")
                .body(Collections.singletonMap("firstName", testFirstName)) //RestAssured read encodetype and encode as UTF8
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=UTF-8").body(testJson.getBytes("UTF-8")) //We force the encode charset
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=ISO-8859-1")
                .body(Collections.singletonMap("firstName", testFirstName)) //RestAssured read encodetype and encode as ISO-8859-1
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=ISO-8859-1").body(testJson.getBytes("ISO-8859-1")) //We force the encode charset
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");
    }

    @Test
    public void testPostCharsetUtf8() throws UnsupportedEncodingException {
        final String testFirstName = UTF8_TEST_STRING;
        final String testJson = "{ \"firstName\" : \"" + testFirstName + "\" }";
        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=UTF-8")
                .body(Collections.singletonMap("firstName", testFirstName)) //RestAssured read encodetype and encode as UTF8
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=UTF-8").body(testJson.getBytes("UTF-8")) //We force the encode charset
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");
    }

    @Test
    public void testPostCharsetDefaultUtf8() throws UnsupportedEncodingException {
        final String testFirstName = UTF8_TEST_STRING;
        final String testJson = "{ \"firstName\" : \"" + testFirstName + "\" }";

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset") //We precise an incomplete charset otherwise Restassured add a default charset=ISO-8859-1 to contentType
                .body(testJson.getBytes("UTF-8")) //We force the encode charset
                .expect().body("firstName", Matchers.equalTo(testFirstName)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");
    }

    @Test
    public void testPutContactCharsetIso8859() throws UnsupportedEncodingException {
        final String testedCharset = "ISO-8859-1";
        final String testFirstName = UTF8_TEST_STRING;
        final String testFirstNameIso = new String(UTF8_TEST_STRING.getBytes(testedCharset), testedCharset);
        final String testJson = "{ \"firstName\" : \"" + testFirstName + "\" }";

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=" + testedCharset)
                .body(Collections.singletonMap("firstName", testFirstName)).expect()
                .body("firstName", Matchers.equalTo(testFirstNameIso)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");

        given().filter(loggedSessionFilter) //logged
                .contentType("application/json;charset=" + testedCharset).body(testJson.getBytes(testedCharset))
                .expect().body("firstName", Matchers.equalTo(testFirstNameIso)).statusCode(HttpStatus.SC_OK).when()
                .post("/test/charset");
    }

    @Test
    public void testSearchQueryPagined() throws ParseException {
        doTestSearchPagined(false);
    }

    @Test
    public void testSearchAutoPagined() throws ParseException {
        doTestSearchPagined(true);
    }

    private void doTestSearchPagined(final boolean isAuto) throws ParseException {
        final Map<String, Object> criteriaContact = new MapBuilder<String, Object>()
                .put("birthdayMin", convertDate("19/05/1978")).put("birthdayMax", convertDate("19/05/1985"))
                .build();

        final String serverSideToken;
        serverSideToken = doPaginedSearch(criteriaContact, 3, 0, "name", false, null, 3, "Dubois", "Garcia",
                isAuto);
        doPaginedSearch(criteriaContact, 3, 2, "name", false, serverSideToken, 3, "Garcia", "Moreau", isAuto);
        doPaginedSearch(criteriaContact, 3, 5, "name", false, serverSideToken, 1, "Petit", "Petit", isAuto);
        doPaginedSearch(criteriaContact, 10, 10, "name", false, serverSideToken, 0, "Petit", "Petit", isAuto);
    }

    @Test
    public void testSearchQueryPaginedSortName() throws ParseException {
        doTestSearchPaginedSortName(false);
    }

    @Test
    public void testSearchAutoPaginedSortName() throws ParseException {
        doTestSearchPaginedSortName(true);
    }

    private void doTestSearchPaginedSortName(final boolean isAuto) throws ParseException {
        final Map<String, Object> criteriaContact = new MapBuilder<String, Object>()
                .put("birthdayMin", convertDate("19/05/1978")).put("birthdayMax", convertDate("19/05/1985"))
                .build();
        //gets : "Dubois","Durant","Garcia","Martin","Moreau","Petit"

        final String serverSideToken;
        serverSideToken = doPaginedSearch(criteriaContact, 3, 0, "name", false, null, 3, "Dubois", "Garcia",
                isAuto);
        doPaginedSearch(criteriaContact, 10, 0, "name", false, serverSideToken, 6, "Dubois", "Petit", isAuto);
        doPaginedSearch(criteriaContact, 4, 0, "name", false, serverSideToken, 4, "Dubois", "Martin", isAuto);
        doPaginedSearch(criteriaContact, 4, 0, "name", true, serverSideToken, 4, "Petit", "Garcia", isAuto);
        doPaginedSearch(criteriaContact, 4, 1, "name", true, serverSideToken, 4, "Moreau", "Durant", isAuto);
        doPaginedSearch(criteriaContact, 4, 1, "name", false, serverSideToken, 4, "Durant", "Moreau", isAuto);
    }

    @Test
    public void testSearchQueryPaginedSortDate() throws ParseException {
        doTestSearchPaginedSortDate(false);
    }

    @Test
    public void testSearchAutoPaginedSortDate() throws ParseException {
        doTestSearchPaginedSortDate(true);
    }

    private void doTestSearchPaginedSortDate(final boolean isAuto) throws ParseException {
        final Map<String, Object> criteriaContact = new MapBuilder<String, Object>()
                .put("birthdayMin", convertDate("19/05/1978")).put("birthdayMax", convertDate("19/05/1985"))
                .build();

        final String serverSideToken;
        serverSideToken = doPaginedSearch(criteriaContact, 3, 0, "name", false, null, 3, "Dubois", "Garcia",
                isAuto);
        doPaginedSearch(criteriaContact, 10, 0, "birthday", false, serverSideToken, 6, "Petit", "Garcia", isAuto);
        doPaginedSearch(criteriaContact, 3, 1, "birthday", false, serverSideToken, 3, "Martin", "Durant", isAuto);
        doPaginedSearch(criteriaContact, 3, 1, "birthday", true, serverSideToken, 3, "Moreau", "Dubois", isAuto);
    }

    @Test
    public void testSearchQueryPaginedMissing() throws ParseException {
        doTestSearchPaginedMissing(false);
    }

    @Test
    public void testSearchAutoPaginedMissing() throws ParseException {
        doTestSearchPaginedMissing(true);
    }

    private void doTestSearchPaginedMissing(final boolean isAuto) throws ParseException {
        final Map<String, Object> criteriaContact = new MapBuilder<String, Object>()
                .put("birthdayMin", convertDate("19/05/1978")).put("birthdayMax", convertDate("19/05/1985"))
                .build();

        String serverSideToken;
        serverSideToken = doPaginedSearch(criteriaContact, 3, 0, "name", false, null, 3, "Dubois", "Garcia",
                isAuto);
        doPaginedSearch(criteriaContact, null, null, null, null, serverSideToken, 6, "Martin", "Garcia", isAuto);
        doPaginedSearch(criteriaContact, 5, null, null, null, serverSideToken, 5, "Martin", "Moreau", isAuto);
        doPaginedSearch(criteriaContact, 5, 5, null, null, serverSideToken, 1, "Garcia", "Garcia", isAuto);
    }

    private String doPaginedSearch(final Map<String, Object> criteriaContact, final Integer top, final Integer skip,
            final String sortFieldName, final Boolean sortDesc, final String listServerToken,
            final int expectedSize, final String firstContactName, final String lastContactName,
            final boolean isAuto) {
        final RequestSpecification given = given().filter(loggedSessionFilter);
        final String wsUrl = isAuto ? "/test/_searchAutoPagined" : "/test/_searchQueryPagined";
        if (top != null) {
            given.queryParam("top", top);
        }
        if (skip != null) {
            given.queryParam("skip", skip);
        }
        if (sortFieldName != null) {
            given.queryParam("sortFieldName", sortFieldName);
        }
        if (sortDesc != null) {
            given.queryParam("sortDesc", sortDesc);
        }
        if (listServerToken != null) {
            given.queryParam("listServerToken", listServerToken);
        }
        ResponseSpecification responseSpecification = given.body(criteriaContact).expect().body("size()",
                Matchers.equalTo(expectedSize));
        if (expectedSize > 0) {
            responseSpecification = responseSpecification.body("get(0).name", Matchers.equalTo(firstContactName))
                    .body("get(" + (expectedSize - 1) + ").name", Matchers.equalTo(lastContactName));
        }
        final String newListServerToken = responseSpecification.statusCode(HttpStatus.SC_OK).when().post(wsUrl)
                .header("listServerToken");
        return newListServerToken;
    }

    @Test
    public void testLoadListMeta() {
        loggedAndExpect().header("testLong", Matchers.equalTo("12"))
                .header("testString", Matchers.equalTo("the String test"))
                .header("testDate", Matchers.notNullValue())
                //.header("testDate", Matchers.any(Date.class)) //get a string no directly a Date can't ensured
                .header("testEscapedString", Matchers.equalTo("the EscapedString \",} test"))
                .body("size()", Matchers.greaterThanOrEqualTo(10)).statusCode(HttpStatus.SC_OK).when()
                .get("/test/dtListMeta");
    }

    @Test
    public void testLoadListMetaAsList() {
        loggedAndExpect().header("testLong", Matchers.equalTo("12"))
                .header("testString", Matchers.equalTo("the String test"))
                .header("testDate", Matchers.notNullValue())
                //.header("testDate", Matchers.any(Date.class)) //get a string no directly a Date can't ensured
                .header("testEscapedString", Matchers.equalTo("the EscapedString \",} test"))
                .body("size()", Matchers.greaterThanOrEqualTo(10)).statusCode(HttpStatus.SC_OK).when()
                .get("/test/dtListMetaAsList");
    }

    @Test
    public void testLoadListComplexMeta() {
        loggedAndExpect().body("testLong", Matchers.equalTo(12))
                .body("testString", Matchers.equalTo("the String test")).body("testDate", Matchers.notNullValue())
                //.body("testDate", Matchers.any(Date.class)) //get a string no directly a Date can't ensured
                .body("testEscapedString", Matchers.equalTo("the EscapedString \",} test"))
                .body("contact1.name", Matchers.notNullValue()).body("contact2.name", Matchers.notNullValue())
                .body("size()", Matchers.equalTo(1 + 6)) //value field + meta fields
                .body("value.size()", Matchers.greaterThanOrEqualTo(10)) //list value
                .statusCode(HttpStatus.SC_OK).when().get("/test/listComplexMeta");
    }

    @Test
    public void testSaveListDelta() throws ParseException {
        final Map<String, Object> dtListDelta = new LinkedHashMap<>();
        final Map<String, Map<String, Object>> collCreates = new LinkedHashMap<>();
        final Map<String, Map<String, Object>> collUpdates = new LinkedHashMap<>();
        final Map<String, Map<String, Object>> collDeletes = new LinkedHashMap<>();
        dtListDelta.put("collCreates", collCreates);
        dtListDelta.put("collUpdates", collUpdates);
        dtListDelta.put("collDeletes", collDeletes);
        collCreates.put("c110", createDefaultContact(110L));
        collCreates.put("c111", createDefaultContact(111L));
        collUpdates.put("c100", createDefaultContact(100L));
        collUpdates.put("c101", createDefaultContact(101L));
        collUpdates.put("c102", createDefaultContact(102L));
        collDeletes.put("c90", createDefaultContact(90L));

        loggedAndExpect(given().body(dtListDelta))
                .body(Matchers.equalTo("OK : add 2 contacts, update 3 contacts, removed 1"))
                .statusCode(HttpStatus.SC_OK).when().post("/test/saveListDelta");
    }

    @Test
    public void testSaveListDeltaValdationError() throws ParseException {
        final Map<String, Object> dtListDelta = new LinkedHashMap<>();
        final Map<String, Map<String, Object>> collCreates = new LinkedHashMap<>();
        final Map<String, Map<String, Object>> collUpdates = new LinkedHashMap<>();
        final Map<String, Map<String, Object>> collDeletes = new LinkedHashMap<>();

        dtListDelta.put("collCreates", collCreates);
        dtListDelta.put("collUpdates", collUpdates);
        dtListDelta.put("collDeletes", collDeletes);

        collCreates.put("c110", createDefaultContact(110L));
        collCreates.put("c111", createDefaultContact(111L));
        final Map<String, Object> newContact = createDefaultContact(100L);
        newContact.put("birthday", convertDate("24/10/2012"));
        collUpdates.put("c100", newContact);
        collUpdates.put("c101", createDefaultContact(101L));
        collUpdates.put("c102", createDefaultContact(102L));
        collDeletes.put("c90", createDefaultContact(90L));

        loggedAndExpect(given().body(dtListDelta))
                .body("objectFieldErrors.c100.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/saveListDelta");
    }

    @Test
    public void testSaveDtListContact() throws ParseException {
        final List<Map<String, Object>> dtList = new ListBuilder<Map<String, Object>>()
                .add(createDefaultContact(120L)).add(createDefaultContact(121L)).add(createDefaultContact(123L))
                .add(createDefaultContact(124L)).add(createDefaultContact(125L)).add(createDefaultContact(126L))
                .build();

        loggedAndExpect(given().body(dtList)).body(Matchers.equalTo("OK : received 6 contacts"))
                .statusCode(HttpStatus.SC_OK).when().post("/test/saveDtListContact");
    }

    @Test
    public void testSaveDtListContactValidationError() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(123L);
        newContact.remove("name");

        final List<Map<String, Object>> dtList = new ListBuilder<Map<String, Object>>()
                .add(createDefaultContact(120L)).add(createDefaultContact(121L)).add(newContact)
                .add(createDefaultContact(124L)).add(createDefaultContact(125L)).add(createDefaultContact(126L))
                .build();

        loggedAndExpect(given().body(dtList)).body("globalErrors", Matchers.contains("Name is mandatory"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/saveDtListContact");

        final Map<String, Object> new2Contact = createDefaultContact(127L);
        new2Contact.put("birthday", convertDate("24/10/2012"));
        dtList.add(new2Contact);
        loggedAndExpect(given().body(dtList))
                .body("objectFieldErrors.idx6.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/saveDtListContact");
    }

    @Test
    public void testSaveListContact() throws ParseException {
        final List<Map<String, Object>> dtList = new ListBuilder<Map<String, Object>>()
                .add(createDefaultContact(130L)).add(createDefaultContact(131L)).add(createDefaultContact(133L))
                .add(createDefaultContact(134L)).add(createDefaultContact(135L)).build();

        loggedAndExpect(given().body(dtList)).body(Matchers.equalTo("OK : received 5 contacts"))
                .statusCode(HttpStatus.SC_OK).when().post("/test/saveListContact");
    }

    @Test
    public void testSaveDtListContactValidationError2() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(123L);
        newContact.put("birthday", convertDate("24/10/2012"));

        final List<Map<String, Object>> dtList = new ListBuilder<Map<String, Object>>()
                .add(createDefaultContact(120L)).add(createDefaultContact(121L)).add(newContact)
                .add(createDefaultContact(124L)).add(createDefaultContact(125L)).add(createDefaultContact(126L))
                .build();

        loggedAndExpect(given().body(dtList))
                .body("objectFieldErrors.idx2.birthday", Matchers.contains("You can't add contact younger than 16"))
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY).when().post("/test/saveDtListContact");
    }

    @Test
    public void testUploadFile() throws UnsupportedEncodingException {
        final URL imageUrl = Thread.currentThread().getContextClassLoader().getResource("npi2loup.png");
        final File imageFile = new File(URLDecoder.decode(imageUrl.getFile(), "UTF-8"));

        loggedAndExpect(given().multiPart("upfile", imageFile, "image/png").formParam("id", 12).formParam("note",
                "Some very important notes about this file."))
                        //expect
                        .header("Content-Type", Matchers.equalToIgnoringCase("image/png"))
                        .header("Content-Disposition",
                                Matchers.equalToIgnoringCase(
                                        "attachment;filename=\"npi2loup.png\";filename*=UTF-8''npi2loup.png"))
                        .header("Content-Length", Matchers.equalTo("27039")).statusCode(HttpStatus.SC_OK).when()//.log().headers()
                        .post("/test/uploadFile");

        loggedAndExpect(given().formParam("id", 12).formParam("note", "Some very important notes about this file.")
                .multiPart("upfile", imageFile, "image/png"))
                        //expect
                        .header("Content-Type", Matchers.equalToIgnoringCase("image/png"))
                        .header("Content-Disposition",
                                Matchers.equalToIgnoringCase(
                                        "attachment;filename=\"npi2loup.png\";filename*=UTF-8''npi2loup.png"))
                        .header("Content-Length", Matchers.equalTo("27039")).statusCode(HttpStatus.SC_OK).when()//.log().headers()
                        .post("/test/uploadFile");
    }

    @Test
    public void testUploadFileError() throws UnsupportedEncodingException {
        final URL imageUrl = Thread.currentThread().getContextClassLoader().getResource("npi2loup.png");
        final File imageFile = new File(URLDecoder.decode(imageUrl.getFile(), "UTF-8"));

        RestAssured.given().filter(loggedSessionFilter).given().multiPart("upFile", imageFile, "image/png")
                .formParam("id", 12).formParam("note", "Some very important notes about this file.").expect()
                .body("globalErrors",
                        Matchers.anyOf(Matchers.contains("File upfile not found. Parts sent : id, upFile, note"),
                                Matchers.contains("File upfile not found. Parts sent : note, upFile, id")))
                .statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR).when().post("/test/uploadFile");

        RestAssured.given().filter(loggedSessionFilter).given().formParam("id", 12)
                .formParam("note", "Some very important notes about this file.").formParam("upfile", imageFile)
                .expect()
                .body("globalErrors",
                        Matchers.contains(
                                "File upfile not found. Request contentType isn't \"multipart/form-data\""))
                .statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR).when().post("/test/uploadFile");
    }

    @Test
    public void testDownloadFileContentType() throws UnsupportedEncodingException {
        final String[] expectedSimpleNames = { "image0.png", "image1.png", "image2_.png",
                "image3%20_.png", "image4 __~.png",
                "image5  abcABC!#%&()=`@$ {[]}+^~'-_,_.png" };
        final String[] expectedEncodedNames = { "image0.png", "image1.png", "image2_.png",
                "image3 _.png", "image4__~.png",
                "image5abcABC!#%&()=`@${[]}+^~'-_,_.png" };

        for (int id = 0; id < expectedSimpleNames.length; id++) {
            final String expectedSimpleName = expectedSimpleNames[id];
            final String expectedEncodedName = URLEncoder.encode(expectedEncodedNames[id], "utf8").replace("+",
                    "%20");

            loggedAndExpect(given().queryParam("id", id))
                    .header("Content-Type", Matchers.equalToIgnoringCase("image/png"))
                    .header("Content-Disposition",
                            Matchers.equalToIgnoringCase("attachment;filename=\"" + expectedSimpleName
                                    + "\";filename*=UTF-8''" + expectedEncodedName))
                    .header("Content-Length", Matchers.equalTo("27039")).statusCode(HttpStatus.SC_OK).when()
                    .get("/test/downloadFileContentType");
        }
    }

    @Test
    public void testDownloadFile() {
        loggedAndExpect(given().queryParam("id", 10))
                .header("Content-Type", Matchers.equalToIgnoringCase("image/png"))
                .header("Content-Disposition",
                        Matchers.equalToIgnoringCase(
                                "attachment;filename=\"image10.png\";filename*=UTF-8''image10.png"))
                .header("Content-Length", Matchers.equalTo("27039")).statusCode(HttpStatus.SC_OK).when()
                .get("/test/downloadFile");
    }

    @Test
    public void testDownloadNotModifiedFile() throws ParseException {
        final DateFormat httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
        //Sans prciser le if-Modified-Since, le serveur retourne le fichier
        final Response response = loggedAndExpect(given().queryParam("id", 10))
                .header("Content-Type", Matchers.equalToIgnoringCase("image/png"))
                .header("Content-Disposition",
                        Matchers.equalToIgnoringCase(
                                "attachment;filename=\"image10.png\";filename*=UTF-8''image10.png"))
                .header("Content-Length", Matchers.equalTo("27039")).statusCode(HttpStatus.SC_OK).when()
                .get("/test/downloadNotModifiedFile");
        final String lastModified = response.getHeader("Last-Modified");
        final Date lastModifiedDate = httpDateFormat.parse(lastModified);
        final String now = httpDateFormat.format(new Date());

        //On test avec le if-Modified-Since now : le server test mais ne retourne pas le fichier
        loggedAndExpect(given().queryParam("id", 10).header("if-Modified-Since", now))
                .statusCode(HttpStatus.SC_NOT_MODIFIED).when().get("/test/downloadNotModifiedFile");

        //On test avec le if-Modified-Since 10 min avant le lastModified : le server test et retourne le fichier
        final String beforeLastModified = httpDateFormat
                .format(new DateBuilder(lastModifiedDate).addMinutes(-10).build());
        loggedAndExpect(given().queryParam("id", 10).header("if-Modified-Since", beforeLastModified))
                .header("Content-Type", Matchers.equalToIgnoringCase("image/png"))
                .header("Content-Disposition",
                        Matchers.equalToIgnoringCase(
                                "attachment;filename=\"image10.png\";filename*=UTF-8''image10.png"))
                .header("Content-Length", Matchers.equalTo("27039")).statusCode(HttpStatus.SC_OK).when()
                .get("/test/downloadNotModifiedFile");
    }

    @Test
    public void testDatesUTC() {
        final String inputUtc = "2016-01-18T17:21:42.026Z";
        loggedAndExpect(given()).body("input", Matchers.equalTo(inputUtc))
                .body("inputAsString", Matchers.equalTo("Mon Jan 18 18:21:42 CET 2016"))
                .statusCode(HttpStatus.SC_OK).when().get("/test/dates?date=" + inputUtc);
    }

    @Test
    public void testLocalDate() {
        loggedAndExpect(given()).body(Matchers.equalTo("\"2017-06-27\"")).statusCode(HttpStatus.SC_OK).when()
                .get("/test/localDate");

        final String inputLocalDate = "2016-01-18";
        loggedAndExpect(given()).body("input", Matchers.equalTo(inputLocalDate))
                .body("inputAsString", Matchers.equalTo("2016-01-18")).statusCode(HttpStatus.SC_OK).when()
                .put("/test/localDate?date=" + inputLocalDate);
    }

    @Test
    public void testZonedDateTime() {
        loggedAndExpect(given()).body(Matchers.equalTo("\"2016-05-26T21:30:20Z\"")).statusCode(HttpStatus.SC_OK)
                .when().get("/test/zonedDateTime");

        final String inputZonedDateTime = "2016-01-18T17:21:42Z";
        loggedAndExpect(given()).body("input", Matchers.equalTo(inputZonedDateTime))
                .body("inputAsString", Matchers.equalTo("2016-01-18T17:21:42Z[UTC]")).statusCode(HttpStatus.SC_OK)
                .when().put("/test/zonedDateTime?date=" + inputZonedDateTime);
    }

    @Test
    public void testZonedDateTimeUTC() {
        loggedAndExpect(given()).body(Matchers.equalTo("\"2016-07-27T22:00:00Z\"")).statusCode(HttpStatus.SC_OK)
                .when().get("/test/zonedDateTimeUTC");

        final String inputZonedDateTime = "2016-04-25T00:00:00Z";
        loggedAndExpect(given()).body("input", Matchers.equalTo(inputZonedDateTime))
                .body("inputAsString", Matchers.equalTo("2016-04-25T00:00Z[UTC]")).statusCode(HttpStatus.SC_OK)
                .when().put("/test/zonedDateTime?date=" + inputZonedDateTime);
    }

    @Test
    public void testString() {
        loggedAndExpect(given().body(UTF8_TEST_STRING)).statusCode(HttpStatus.SC_OK).log().all().when()
                .post("/test/string");
    }

    @Test
    public void testOptionalQueryParam() throws ParseException {
        final Map<String, Object> newContact = createDefaultContact(null);
        loggedAndExpect(given().body(newContact).queryParam("token", "TestedToken")).statusCode(HttpStatus.SC_OK)
                .body(Matchers.equalTo("TestedToken")).when().post("/test/contact/secured");

        loggedAndExpect(given().body(newContact)).statusCode(HttpStatus.SC_OK).body(Matchers.equalTo("empty"))
                .when().post("/test/contact/secured");

    }

    //=========================================================================

    private static RequestSpecification given() {
        return RestAssured.given();
    }

    private ResponseSpecification loggedAndExpect() {
        return RestAssured.given().filter(loggedSessionFilter).expect().log().ifValidationFails();
    }

    private ResponseSpecification loggedAndExpect(final RequestSpecification given) {
        return given.filter(loggedSessionFilter).expect().log().ifValidationFails();
    }

    private Map<String, Object> doGetServerSideObject() throws ParseException {
        final Map<String, Object> contact = doCreateContact();
        final Long oldConId = (Long) contact.get("conId");

        final Response getResponse = loggedAndExpect().statusCode(HttpStatus.SC_OK).when()
                .get("/test/filtered/" + oldConId);
        final String serverToken = getResponse.body().path("serverToken");
        contact.put("serverToken", serverToken);
        return contact;
    }

    private static String convertDate(final String dateStr) throws ParseException {
        final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
        final Date date = dateFormat.parse(dateStr);
        final DateFormat dateFormatUtc = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        return dateFormatUtc.format(date);
    }

    private static Map<String, Object> createDefaultContact(final Long conId) throws ParseException {
        final Map<String, Object> newContact = createContact2(conId, "Mrs", "Fournier", "Catherine",
                convertDate("24/10/1985"),
                createAddress(10L, "10, avenue Claude Vellefaux", "", "Paris", "75010", "France"),
                "catherine.fournier@gmail.com", "01 91 92 93 94");
        return newContact;
    }

    private static Map<String, Object> createContact2(final Long conId, final String honorific, final String name,
            final String firstName, final String birthday, final Map<String, Object> address, final String email,
            final String... tels) {
        return new MapBuilder<String, Object>().putNullable("conId", conId).put("honorificCode", honorific)
                .put("name", name).put("firstName", firstName).put("birthday", birthday).put("address", address)
                .put("email", email).put("tels", Arrays.asList(tels)).build();
    }

    private static Map<String, Object> createAddress(final Long adrId, final String street1, final String street2,
            final String city, final String postalCode, final String country) {
        return new MapBuilder<String, Object>().put("adrId", adrId).put("street1", street1).put("street2", street2)
                .put("city", city).put("postalCode", postalCode).put("country", country).build();
    }
}