org.dspace.app.rest.WorkspaceItemRestRepositoryIT.java Source code

Java tutorial

Introduction

Here is the source code for org.dspace.app.rest.WorkspaceItemRestRepositoryIT.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.app.rest;

import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.ws.rs.core.MediaType;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.dspace.app.rest.builder.BitstreamBuilder;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.ItemMatcher;
import org.dspace.app.rest.matcher.WorkspaceItemMatcher;
import org.dspace.app.rest.model.patch.AddOperation;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.RemoveOperation;
import org.dspace.app.rest.model.patch.ReplaceOperation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.mock.web.MockMultipartFile;

/**
 * Test suite for the WorkspaceItem endpoint
 * @author Andrea Bollini (andrea.bollini at 4science.it)
 *
 */
public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegrationTest {

    @Test
    /**
     * All the workspaceitem should be returned regardless of the collection where they were created
     *
     * @throws Exception
     */
    public void findAllTest() throws Exception {
        context.setCurrentUser(admin);

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();

        //2. Three workspace items in two different collections
        WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 1").withIssueDate("2017-10-17").build();

        WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
                .withTitle("Workspace Item 2").withIssueDate("2016-02-13").build();

        WorkspaceItem workspaceItem3 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
                .withTitle("Workspace Item 3").withIssueDate("2016-02-13").build();

        String token = getAuthToken(admin.getEmail(), password);

        getClient(token).perform(get("/api/submission/workspaceitems")).andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.containsInAnyOrder(
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workspace Item 1", "2017-10-17"),
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                        "Workspace Item 2", "2016-02-13"),
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                        "Workspace Item 3", "2016-02-13"))))
                .andExpect(
                        jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(3)));
    }

    @Test
    /**
     * The workspaceitem endpoint must provide proper pagination
     *
     * @throws Exception
     */
    public void findAllWithPaginationTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();

        //2. Three workspace items in two different collections
        WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 1").withIssueDate("2017-10-17").build();

        WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
                .withTitle("Workspace Item 2").withIssueDate("2016-02-13").build();

        WorkspaceItem workspaceItem3 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
                .withTitle("Workspace Item 3").withIssueDate("2016-02-13").build();

        String token = getAuthToken(admin.getEmail(), password);

        getClient(token).perform(get("/api/submission/workspaceitems").param("size", "2"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.containsInAnyOrder(
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workspace Item 1", "2017-10-17"),
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                        "Workspace Item 2", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workspaceitems", Matchers
                        .not(Matchers.contains(WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                "Workspace Item 3", "2016-02-13")))));

        getClient(token).perform(get("/api/submission/workspaceitems").param("size", "2").param("page", "1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.contains(WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                "Workspace Item 3", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.not(Matchers.contains(
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workspace Item 1", "2017-10-17"),
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                        "Workspace Item 2", "2016-02-13")))))
                .andExpect(jsonPath("$.page.size", is(2))).andExpect(jsonPath("$.page.totalElements", is(3)));
    }

    @Test
    /**
     * The workspaceitem resource endpoint must expose the proper structure
     *
     * @throws Exception
     */
    public void findOneTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();

        //2. a workspace item
        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1).withTitle("Workspace Item 1")
                .withIssueDate("2017-10-17").withAuthor("Smith, Donald").withAuthor("Doe, John")
                .withSubject("ExtraEntry").build();

        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "Workspace Item 1", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * The workspaceitem resource endpoint must expose the proper structure
     *
     * @throws Exception
     */
    public void findOneRelsTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();

        //2. a workspace item
        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1).withTitle("Workspace Item 1")
                .withIssueDate("2017-10-17").withAuthor("Smith, Donald").withAuthor("Doe, John")
                .withSubject("ExtraEntry").build();

        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection"))
                .andExpect(status().isOk()).andExpect(jsonPath("$", Matchers.is(
                        CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()))));

        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/item"))
                .andExpect(status().isOk()).andExpect(jsonPath("$", Matchers.is(ItemMatcher
                        .matchItemWithTitleAndDateIssued(witem.getItem(), "Workspace Item 1", "2017-10-17"))));

        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID() + "/submissionDefinition"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", Matchers.is(hasJsonPath("$.id", is("traditional")))));

    }

    @Test
    /**
     * Check the response code for unexistent workspaceitem
     *
     * @throws Exception
     */
    public void findOneWrongUUIDTest() throws Exception {
        String token = getAuthToken(admin.getEmail(), password);

        getClient(token).perform(get("/api/submission/workspaceitems/" + UUID.randomUUID()))
                .andExpect(status().isNotFound());
    }

    @Test
    /**
     * Removing a workspaceitem should result in delete of all the underline resources (item and bitstreams)
     *
     * @throws Exception
     */
    public void deleteOneTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community with one collection.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
                .build();

        //2. a workspace item
        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1).withTitle("Workspace Item 1")
                .withIssueDate("2017-10-17").build();

        Item item = witem.getItem();

        //Add a bitstream to the item
        String bitstreamContent = "ThisIsSomeDummyText";
        Bitstream bitstream = null;
        try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
            bitstream = BitstreamBuilder.createBitstream(context, item, is).withName("Bitstream1")
                    .withMimeType("text/plain").build();
        }

        String token = getAuthToken(admin.getEmail(), password);

        //Delete the workspaceitem
        getClient(token).perform(delete("/api/submission/workspaceitems/" + witem.getID()))
                .andExpect(status().is(204));

        //Trying to get deleted item should fail with 404
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().is(404));

        //Trying to get deleted workspaceitem's item should fail with 404
        getClient().perform(get("/api/core/items/" + item.getID())).andExpect(status().is(404));

        //Trying to get deleted workspaceitem's bitstream should fail with 404
        getClient().perform(get("/api/core/biststreams/" + bitstream.getID())).andExpect(status().is(404));
    }

    @Test
    /**
     * Create three workspaceitem with two different submitter and verify that the findBySubmitter return the proper
     * list of workspaceitem for each submitter also paginating
     *
     * @throws Exception
     */
    public void findBySubmitterTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();

        //2. create two users to use as submitters
        EPerson submitter1 = EPersonBuilder.createEPerson(context).withEmail("submitter1@example.com").build();
        EPerson submitter2 = EPersonBuilder.createEPerson(context).withEmail("submitter2@example.com").build();

        // create two workspaceitems with the first submitter
        context.setCurrentUser(submitter1);

        //3. Two workspace items in two different collections
        WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 1").withIssueDate("2017-10-17").build();

        WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
                .withTitle("Workspace Item 2").withIssueDate("2016-02-13").build();

        //4. A workspaceitem for the second submitter
        context.setCurrentUser(submitter2);
        WorkspaceItem workspaceItem3 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
                .withTitle("Workspace Item 3").withIssueDate("2016-02-13").build();

        // use our admin to retrieve all the workspace by submitter
        String token = getAuthToken(admin.getEmail(), password);

        // the first submitter has two workspace
        getClient(token)
                .perform(get("/api/submission/workspaceitems/search/findBySubmitter")
                        .param("size", "20").param("uuid", submitter1.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.containsInAnyOrder(
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workspace Item 1", "2017-10-17"),
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                        "Workspace Item 2", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.not(Matchers.contains(WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(
                                workspaceItem3, "Workspace Item 3", "2016-02-13")))))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));

        // the first submitter has two workspace so if we paginate with a 1-size windows the page 1 will contains the
        // second workspace
        getClient(token)
                .perform(get("/api/submission/workspaceitems/search/findBySubmitter")
                        .param("size", "1").param("page", "1").param("uuid", submitter1.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.contains(WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2,
                                "Workspace Item 2", "2016-02-13"))))
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.not(Matchers.contains(
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1,
                                        "Workspace Item 1", "2017-10-17"),
                                WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                        "Workspace Item 3", "2016-02-13")))))
                .andExpect(jsonPath("$.page.size", is(1))).andExpect(jsonPath("$.page.totalElements", is(2)));

        // the second submitter has a single workspace
        getClient(token)
                .perform(get("/api/submission/workspaceitems/search/findBySubmitter")
                        .param("size", "20").param("uuid", submitter2.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$._embedded.workspaceitems",
                        Matchers.contains(WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem3,
                                "Workspace Item 3", "2016-02-13"))))
                .andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
    }

    @Test
    /**
     * Test the creation of workspaceitem POSTing to the resource collection endpoint. It should respect the collection
     * param if present or use a default if it is not used
     *
     * @throws Exception
     */
    public void createEmptyWorkspateItemTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        // create a workspaceitem explicitly in the col1
        getClient(authToken)
                .perform(post("/api/submission/workspaceitems").param("collection", col1.getID().toString())
                        .contentType(org.springframework.http.MediaType.APPLICATION_JSON))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString())));

        // create a workspaceitem explicitly in the col2
        getClient(authToken)
                .perform(post("/api/submission/workspaceitems").param("collection", col2.getID().toString())
                        .contentType(org.springframework.http.MediaType.APPLICATION_JSON))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$._embedded.collection.id", is(col2.getID().toString())));

        // create a workspaceitem without an explicit collection, this will go in the first valid collection for the
        // user: the col1
        getClient(authToken)
                .perform(post("/api/submission/workspaceitems")
                        .contentType(org.springframework.http.MediaType.APPLICATION_JSON))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString())));

        // TODO cleanup the context!!!
    }

    @Test
    /**
     * Test the creation of workspaceitems POSTing to the resource collection endpoint a bibtex file
     *
     * @throws Exception
     */
    public void createMultipleWorkspaceItemFromFileTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();

        String authToken = getAuthToken(admin.getEmail(), password);

        InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib");
        final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib",
                "application/x-bibtex", bibtex);

        // bulk create workspaceitems in the default collection (col1)
        getClient(authToken).perform(fileUpload("/api/submission/workspaceitems").file(bibtexFile))
                // bulk create should return 200, 201 (created) is better for single resource
                .andExpect(status().isOk())
                .andExpect(
                        jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value",
                                is("My Article")))
                .andExpect(jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
                        is(col1.getID().toString())))
                .andExpect(
                        jsonPath("$._embedded.workspaceitems[1].sections.traditionalpageone['dc.title'][0].value",
                                is("My Article 2")))
                .andExpect(jsonPath("$._embedded.workspaceitems[1]._embedded.collection.id",
                        is(col1.getID().toString())))
                .andExpect(
                        jsonPath("$._embedded.workspaceitems[2].sections.traditionalpageone['dc.title'][0].value",
                                is("My Article 3")))
                .andExpect(jsonPath("$._embedded.workspaceitems[2]._embedded.collection.id",
                        is(col1.getID().toString())))
                .andExpect(jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist());

        // bulk create workspaceitems explicitly in the col2
        getClient(authToken)
                .perform(fileUpload("/api/submission/workspaceitems").file(bibtexFile).param("collection",
                        col2.getID().toString()))
                .andExpect(status().isOk())
                .andExpect(
                        jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value",
                                is("My Article")))
                .andExpect(jsonPath("$._embedded.workspaceitems[0]._embedded.collection.id",
                        is(col2.getID().toString())))
                .andExpect(
                        jsonPath("$._embedded.workspaceitems[1].sections.traditionalpageone['dc.title'][0].value",
                                is("My Article 2")))
                .andExpect(jsonPath("$._embedded.workspaceitems[1]._embedded.collection.id",
                        is(col2.getID().toString())))
                .andExpect(
                        jsonPath("$._embedded.workspaceitems[2].sections.traditionalpageone['dc.title'][0].value",
                                is("My Article 3")))
                .andExpect(jsonPath("$._embedded.workspaceitems[2]._embedded.collection.id",
                        is(col2.getID().toString())))
                .andExpect(jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist());

        bibtex.close();
    }

    @Test
    /**
     * Test the creation of a workspaceitem POSTing to the resource collection endpoint a PDF file. As a single item
     * will be created we expect to have the pdf file stored as a bitstream
     *
     * @throws Exception
     */
    public void createWorkspaceItemFromPDFFileTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();

        String authToken = getAuthToken(admin.getEmail(), password);

        InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
        final MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/myfile.pdf", "application/pdf",
                pdf);

        // bulk create a workspaceitem
        getClient(authToken).perform(fileUpload("/api/submission/workspaceitems").file(pdfFile))
                // bulk create should return 200, 201 (created) is better for single resource
                .andExpect(status().isOk())
                //FIXME it will be nice to setup a mock grobid server for end to end testing
                // no metadata for now
                //              .andExpect(jsonPath("$._embedded.workspaceitems[0]._embedded.traditionalpageone['dc.title'][0].value",
                //              is("This is a simple test file")))
                // we can just check that the pdf is stored in the item
                .andExpect(jsonPath(
                        "$._embedded.workspaceitems[0].sections.upload.files[0].metadata['dc.title'][0].value",
                        is("myfile.pdf")))
                .andExpect(jsonPath(
                        "$._embedded.workspaceitems[0].sections.upload.files[0].metadata['dc.source'][0].value",
                        is("/local/path/myfile.pdf")));

        pdf.close();
    }

    @Test
    /**
     * Test the exposition of validation error for missing required metadata both at the creation time than on existent
     * workspaceitems
     *
     * @throws Exception
     */
    public void validationErrorsRequiredMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 1").withIssueDate("2017-10-17").build();

        getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItem1.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist());

        WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 2").build();

        getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItem2.getID()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths", Matchers.contains(
                                hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.date.issued")))))));

        // create an empty workspaceitem explicitly in the col1, check validation on creation
        getClient(authToken)
                .perform(post("/api/submission/workspaceitems").param("collection", col1.getID().toString())
                        .contentType(org.springframework.http.MediaType.APPLICATION_JSON))
                .andExpect(status().isCreated())
                // title and dateissued are required in the first panel
                // the json path with a @ selector always return an array
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths", Matchers.containsInAnyOrder(
                                hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.title")),
                                hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.date.issued")))))));
    }

    @Test
    /**
     * Test the update of metadata
     *
     * @throws Exception
     */
    public void patchUpdateMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1).withTitle("Workspace Item 1")
                .withIssueDate("2017-10-17").withSubject("ExtraEntry").build();

        // a simple patch to update an existent metadata
        List<Operation> updateTitle = new ArrayList<Operation>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "New Title");
        updateTitle.add(new ReplaceOperation("/sections/traditionalpageone/dc.title/0", value));

        String patchBody = getPatchContent(updateTitle);

        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist()).andExpect(jsonPath("$",
                        // check the new title and untouched values
                        Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
        ;

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * Test delete of a metadata
     *
     * @throws Exception
     */
    public void patchDeleteMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1).withTitle("Workspace Item 1")
                .withIssueDate("2017-10-17").withSubject("ExtraEntry").build();

        WorkspaceItem witemMultipleSubjects = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 1").withIssueDate("2017-10-17").withSubject("Subject1")
                .withSubject("Subject2").withSubject("Subject3").withSubject("Subject4").build();

        WorkspaceItem witemWithTitleDateAndSubjects = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Workspace Item 1").withIssueDate("2017-10-17").withSubject("Subject1")
                .withSubject("Subject2").withSubject("Subject3").withSubject("Subject4").build();

        // try to remove the title
        List<Operation> removeTitle = new ArrayList<Operation>();
        removeTitle.add(new RemoveOperation("/sections/traditionalpageone/dc.title/0"));

        String patchBody = getPatchContent(removeTitle);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID())
                        .content(patchBody).contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths",
                                Matchers.contains(
                                        hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.title")))))))
                .andExpect(jsonPath("$",
                        // check the new title and untouched values
                        Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, null,
                                "2017-10-17", "ExtraEntry"))));
        ;

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
                        Matchers.contains(hasJsonPath("$.paths",
                                Matchers.contains(
                                        hasJsonPath("$", Matchers.is("/sections/traditionalpageone/dc.title")))))))
                .andExpect(jsonPath("$", Matchers.is(WorkspaceItemMatcher
                        .matchItemWithTitleAndDateIssuedAndSubject(witem, null, "2017-10-17", "ExtraEntry"))));

        // try to remove a metadata in a specific position
        List<Operation> removeMidSubject = new ArrayList<Operation>();
        removeMidSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/1"));

        patchBody = getPatchContent(removeMidSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject4")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject4")));

        List<Operation> removeFirstSubject = new ArrayList<Operation>();
        removeFirstSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/0"));

        patchBody = getPatchContent(removeFirstSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject4")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject4")));

        List<Operation> removeLastSubject = new ArrayList<Operation>();
        removeLastSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/1"));

        patchBody = getPatchContent(removeLastSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")));

        List<Operation> removeFinalSubject = new ArrayList<Operation>();
        removeFinalSubject.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject/0"));

        patchBody = getPatchContent(removeFinalSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());

        // remove all the subjects with a single operation
        List<Operation> removeSubjectsAllAtOnce = new ArrayList<Operation>();
        removeSubjectsAllAtOnce.add(new RemoveOperation("/sections/traditionalpagetwo/dc.subject"));

        patchBody = getPatchContent(removeSubjectsAllAtOnce);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witemWithTitleDateAndSubjects.getID())
                        .content(patchBody).contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witemWithTitleDateAndSubjects.getID()))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist());
    }

    @Test
    /**
     * Test the addition of metadata
     *
     * @throws Exception
     */
    public void patchAddMetadataTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1).withIssueDate("2017-10-17")
                .withSubject("ExtraEntry").build();

        // try to add the title
        List<Operation> addTitle = new ArrayList<Operation>();
        // create a list of values to use in add operation
        List<Map<String, String>> values = new ArrayList<Map<String, String>>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "New Title");
        values.add(value);
        addTitle.add(new AddOperation("/sections/traditionalpageone/dc.title", values));

        String patchBody = getPatchContent(addTitle);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist()).andExpect(jsonPath("$",
                        // check if the new title if back and the other values untouched
                        Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
        ;

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$",
                        Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
                                "New Title", "2017-10-17", "ExtraEntry"))));
    }

    @Test
    /**
     * Test the addition of metadata
     *
     * @throws Exception
     */
    public void patchAddMultipleMetadataValuesTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem").withIssueDate("2017-10-17").build();

        // try to add multiple subjects at once
        List<Operation> addSubjects = new ArrayList<Operation>();
        // create a list of values to use in add operation
        List<Map<String, String>> values = new ArrayList<Map<String, String>>();
        Map<String, String> value1 = new HashMap<String, String>();
        value1.put("value", "Subject1");
        Map<String, String> value2 = new HashMap<String, String>();
        value2.put("value", "Subject2");
        values.add(value1);
        values.add(value2);

        addSubjects.add(new AddOperation("/sections/traditionalpagetwo/dc.subject", values));

        String patchBody = getPatchContent(addSubjects);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject2")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject2")));

        // add a subject in the first position
        List<Operation> addFirstSubject = new ArrayList<Operation>();
        Map<String, String> firstSubject = new HashMap<String, String>();
        firstSubject.put("value", "First Subject");

        addFirstSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/0", firstSubject));

        patchBody = getPatchContent(addFirstSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject2")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Subject2")));

        // add a subject in a central position
        List<Operation> addMidSubject = new ArrayList<Operation>();
        Map<String, String> midSubject = new HashMap<String, String>();
        midSubject.put("value", "Mid Subject");

        addMidSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/2", midSubject));

        patchBody = getPatchContent(addMidSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")));

        // append a last subject without specifying the index
        List<Operation> addLastSubject = new ArrayList<Operation>();
        Map<String, String> lastSubject = new HashMap<String, String>();
        lastSubject.put("value", "Last Subject");

        addLastSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/4", lastSubject));

        patchBody = getPatchContent(addLastSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")));

        // append a last subject without specifying the index
        List<Operation> addFinalSubject = new ArrayList<Operation>();
        Map<String, String> finalSubject = new HashMap<String, String>();
        finalSubject.put("value", "Final Subject");

        addFinalSubject.add(new AddOperation("/sections/traditionalpagetwo/dc.subject/-", finalSubject));

        patchBody = getPatchContent(addFinalSubject);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][5].value", is("Final Subject")));

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("First Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][1].value", is("Subject1")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][2].value", is("Mid Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][3].value", is("Subject2")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][4].value", is("Last Subject")))
                .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][5].value", is("Final Subject")));
    }

    @Test
    /**
     * Test the acceptance of the deposit license
     *
     * @throws Exception
     */
    public void patchAcceptLicenseTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem").withIssueDate("2017-10-17").build();

        WorkspaceItem witem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem 2").withIssueDate("2017-10-17").build();

        WorkspaceItem witem3 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem 3").withIssueDate("2017-10-17").build();

        WorkspaceItem witem4 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem 4").withIssueDate("2017-10-17").build();

        // check that our workspaceitems come without a license (all are build in the same way, just check the first)
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // try to grant the license with an add operation
        List<Operation> addGrant = new ArrayList<Operation>();
        addGrant.add(new AddOperation("/sections/license/granted", true));

        String patchBody = getPatchContent(addGrant);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // try to grant the license with an add operation supplying a string instead than a boolean
        List<Operation> addGrantString = new ArrayList<Operation>();
        addGrantString.add(new AddOperation("/sections/license/granted", "true"));

        patchBody = getPatchContent(addGrantString);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem2.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem2.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // try to grant the license with a replace operation
        List<Operation> replaceGrant = new ArrayList<Operation>();
        replaceGrant.add(new ReplaceOperation("/sections/license/granted", true));

        patchBody = getPatchContent(replaceGrant);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem3.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem3.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // try to grant the license with a replace operation supplying a string
        List<Operation> replaceGrantString = new ArrayList<Operation>();
        replaceGrant.add(new ReplaceOperation("/sections/license/granted", "true"));

        patchBody = getPatchContent(replaceGrant);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem4.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem4.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());
    }

    @Test
    /**
     * Test the reject of the deposit license
     *
     * @throws Exception
     */
    public void patchRejectLicenseTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem").withIssueDate("2017-10-17").grantLicense().build();

        WorkspaceItem witem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem 2").withIssueDate("2017-10-17").grantLicense().build();

        WorkspaceItem witem3 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem 3").withIssueDate("2017-10-17").grantLicense().build();

        WorkspaceItem witem4 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem 4").withIssueDate("2017-10-17").grantLicense().build();

        // check that our workspaceitems come with a license (all are build in the same way, just check the first)
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.license.granted", is(true)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isNotEmpty())
                .andExpect(jsonPath("$.sections.license.url").isNotEmpty());

        // try to reject the license with an add operation
        List<Operation> addGrant = new ArrayList<Operation>();
        addGrant.add(new AddOperation("/sections/license/granted", false));

        String patchBody = getPatchContent(addGrant);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // try to reject the license with an add operation supplying a string instead than a boolean
        List<Operation> addGrantString = new ArrayList<Operation>();
        addGrantString.add(new AddOperation("/sections/license/granted", "false"));

        patchBody = getPatchContent(addGrantString);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem2.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem2.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // try to reject the license with a replace operation
        List<Operation> replaceGrant = new ArrayList<Operation>();
        replaceGrant.add(new ReplaceOperation("/sections/license/granted", false));

        patchBody = getPatchContent(replaceGrant);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem3.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem3.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // try to reject the license with a replace operation supplying a string
        List<Operation> replaceGrantString = new ArrayList<Operation>();
        replaceGrant.add(new ReplaceOperation("/sections/license/granted", "false"));

        patchBody = getPatchContent(replaceGrant);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem4.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk()).andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

        // verify that the patch changes have been persisted
        getClient().perform(get("/api/submission/workspaceitems/" + witem4.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.errors").doesNotExist())
                .andExpect(jsonPath("$.sections.license.granted", is(false)))
                .andExpect(jsonPath("$.sections.license.acceptanceDate").isEmpty())
                .andExpect(jsonPath("$.sections.license.url").isEmpty());

    }

    @Test
    /**
     * Test update of bitstream metadata in the upload section
     *
     * @throws Exception
     */
    public void patchUploadTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();

        String authToken = getAuthToken(admin.getEmail(), password);

        InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem").withIssueDate("2017-10-17")
                .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf).build();

        // check the file metadata
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
                        is("/local/path/simple-article.pdf")))
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
                        is("simple-article.pdf")));

        // try to change the filename and add a description
        List<Operation> addOpts = new ArrayList<Operation>();
        Map<String, String> value = new HashMap<String, String>();
        value.put("value", "newfilename.pdf");
        Map<String, String> valueDesc = new HashMap<String, String>();
        valueDesc.put("value", "Description");
        List valueDescs = new ArrayList();
        valueDescs.add(valueDesc);
        addOpts.add(new AddOperation("/sections/upload/files/0/metadata/dc.title/0", value));
        addOpts.add(new AddOperation("/sections/upload/files/0/metadata/dc.description", valueDescs));

        String patchBody = getPatchContent(addOpts);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk())
                // is the source still here?
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
                        is("/local/path/simple-article.pdf")))
                // check the new filename
                .andExpect(
                        jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", is("newfilename.pdf")))
                // check the description
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.description'][0].value",
                        is("Description")));

        // check that changes persist
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
                        is("/local/path/simple-article.pdf")))
                .andExpect(
                        jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", is("newfilename.pdf")))
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.description'][0].value",
                        is("Description")));

        // try to remove the description and the source now
        List<Operation> removeOpts = new ArrayList<Operation>();
        removeOpts.add(new RemoveOperation("/sections/upload/files/0/metadata/dc.source/0"));
        removeOpts.add(new RemoveOperation("/sections/upload/files/0/metadata/dc.description"));

        patchBody = getPatchContent(removeOpts);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk())
                // check the removed source
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source']").doesNotExist())
                // check the filename still here
                .andExpect(
                        jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", is("newfilename.pdf")))
                // check the removed description
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.description']").doesNotExist());

        // check that changes persist
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source']").doesNotExist())
                .andExpect(
                        jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", is("newfilename.pdf")))
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.description']").doesNotExist());

        // try to update the filename with an update opt
        List<Operation> updateOpts = new ArrayList<Operation>();
        Map<String, String> updateValue = new HashMap<String, String>();
        updateValue.put("value", "another-filename.pdf");
        updateOpts.add(new ReplaceOperation("/sections/upload/files/0/metadata/dc.title/0", updateValue));

        patchBody = getPatchContent(updateOpts);
        getClient(authToken)
                .perform(patch("/api/submission/workspaceitems/" + witem.getID()).content(patchBody)
                        .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
                .andExpect(status().isOk())
                // check the filename still here
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
                        is("another-filename.pdf")));

        // check that changes persist
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
                        is("another-filename.pdf")));
    }

    @Test
    /**
     * Test the upload of files in the upload over section
     *
     * @throws Exception
     */
    public void uploadTest() throws Exception {
        context.turnOffAuthorisationSystem();

        //** GIVEN **
        //1. A community-collection structure with one parent community with sub-community and two collections.
        parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
        Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity).withName("Sub Community")
                .build();
        Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();

        String authToken = getAuthToken(admin.getEmail(), password);

        WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
                .withTitle("Test WorkspaceItem").withIssueDate("2017-10-17").build();

        InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
        final MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/simple-article.pdf",
                "application/pdf", pdf);

        // upload the file in our workspaceitem
        getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID()).file(pdfFile))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
                        is("simple-article.pdf")))
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
                        is("/local/path/simple-article.pdf")));

        // check the file metadata
        getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
                        is("simple-article.pdf")))
                .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
                        is("/local/path/simple-article.pdf")));
    }

}