/*
   * Test POST or PUT to /restAPI/items/{id}, to update a new document
   * But the specified document is not found
   */
  public void testUpdateDocNotFound(boolean isPost) throws Exception {

    ModelMap newDoc = createDoc("Bach", "unknonwn", "Wonderful");

    String jsonNewDoc = objectToJson(newDoc);

    String id = this.preAddedDocs.get(0).getId();

    // Fake ID which does not exist
    String fakeId = id.substring(0, id.length() - 1);

    MockHttpServletRequestBuilder builder;

    if (isPost) builder = post("/restAPI/items/" + fakeId);
    else builder = put("/restAPI/items/" + fakeId);

    this.mockMvc
        .perform(builder.contentType(contentType).content(jsonNewDoc))
        .andExpect(status().isNotFound());

    // Number of document not changed
    assertEquals(2, docRepository.count());

    // Original document not changed
    StoredDocument retrievedDoc = docRepository.findOne(id);

    assertEquals(preAddedDocs.get(0).getDocument(), retrievedDoc.getDocument());
  }
  /*
   * Test POST or PUT to /restAPI/items/{id}, to update a new document
   * But the request body is not valid
   */
  public void testUpdateDocNotValid(boolean isPost) throws Exception {

    ModelMap newDoc = createDoc("Bach", "unknonwn", "Wonderful");

    String jsonNewDoc = objectToJson(newDoc);

    String id = this.preAddedDocs.get(0).getId();

    // Manipulate the json string to be a wrong input
    jsonNewDoc = jsonNewDoc.substring(0, jsonNewDoc.lastIndexOf("}"));

    MockHttpServletRequestBuilder builder;

    if (isPost) builder = post("/restAPI/items/" + id);
    else builder = put("/restAPI/items/" + id);

    this.mockMvc
        .perform(builder.contentType(contentType).content(jsonNewDoc))
        .andExpect(status().isBadRequest());

    // Number of document not changed
    assertEquals(2, docRepository.count());

    // Original document not changed
    StoredDocument retrievedDoc = docRepository.findOne(id);

    assertEquals(preAddedDocs.get(0).getDocument(), retrievedDoc.getDocument());
  }
  /*
   * Test POST or PUT to /restAPI/items/{id}, to update a new document
   */
  public void testUpdateDoc(boolean isPost) throws Exception {

    ModelMap newDoc = createDoc("Bach", "unknonwn", "Wonderful");

    String jsonNewDoc = objectToJson(newDoc);

    // get an existing id
    String id = this.preAddedDocs.get(0).getId();

    MockHttpServletRequestBuilder builder;

    if (isPost) builder = post("/restAPI/items/" + id);
    else builder = put("/restAPI/items/" + id);

    this.mockMvc
        .perform(builder.contentType(contentType).content(jsonNewDoc))
        .andExpect(status().isOk());

    // the number of document not changed
    assertEquals(2, docRepository.count());

    // Verify the original document is updated
    StoredDocument retrievedDoc = docRepository.findOne(id);

    assertEquals(newDoc, retrievedDoc.getDocument());
  }
  // Test POST or PUT on /restAPI/items, to add a document
  private void testAddOneNewDoc(boolean isPost) throws Exception {
    ModelMap newDoc = createDoc("Bach", "unknonwn", "Wonderful");

    String jsonNewDoc = objectToJson(newDoc);

    MockHttpServletRequestBuilder builder;

    if (isPost) builder = post("/restAPI/items/");
    else builder = put("/restAPI/items");

    MvcResult result =
        mockMvc
            .perform(builder.contentType(contentType).content(jsonNewDoc))
            .andExpect(status().isCreated())
            .andReturn();

    // Get the response body
    String resultJson = result.getResponse().getContentAsString();

    // Get the document ID returned
    DocIDReturn idRet = mapper.readValue(resultJson, DocIDReturn.class);

    // Now in the database there shall be 3 documents
    assertEquals(3, docRepository.count());

    // Get the document just added, and verify
    StoredDocument retrievedDoc = docRepository.findOne(idRet.getId());

    assertEquals(newDoc, retrievedDoc.getDocument());
  }
  /**
   * Test GET on /restAPI/items/{id}, but the document is not found
   *
   * @throws Exception
   */
  @Test
  public void testGetOneDocNotFound() throws Exception {

    StoredDocument doc = preAddedDocs.get(0);
    String id = doc.getId();

    // Manipulate the id to be a wrong id
    id = id.substring(0, id.length() - 1);

    ResultActions r = mockMvc.perform(get("/restAPI/items/" + id));

    // HTTP status shall be NotFound
    r.andExpect(status().isNotFound());
  }
  /**
   * Test DELETE to /restAPI/items/{id}, to delete a document But the specified document is not
   * found
   *
   * @throws Exception
   */
  @Test
  public void testDeleteDocNotFound() throws Exception {

    String id = this.preAddedDocs.get(1).getId();

    // Provide a wrong id
    String fakeId = id.substring(0, id.length() - 1);

    this.mockMvc
        .perform(delete("/restAPI/items/" + fakeId).contentType(contentType))
        .andExpect(status().isNotFound());

    // Number of document not changed
    assertEquals(2, docRepository.count());

    // Original document still exist
    StoredDocument retrievedDoc = docRepository.findOne(id);

    assertEquals(preAddedDocs.get(1).getDocument(), retrievedDoc.getDocument());
  }
  /**
   * Helper method to verify a document (together with id) returned in ResultActions, to check if it
   * is the same as provided in doc
   *
   * <p>If i>=0, it is expected that the result in ResultActions is in a JSON array and the compare
   * is to be performed towards the document in the array with index i (0 based)
   *
   * <p>If i < 0, it is expected that the result is a single document which is to be compared
   *
   * <p>Each document is to expected to be as following scheme (aligned with the one generated by
   * createDoc() )
   *
   * <p>{ "id" : id, "document" : { "author" : { "name":name, "address":address }, "content" :
   * content } }
   *
   * @param r The result to be verified
   * @param i The index of the document to be verified (if i>= 0)
   * @param doc The document to be compared
   */
  @SuppressWarnings("unchecked")
  private ResultActions verifyDoc(ResultActions r, int i, StoredDocument doc) throws Exception {

    String id = doc.getId();
    String content = (String) doc.getDocument().get("content");

    Map<String, Object> auth = (Map<String, Object>) doc.getDocument().get("author");

    String name = (String) auth.get("name");
    String address = (String) auth.get("address");

    String pathPrefix = (i >= 0 ? "$[" + i + "]" : "$");

    return r.andExpect(status().isOk())
        .andExpect(content().contentTypeCompatibleWith(contentType))
        .andExpect(jsonPath(pathPrefix + ".id", is(id)))
        .andExpect(jsonPath(pathPrefix + ".document.content", is(content)))
        .andExpect(jsonPath(pathPrefix + ".document.author.name", is(name)))
        .andExpect(jsonPath(pathPrefix + ".document.author.address", is(address)));
  }
  /**
   * Test GET on /restAPI/items/{id}, to retrieve one document
   *
   * @throws Exception
   */
  @Test
  public void testGetOneDoc() throws Exception {

    // Try retrieving the first document
    StoredDocument doc = preAddedDocs.get(0);
    String id = doc.getId();

    ResultActions r = mockMvc.perform(get("/restAPI/items/" + id));

    r.andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(contentType));

    // The expected result is not an array, so provide i < 0
    verifyDoc(r, -1, doc);

    // Try retrieving the second document
    doc = preAddedDocs.get(1);
    id = doc.getId();

    r = mockMvc.perform(get("/restAPI/items/" + id));

    r.andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(contentType));

    verifyDoc(r, -1, doc);
  }