org.roda.core.model.ModelServiceTest.java Source code

Java tutorial

Introduction

Here is the source code for org.roda.core.model.ModelServiceTest.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE file at the root of the source
 * tree and available online at
 *
 * https://github.com/keeps/roda
 */
package org.roda.core.model;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.testng.Assert.fail;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.xmlbeans.XmlException;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.hamcrest.core.Is;
import org.roda.core.CorporaConstants;
import org.roda.core.RodaCoreFactory;
import org.roda.core.TestsHelper;
import org.roda.core.common.PremisV3Utils;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.common.iterables.CloseableIterables;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.exceptions.AlreadyExistsException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RODAException;
import org.roda.core.data.v2.LiteOptionalWithCause;
import org.roda.core.data.v2.LiteRODAObject;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.AIPState;
import org.roda.core.data.v2.ip.File;
import org.roda.core.data.v2.ip.Representation;
import org.roda.core.data.v2.ip.StoragePath;
import org.roda.core.data.v2.ip.TransferredResource;
import org.roda.core.data.v2.ip.metadata.DescriptiveMetadata;
import org.roda.core.data.v2.log.LogEntry;
import org.roda.core.data.v2.log.LogEntry.LOG_ENTRY_STATE;
import org.roda.core.data.v2.log.LogEntryParameter;
import org.roda.core.data.v2.user.Group;
import org.roda.core.data.v2.user.User;
import org.roda.core.model.utils.ModelUtils;
import org.roda.core.storage.Binary;
import org.roda.core.storage.DefaultStoragePath;
import org.roda.core.storage.StorageService;
import org.roda.core.storage.StorageTestUtils;
import org.roda.core.storage.fs.FSUtils;
import org.roda.core.storage.fs.FileStorageService;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.google.common.collect.Iterables;

import gov.loc.premis.v3.AgentComplexType;
import gov.loc.premis.v3.EventComplexType;
import gov.loc.premis.v3.ObjectCharacteristicsComplexType;
import gov.loc.premis.v3.ObjectIdentifierComplexType;
import jersey.repackaged.com.google.common.collect.Lists;

/**
 * Unit tests for ModelService
 * 
 * @author Hlder Silva <hsilva@keep.pt>
 * @author Luis Faria <lfaria@keep.pt>
 * 
 * @see ModelService
 */
// @PrepareForTest({})
@Test(groups = { RodaConstants.TEST_GROUP_ALL, RodaConstants.TEST_GROUP_TRAVIS })
public class ModelServiceTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(ModelServiceTest.class);
    private static final String ROLE1 = RodaConstants.REPOSITORY_PERMISSIONS_DESCRIPTIVE_METADATA_UPDATE;
    private static final String ROLE2 = RodaConstants.REPOSITORY_PERMISSIONS_LOG_ENTRY_READ;

    private static Path basePath;
    private static Path logPath;
    private static StorageService storage;
    private static ModelService model;

    private static Path corporaPath;
    private static StorageService corporaService;
    private static int fileCounter = 0;

    @BeforeClass
    public static void setUp() throws IOException, URISyntaxException, GenericException {
        URL corporaURL = ModelServiceTest.class.getResource("/corpora");
        corporaPath = Paths.get(corporaURL.toURI());
        corporaService = new FileStorageService(corporaPath);

        LOGGER.debug("Running model test under storage: {}", basePath);
    }

    // @BeforeMethod
    @BeforeClass
    public void init() throws IOException, GenericException {
        basePath = TestsHelper.createBaseTempDir(getClass(), true);

        boolean deploySolr = false;
        boolean deployLdap = true;
        boolean deployFolderMonitor = true;
        boolean deployOrchestrator = false;
        boolean deployPluginManager = false;
        boolean deployDefaultResources = false;
        RodaCoreFactory.instantiateTest(deploySolr, deployLdap, deployFolderMonitor, deployOrchestrator,
                deployPluginManager, deployDefaultResources);

        logPath = RodaCoreFactory.getLogPath();
        storage = RodaCoreFactory.getStorageService();
        model = RodaCoreFactory.getModelService();
    }

    // @AfterMethod
    @AfterClass
    public void cleanup() throws NotFoundException, GenericException, IOException {
        RodaCoreFactory.shutdown();
        FSUtils.deletePath(basePath);
    }

    @Test
    public void testCreateAIP() throws RODAException, ParseException, IOException, XmlException {

        // generate AIP ID
        final String aipId = CorporaConstants.SOURCE_AIP_ID;

        // testing AIP
        final AIP aip = model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        assertNotNull(aip);
        assertEquals(aipId, aip.getId());
        assertNull("AIP_1 should not have a parent", aip.getParentId());
        assertThat(aip.getState(), Is.is(AIPState.ACTIVE));

        List<String> descriptiveMetadataIds = aip.getDescriptiveMetadata().stream().map(dm -> dm.getId())
                .collect(Collectors.toList());
        assertThat(descriptiveMetadataIds, containsInAnyOrder(CorporaConstants.DESCRIPTIVE_METADATA_ID));

        List<String> representationIds = aip.getRepresentations().stream().map(rep -> rep.getId())
                .collect(Collectors.toList());

        assertThat(representationIds,
                containsInAnyOrder(CorporaConstants.REPRESENTATION_1_ID, CorporaConstants.REPRESENTATION_2_ID));

        // testing descriptive metadata
        final DescriptiveMetadata descMetadata = model.retrieveDescriptiveMetadata(aipId,
                CorporaConstants.DESCRIPTIVE_METADATA_ID);

        assertEquals(aipId, descMetadata.getAipId());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_ID, descMetadata.getId());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_TYPE, descMetadata.getType());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_VERSION, descMetadata.getVersion());

        StoragePath descriptiveMetadataPath = ModelUtils.getDescriptiveMetadataStoragePath(descMetadata.getAipId(),
                descMetadata.getId());
        final Binary descMetadataBinary = storage.getBinary(descriptiveMetadataPath);
        assertTrue(descMetadataBinary.getSizeInBytes() > 0);
        assertEquals(descMetadataBinary.getSizeInBytes().intValue(),
                IOUtils.toByteArray(descMetadataBinary.getContent().createInputStream()).length);

        // testing representations
        final Representation representation1 = model.retrieveRepresentation(aipId,
                CorporaConstants.REPRESENTATION_1_ID);
        assertEquals(aipId, representation1.getAipId());
        assertEquals(CorporaConstants.REPRESENTATION_1_ID, representation1.getId());
        assertEquals(CorporaConstants.REPRESENTATION_1_ORIGINAL, representation1.isOriginal());

        CloseableIterable<OptionalWithCause<File>> allRep1Files = model.listFilesUnder(aipId,
                CorporaConstants.REPRESENTATION_1_ID, true);
        List<String> allRep1FileIds = Lists.newArrayList(allRep1Files).stream().filter(f -> f.isPresent())
                .map(f -> f.get().getId()).collect(Collectors.toList());
        allRep1Files.close();

        assertThat(allRep1FileIds, containsInAnyOrder(CorporaConstants.REPRESENTATION_1_FILE_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_2_ID));

        final Representation representation2 = model.retrieveRepresentation(aipId,
                CorporaConstants.REPRESENTATION_2_ID);
        assertEquals(aipId, representation2.getAipId());
        assertEquals(CorporaConstants.REPRESENTATION_2_ID, representation2.getId());
        assertEquals(CorporaConstants.REPRESENTATION_2_ORIGINAL, representation2.isOriginal());

        CloseableIterable<OptionalWithCause<File>> allRep2Files = model.listFilesUnder(aipId,
                CorporaConstants.REPRESENTATION_2_ID, true);
        List<String> allRep2FileIds = Lists.newArrayList(allRep2Files).stream().filter(f -> f.isPresent())
                .map(f -> f.get().getId()).collect(Collectors.toList());
        allRep2Files.close();

        assertThat(allRep2FileIds, containsInAnyOrder(CorporaConstants.REPRESENTATION_2_FILE_1_ID,
                CorporaConstants.REPRESENTATION_2_FILE_2_ID));

        // testing files
        final File file_1_1 = model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
        assertEquals(aipId, file_1_1.getAipId());
        assertEquals(CorporaConstants.REPRESENTATION_1_ID, file_1_1.getRepresentationId());
        assertEquals(CorporaConstants.REPRESENTATION_1_FILE_1_ID, file_1_1.getId());

        final Binary binary_1_1 = storage.getBinary(ModelUtils.getFileStoragePath(file_1_1));
        assertTrue(binary_1_1.getSizeInBytes() > 0);
        assertEquals(binary_1_1.getSizeInBytes().intValue(),
                IOUtils.toByteArray(binary_1_1.getContent().createInputStream()).length);

        final File file_1_2 = model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_2_PATH, CorporaConstants.REPRESENTATION_1_FILE_2_ID);
        assertEquals(aipId, file_1_2.getAipId());
        assertEquals(CorporaConstants.REPRESENTATION_1_ID, file_1_2.getRepresentationId());
        assertEquals(CorporaConstants.REPRESENTATION_1_FILE_2_ID, file_1_2.getId());

        final Binary binary_1_2 = storage.getBinary(ModelUtils.getFileStoragePath(file_1_2));
        assertTrue(binary_1_2.getSizeInBytes() > 0);
        assertEquals(binary_1_2.getSizeInBytes().intValue(),
                IOUtils.toByteArray(binary_1_2.getContent().createInputStream()).length);

        final File file_2_1 = model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_2_ID,
                CorporaConstants.REPRESENTATION_2_FILE_1_PATH, CorporaConstants.REPRESENTATION_2_FILE_1_ID);
        assertEquals(aipId, file_2_1.getAipId());
        assertEquals(CorporaConstants.REPRESENTATION_2_ID, file_2_1.getRepresentationId());
        assertEquals(CorporaConstants.REPRESENTATION_2_FILE_1_ID, file_2_1.getId());

        final Binary binary_2_1 = storage.getBinary(ModelUtils.getFileStoragePath(file_2_1));
        assertTrue(binary_2_1.getSizeInBytes() > 0);
        assertEquals(binary_2_1.getSizeInBytes().intValue(),
                IOUtils.toByteArray(binary_2_1.getContent().createInputStream()).length);

        final File file_2_2 = model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_2_ID,
                CorporaConstants.REPRESENTATION_2_FILE_2_PATH, CorporaConstants.REPRESENTATION_2_FILE_2_ID);
        assertEquals(aipId, file_2_2.getAipId());
        assertEquals(CorporaConstants.REPRESENTATION_2_ID, file_2_2.getRepresentationId());
        assertEquals(CorporaConstants.REPRESENTATION_2_FILE_2_ID, file_2_2.getId());

        final Binary binary_2_2 = storage.getBinary(ModelUtils.getFileStoragePath(file_2_2));
        assertTrue(binary_2_2.getSizeInBytes() > 0);
        assertEquals(binary_2_2.getSizeInBytes().intValue(),
                IOUtils.toByteArray(binary_2_2.getContent().createInputStream()).length);

        // test preservation metadata

        Binary preservationObject = model.retrievePreservationRepresentation(aipId,
                CorporaConstants.REPRESENTATION_1_ID);
        gov.loc.premis.v3.Representation rpo = PremisV3Utils.binaryToRepresentation(preservationObject.getContent(),
                true);

        ObjectIdentifierComplexType[] objectIdentifierArray = rpo.getObjectIdentifierArray();
        assertEquals(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN,
                objectIdentifierArray[0].getObjectIdentifierType().getStringValue());
        assertEquals(CorporaConstants.REPRESENTATION_1_URN,
                rpo.getObjectIdentifierArray()[0].getObjectIdentifierValue());
        assertEquals(CorporaConstants.PRESERVATION_LEVEL_FULL,
                rpo.getPreservationLevelArray(0).getPreservationLevelValue().getStringValue());

        Binary f0_premis_bin = model.retrievePreservationFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
        gov.loc.premis.v3.File f0_premis_file = PremisV3Utils.binaryToFile(f0_premis_bin.getContent(), true);

        ObjectCharacteristicsComplexType f0_characteristics = f0_premis_file.getObjectCharacteristicsArray(0);
        assertEquals(0, f0_characteristics.getCompositionLevel().getBigIntegerValue().intValue());
        assertEquals(0, f0_characteristics.getCompositionLevel().getBigIntegerValue().intValue());

        assertEquals(f0_characteristics.getFormatArray(0).getFormatDesignation().getFormatName().getStringValue(),
                CorporaConstants.TEXT_XML);

        Binary event_premis_bin = model.retrievePreservationEvent(aipId, CorporaConstants.REPRESENTATION_1_ID, null,
                null, CorporaConstants.REPRESENTATION_1_PREMIS_EVENT_ID);
        EventComplexType event_premis = PremisV3Utils.binaryToEvent(event_premis_bin.getContent(), true);
        assertEquals(CorporaConstants.INGESTION, event_premis.getEventType().getStringValue());
        assertEquals(CorporaConstants.SUCCESS,
                event_premis.getEventOutcomeInformationArray(0).getEventOutcome().getStringValue());

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testCreateAIPVersionEAD3() throws RODAException, ParseException, IOException, XmlException {

        // generate AIP ID
        final String aipId = IdUtils.createUUID();

        // testing AIP
        final AIP aip = model.createAIP(aipId, corporaService, DefaultStoragePath
                .parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_VERSION_EAD_3),
                RodaConstants.ADMIN);

        assertNotNull(aip);
        assertEquals(aipId, aip.getId());
        assertNull("AIP_1 should not have a parent", aip.getParentId());
        assertThat(aip.getState(), Is.is(AIPState.ACTIVE));

        List<String> descriptiveMetadataIds = aip.getDescriptiveMetadata().stream().map(dm -> dm.getId())
                .collect(Collectors.toList());
        assertThat(descriptiveMetadataIds, containsInAnyOrder(CorporaConstants.DESCRIPTIVE_METADATA_ID_EAD3));

        List<String> representationIds = aip.getRepresentations().stream().map(rep -> rep.getId())
                .collect(Collectors.toList());

        assertThat(representationIds,
                containsInAnyOrder(CorporaConstants.REPRESENTATION_1_ID, CorporaConstants.REPRESENTATION_2_ID));

        // testing descriptive metadata
        final DescriptiveMetadata descMetadata = model.retrieveDescriptiveMetadata(aipId,
                CorporaConstants.DESCRIPTIVE_METADATA_ID_EAD3);

        assertEquals(aipId, descMetadata.getAipId());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_ID_EAD3, descMetadata.getId());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_TYPE_EAD, descMetadata.getType());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_TYPE_EAD_VERSION3, descMetadata.getVersion());

        StoragePath descriptiveMetadataPath = ModelUtils.getDescriptiveMetadataStoragePath(descMetadata.getAipId(),
                descMetadata.getId());
        final Binary descMetadataBinary = storage.getBinary(descriptiveMetadataPath);
        assertTrue(descMetadataBinary.getSizeInBytes() > 0);
        assertEquals(descMetadataBinary.getSizeInBytes().intValue(),
                IOUtils.toByteArray(descMetadataBinary.getContent().createInputStream()).length);

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testCreateAIPVersionUnknown() throws RODAException, ParseException, IOException, XmlException {

        // generate AIP ID
        final String aipId = IdUtils.createUUID();

        // testing AIP
        final AIP aip = model.createAIP(aipId, corporaService, DefaultStoragePath
                .parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_VERSION_EAD_UNKNOWN),
                RodaConstants.ADMIN);

        assertNotNull(aip);
        assertEquals(aipId, aip.getId());
        assertNull("AIP_1 should not have a parent", aip.getParentId());
        assertThat(aip.getState(), Is.is(AIPState.ACTIVE));

        List<String> descriptiveMetadataIds = aip.getDescriptiveMetadata().stream().map(dm -> dm.getId())
                .collect(Collectors.toList());
        assertThat(descriptiveMetadataIds, containsInAnyOrder(CorporaConstants.DESCRIPTIVE_METADATA_ID_EADUNKNOWN));

        List<String> representationIds = aip.getRepresentations().stream().map(rep -> rep.getId())
                .collect(Collectors.toList());

        assertThat(representationIds,
                containsInAnyOrder(CorporaConstants.REPRESENTATION_1_ID, CorporaConstants.REPRESENTATION_2_ID));

        // testing descriptive metadata
        final DescriptiveMetadata descMetadata = model.retrieveDescriptiveMetadata(aipId,
                CorporaConstants.DESCRIPTIVE_METADATA_ID_EADUNKNOWN);

        assertEquals(aipId, descMetadata.getAipId());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_ID_EADUNKNOWN, descMetadata.getId());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_TYPE_EAD, descMetadata.getType());
        assertEquals(CorporaConstants.DESCRIPTIVE_METADATA_TYPE_EAD_VERSIONUNKNOWN, descMetadata.getVersion());

        StoragePath descriptiveMetadataPath = ModelUtils.getDescriptiveMetadataStoragePath(descMetadata.getAipId(),
                descMetadata.getId());
        final Binary descMetadataBinary = storage.getBinary(descriptiveMetadataPath);
        assertTrue(descMetadataBinary.getSizeInBytes() > 0);
        assertEquals(descMetadataBinary.getSizeInBytes().intValue(),
                IOUtils.toByteArray(descMetadataBinary.getContent().createInputStream()).length);

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testCreateAIPWithSubFolders() throws RODAException, ParseException, IOException {

        // generate AIP ID
        final String aipId = IdUtils.createUUID();

        // testing AIP
        model.createAIP(aipId, corporaService, DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER,
                CorporaConstants.SOURCE_AIP_REP_WITH_SUBFOLDERS), RodaConstants.ADMIN);

        CloseableIterable<OptionalWithCause<File>> allFiles = model.listFilesUnder(aipId,
                CorporaConstants.REPRESENTATION_1_ID, true);

        List<File> reusableList = new ArrayList<>();
        Iterables.addAll(reusableList, Lists.newArrayList(allFiles).stream().filter(f -> f.isPresent())
                .map(f -> f.get()).collect(Collectors.toList()));
        allFiles.close();

        assertTrue(reusableList.contains(new File("2012-roda-promo-en.pdf", aipId,
                CorporaConstants.REPRESENTATION_1_ID, new ArrayList<>(), false)));
        assertTrue(reusableList.contains(
                new File("folder", aipId, CorporaConstants.REPRESENTATION_1_ID, new ArrayList<>(), true)));
        assertTrue(reusableList.contains(
                new File("subfolder", aipId, CorporaConstants.REPRESENTATION_1_ID, Arrays.asList("folder"), true)));
        assertTrue(reusableList.contains(new File("RODA 2 logo.svg", aipId, CorporaConstants.REPRESENTATION_1_ID,
                Arrays.asList("folder", "subfolder"), false)));

        assertTrue(reusableList.contains(new File("RODA 2 logo-circle-black.svg", aipId,
                CorporaConstants.REPRESENTATION_1_ID, Arrays.asList("folder"), false)));
        assertTrue(reusableList.contains(new File("RODA 2 logo-circle-white.svg", aipId,
                CorporaConstants.REPRESENTATION_1_ID, Arrays.asList("folder"), false)));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testDeleteAIP() throws RODAException {
        // generate AIP ID
        final String aipId = IdUtils.createUUID();

        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        model.deleteAIP(aipId);

        // check if AIP was indeed deleted
        try {
            model.retrieveAIP(aipId);
            Assert.fail("AIP should have been deleted, but yet was retrieved.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        // check if AIP sub-resources were recursively deleted
        try {
            model.retrieveDescriptiveMetadata(aipId, CorporaConstants.DESCRIPTIVE_METADATA_ID);
            Assert.fail("Descriptive metadata binary should have been deleted, but yet was retrieved.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        try {
            model.retrieveRepresentation(aipId, CorporaConstants.REPRESENTATION_1_ID);
            Assert.fail("Descriptive metadata binary should have been deleted, but yet was retrieved.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        try {
            model.retrieveRepresentation(aipId, CorporaConstants.REPRESENTATION_2_ID);
            Assert.fail("Representation should have been deleted, but yet was retrieved.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        try {
            model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                    CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
            Assert.fail("File should have been deleted, but yet was retrieved.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }
    }

    // TODO test if deleting AIP also deletes all sub-AIPs

    @Test
    public void testListAIPs() throws RODAException {

        // generate AIP ID
        final String aip1Id = IdUtils.createUUID();
        final String aip2Id = IdUtils.createUUID();
        final String aip3Id = IdUtils.createUUID();

        final AIP aip1 = model.createAIP(aip1Id, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);
        final AIP aip2 = model.createAIP(aip2Id, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);
        final AIP aip3 = model.createAIP(aip3Id, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        Iterable<OptionalWithCause<AIP>> listAIPs = model.listAIPs();
        List<AIP> reusableList = new ArrayList<>();
        Iterables.addAll(reusableList, Lists.newArrayList(listAIPs).stream().filter(f -> f.isPresent())
                .map(f -> f.get()).collect(Collectors.toList()));

        assertThat(reusableList, containsInAnyOrder(aip1, aip2, aip3));

        // cleanup
        model.deleteAIP(aip1Id);
        model.deleteAIP(aip2Id);
        model.deleteAIP(aip3Id);

    }

    @Test
    public void testCreateAIPWithExistingId() throws RODAException {
        // generate AIP ID
        final String aipId = IdUtils.createUUID();

        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        try {
            model.createAIP(aipId, corporaService,
                    DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                    RodaConstants.ADMIN);
            Assert.fail("AIP shouldn't have been created and yet it was.");

        } catch (AlreadyExistsException e) {
            // do nothing as it was expected
        }

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testUpdateAIP() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        StoragePath otherAipPath = DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER,
                CorporaConstants.OTHER_AIP_ID);
        AIP updatedAIP = model.updateAIP(aipId, corporaService, otherAipPath, RodaConstants.ADMIN);

        // check it is connected
        AIP retrievedAIP = model.retrieveAIP(aipId);
        assertEquals(updatedAIP, retrievedAIP);

        // check content is correct
        StorageTestUtils.testEntityEqualRecursively(corporaService, otherAipPath, storage,
                ModelUtils.getAIPStoragePath(aipId));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testListDescriptiveMetadata() throws RODAException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        Iterable<DescriptiveMetadata> list = model.retrieveAIP(aipId).getDescriptiveMetadata();
        DescriptiveMetadata descriptiveMetadata1 = model.retrieveDescriptiveMetadata(aipId,
                CorporaConstants.DESCRIPTIVE_METADATA_ID);

        assertThat(list, containsInAnyOrder(descriptiveMetadata1));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testCreateDescriptiveMetadata() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final String newDescriptiveMetadataId = IdUtils.createUUID();
        final Binary binary = corporaService
                .getBinary(DefaultStoragePath.parse(CorporaConstants.OTHER_DESCRIPTIVE_METADATA_STORAGEPATH));

        final DescriptiveMetadata newDescriptiveMetadata = model.createDescriptiveMetadata(aipId,
                newDescriptiveMetadataId, binary.getContent(), CorporaConstants.OTHER_DESCRIPTIVE_METADATA_TYPE,
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_VERSION);

        // check if it is connected
        DescriptiveMetadata retrievedDescriptiveMetadata = model.retrieveDescriptiveMetadata(aipId,
                newDescriptiveMetadataId);
        assertEquals(newDescriptiveMetadata, retrievedDescriptiveMetadata);

        // check content
        Binary newDescriptiveMetadataBinary = storage
                .getBinary(ModelUtils.getDescriptiveMetadataStoragePath(newDescriptiveMetadata));
        assertTrue(IOUtils.contentEquals(binary.getContent().createInputStream(),
                newDescriptiveMetadataBinary.getContent().createInputStream()));

        // cleanup
        model.deleteAIP(aipId);

    }

    @Test
    public void testUpdateDescriptiveMetadata() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final Binary binary = corporaService
                .getBinary(DefaultStoragePath.parse(CorporaConstants.OTHER_DESCRIPTIVE_METADATA_STORAGEPATH));

        Map<String, String> properties = new HashMap<>();
        properties.put(RodaConstants.VERSION_ACTION, RodaConstants.VersionAction.UPDATED.toString());

        final DescriptiveMetadata updatedDescriptiveMetadata = model.updateDescriptiveMetadata(aipId,
                CorporaConstants.DESCRIPTIVE_METADATA_ID, binary.getContent(),
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_TYPE,
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_VERSION, properties);

        // check if it is connected
        DescriptiveMetadata retrievedDescriptiveMetadata = model.retrieveDescriptiveMetadata(aipId,
                CorporaConstants.DESCRIPTIVE_METADATA_ID);
        assertEquals(updatedDescriptiveMetadata, retrievedDescriptiveMetadata);

        // check content
        StoragePath storagePath = ModelUtils.getDescriptiveMetadataStoragePath(updatedDescriptiveMetadata);
        Binary updatedDescriptiveMetadataBinary = storage.getBinary(storagePath);
        assertTrue(IOUtils.contentEquals(binary.getContent().createInputStream(),
                updatedDescriptiveMetadataBinary.getContent().createInputStream()));

        // check if binary version was created
        assertEquals(1, Iterables.size(storage.listBinaryVersions(storagePath)));

        // check if binary version message collisions are well treated
        model.updateDescriptiveMetadata(aipId, CorporaConstants.DESCRIPTIVE_METADATA_ID, binary.getContent(),
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_TYPE,
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_VERSION, properties);
        model.updateDescriptiveMetadata(aipId, CorporaConstants.DESCRIPTIVE_METADATA_ID, binary.getContent(),
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_TYPE,
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_VERSION, properties);
        model.updateDescriptiveMetadata(aipId, CorporaConstants.DESCRIPTIVE_METADATA_ID, binary.getContent(),
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_TYPE,
                CorporaConstants.OTHER_DESCRIPTIVE_METADATA_VERSION, properties);

        assertEquals(4, Iterables.size(storage.listBinaryVersions(storagePath)));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testDeleteDescriptiveMetadata() throws RODAException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        model.deleteDescriptiveMetadata(aipId, CorporaConstants.DESCRIPTIVE_METADATA_ID);

        // check if it deleted
        try {
            model.retrieveDescriptiveMetadata(aipId, CorporaConstants.DESCRIPTIVE_METADATA_ID);
            Assert.fail("Descriptive metadata deleted and yet exists.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testListRepresentations() throws RODAException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final Representation representation1 = model.retrieveRepresentation(aipId,
                CorporaConstants.REPRESENTATION_1_ID);
        final Representation representation2 = model.retrieveRepresentation(aipId,
                CorporaConstants.REPRESENTATION_2_ID);

        final Iterable<Representation> list = model.retrieveAIP(aipId).getRepresentations();
        assertThat(list, containsInAnyOrder(representation1, representation2));

        CloseableIterable<OptionalWithCause<Representation>> list2 = model.list(Representation.class);
        assertThat(Iterables.transform(list2, i -> i.get()), containsInAnyOrder(representation1, representation2));

        // cleanup
        model.deleteAIP(aipId);

    }

    @Test
    public void testCreateRepresentation() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final String newRepresentationId = IdUtils.createUUID();
        final StoragePath corporaRepresentationPath = DefaultStoragePath
                .parse(CorporaConstants.OTHER_REPRESENTATION_STORAGEPATH);

        Representation createdRepresentation = model.createRepresentation(aipId, newRepresentationId,
                CorporaConstants.REPRESENTATION_1_ORIGINAL, CorporaConstants.REPRESENTATION_1_TYPE, corporaService,
                DefaultStoragePath.parse(CorporaConstants.OTHER_REPRESENTATION_STORAGEPATH), false);

        // check if it is connected
        Representation retrievedRepresentation = model.retrieveRepresentation(aipId, newRepresentationId);
        assertEquals(createdRepresentation, retrievedRepresentation);

        // check content
        StorageTestUtils.testEntityEqualRecursively(corporaService, corporaRepresentationPath, storage,
                ModelUtils.getRepresentationStoragePath(aipId, newRepresentationId));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testUpdateRepresentation() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final StoragePath corporaRepresentationPath = DefaultStoragePath
                .parse(CorporaConstants.OTHER_REPRESENTATION_STORAGEPATH);
        Representation updatedRepresentation = model.updateRepresentation(aipId,
                CorporaConstants.REPRESENTATION_1_ID, CorporaConstants.REPRESENTATION_1_ORIGINAL,
                CorporaConstants.REPRESENTATION_1_TYPE, corporaService,
                DefaultStoragePath.parse(CorporaConstants.OTHER_REPRESENTATION_STORAGEPATH));

        // check if it is connected
        Representation retrievedRepresentation = model.retrieveRepresentation(aipId,
                CorporaConstants.REPRESENTATION_1_ID);
        assertEquals(updatedRepresentation, retrievedRepresentation);

        // check content
        StorageTestUtils.testEntityEqualRecursively(corporaService, corporaRepresentationPath, storage,
                ModelUtils.getRepresentationStoragePath(aipId, CorporaConstants.REPRESENTATION_1_ID));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testDeleteRepresentation() throws RODAException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        model.deleteRepresentation(aipId, CorporaConstants.REPRESENTATION_1_ID);

        // check if it deleted
        try {
            model.retrieveRepresentation(aipId, CorporaConstants.REPRESENTATION_1_ID);
            Assert.fail("Representation deleted and yet exists.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        // check if file under representation was deleted
        try {
            model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                    CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
            Assert.fail("Representation deleted and yet one of its files still exist.");
        } catch (NotFoundException e) {
            // do nothing as it was expected
        }

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testCreateFile() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final String newFileId = IdUtils.createUUID();
        final List<String> newFileDirectoryPath = new ArrayList<>();
        final StoragePath corporaFilePath = DefaultStoragePath.parse(CorporaConstants.OTHER_FILE_STORAGEPATH);
        final Binary binary = corporaService.getBinary(corporaFilePath);

        File createdFile = model.createFile(aipId, CorporaConstants.REPRESENTATION_1_ID, newFileDirectoryPath,
                newFileId, binary.getContent(), true);

        // check if it is connected
        File retrievedFile = model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID, newFileDirectoryPath,
                newFileId);
        assertEquals(createdFile, retrievedFile);

        // check content
        Binary createdFileBinary = storage.getBinary(ModelUtils.getFileStoragePath(createdFile));
        assertTrue(IOUtils.contentEquals(binary.getContent().createInputStream(),
                createdFileBinary.getContent().createInputStream()));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testUpdateFile() throws RODAException, IOException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        final StoragePath corporaFilePath = DefaultStoragePath.parse(CorporaConstants.OTHER_FILE_STORAGEPATH);
        final Binary binary = corporaService.getBinary(corporaFilePath);

        final boolean createIfNotExists = false;
        final boolean notify = false;
        File createdFile = model.updateFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID,
                binary.getContent(), createIfNotExists, notify);

        // check if it is connected
        File retrievedFile = model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
        assertEquals(createdFile, retrievedFile);

        // check content
        Binary createdFileBinary = storage.getBinary(ModelUtils.getFileStoragePath(createdFile));
        assertTrue(IOUtils.contentEquals(binary.getContent().createInputStream(),
                createdFileBinary.getContent().createInputStream()));

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testDeleteFile() throws RODAException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        model.deleteFile(aipId, CorporaConstants.REPRESENTATION_1_ID, CorporaConstants.REPRESENTATION_1_FILE_1_PATH,
                CorporaConstants.REPRESENTATION_1_FILE_1_ID, true);

        // check if it deleted
        try {
            model.retrieveFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                    CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
            Assert.fail("File deleted and yet exists.");
        } catch (NotFoundException e) {
            // do nothing
        }

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testRetrieveEventPreservationObject() throws RODAException {
        // set up
        final String aipId = IdUtils.createUUID();
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        Binary event_bin = model.retrievePreservationEvent(aipId, CorporaConstants.REPRESENTATION_1_ID, null, null,
                CorporaConstants.REPRESENTATION_1_PREMIS_EVENT_ID);
        EventComplexType event = PremisV3Utils.binaryToEvent(event_bin.getContent(), true);

        assertEquals(CorporaConstants.AGENT_RODA_8,
                event.getLinkingAgentIdentifierArray(0).getLinkingAgentIdentifierValue());
        assertEquals(CorporaConstants.INGESTION, event.getEventType().getStringValue());
    }

    @Test
    public void testRepresentationFileObject() throws RODAException {
        // set up
        final String aipId = CorporaConstants.SOURCE_AIP_ID;
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        Binary file_bin = model.retrievePreservationFile(aipId, CorporaConstants.REPRESENTATION_1_ID,
                CorporaConstants.REPRESENTATION_1_FILE_1_PATH, CorporaConstants.REPRESENTATION_1_FILE_1_ID);
        gov.loc.premis.v3.File file = PremisV3Utils.binaryToFile(file_bin.getContent(), true);

        ObjectCharacteristicsComplexType file_characteristics = file.getObjectCharacteristicsArray(0);
        assertEquals(2, file_characteristics.getFixityArray().length);
        assertEquals(CorporaConstants.METS_XML, file.getOriginalName().getStringValue());

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void testRepresentationPreservationObject() throws RODAException {
        // set up

        final String aipId = CorporaConstants.SOURCE_AIP_ID;
        model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);

        Binary representation_bin = model.retrievePreservationRepresentation(aipId,
                CorporaConstants.REPRESENTATION_1_ID);

        gov.loc.premis.v3.Representation representation = PremisV3Utils
                .binaryToRepresentation(representation_bin.getContent(), true);

        assertEquals(representation.getPreservationLevelArray(0).getPreservationLevelValue().getStringValue(),
                CorporaConstants.PRESERVATION_LEVEL_FULL);

        // cleanup
        model.deleteAIP(aipId);
    }

    @Test
    public void getAgentPreservationObject() throws RODAException {
        // pre-load the preservation container data
        DefaultStoragePath preservationContainerPath = DefaultStoragePath
                .parse(CorporaConstants.SOURCE_PRESERVATION_CONTAINER);

        storage.deleteContainer(preservationContainerPath);
        storage.copy(corporaService, preservationContainerPath,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_PRESERVATION_CONTAINER));

        Binary agent_bin = model.retrievePreservationAgent(CorporaConstants.AGENT_RODA_8);
        AgentComplexType agent = PremisV3Utils.binaryToAgent(agent_bin.getContent(), true);

        assertEquals(CorporaConstants.AGENT_RODA_8, agent.getAgentIdentifierArray(0).getAgentIdentifierValue());
        assertEquals(CorporaConstants.SOFTWARE_INGEST_TASK, agent.getAgentType().getStringValue());
        assertEquals(CorporaConstants.INGEST_CREATE_AIP, agent.getAgentNameArray(0).getStringValue());

    }

    @Test
    public void createLogEntry() throws RODAException {
        // setup
        createLogActionDirectory();

        LogEntry entry = new LogEntry();
        entry.setActionComponent("Action");
        entry.setActionMethod("Method");
        entry.setAddress("Address");
        entry.setId("ID");
        entry.setDatetime(new Date());
        entry.setState(LOG_ENTRY_STATE.SUCCESS);
        List<LogEntryParameter> parameters = new ArrayList<>();
        parameters.add(new LogEntryParameter("NAME1", "VALUE1"));
        parameters.add(new LogEntryParameter("NAME2", "VALUE2"));
        entry.setParameters(parameters);
        model.addLogEntry(entry, logPath);
    }

    private void createLogActionDirectory() {
        try {
            Files.createDirectories(logPath);
        } catch (IOException e) {
        }
    }

    @Test
    public void testMemberInheritance() throws RODAException {
        // create group 1
        Group group1 = new Group("group-1");
        group1.setActive(true);
        group1.setFullName("NAMEGROUP1");
        group1.setDirectRoles(new HashSet<>(Arrays.asList(ROLE1)));
        model.createGroup(group1, true);

        // gen. asserts for group 1
        Group retrievedGroup1 = model.retrieveGroup(group1.getId());
        Assert.assertNotNull(retrievedGroup1);

        // create group 2
        Group group2 = new Group("group2");
        group2.setActive(true);
        group2.setFullName("NAMEGROUP2");
        group2.setDirectRoles(new HashSet<>(Arrays.asList(ROLE2)));
        model.createGroup(group2, true);

        // gen. asserts for group 2
        Group retrievedGroup2 = model.retrieveGroup(group2.getId());
        Assert.assertNotNull(retrievedGroup2);
        MatcherAssert.assertThat(retrievedGroup2.getAllRoles(), Matchers.containsInAnyOrder(ROLE2));

        // create user 1
        User user = new User("user_1");
        user.setActive(true);
        user.setEmail("user1@example.com");
        user.setGuest(false);
        user.setFullName("user1");
        user.addGroup(group1.getId());
        user.addGroup(group2.getId());
        model.createUser(user, true);

        // gen. asserts for user 1
        User retrievedUser = model.retrieveUserByName(user.getId());
        Assert.assertNotNull(retrievedUser);
        MatcherAssert.assertThat(retrievedUser.getGroups(),
                Matchers.containsInAnyOrder(group1.getId(), group2.getId()));
        MatcherAssert.assertThat(retrievedUser.getAllRoles(), Matchers.containsInAnyOrder(ROLE1, ROLE2));

        // modify group 2 groups
        model.deleteGroup(group1.getId(), false);

        try {
            model.retrieveGroup(group1.getId());
            Assert.fail("should have not found exception");
        } catch (NotFoundException e) {
            // expected
        }

        User retrievedUser2 = model.retrieveUserByName(user.getId());
        Assert.assertNotNull(retrievedUser2);
        MatcherAssert.assertThat(retrievedUser2.getGroups(), Matchers.containsInAnyOrder(group2.getId()));
        MatcherAssert.assertThat(retrievedUser2.getGroups(),
                Matchers.not(Matchers.containsInAnyOrder(group1.getId())));
        MatcherAssert.assertThat(retrievedUser2.getAllRoles(), Matchers.containsInAnyOrder(ROLE2));
        MatcherAssert.assertThat(retrievedUser2.getAllRoles(), Matchers.not(Matchers.containsInAnyOrder(ROLE1)));

    }

    @Test
    public void testListing() throws RODAException, IOException {
        populate(RodaCoreFactory.getTransferredResourcesScanner().getBasePath());
        CloseableIterable<OptionalWithCause<LiteRODAObject>> list = model.listLite(TransferredResource.class);
        int size = CloseableIterables.size(list);
        assertEquals(fileCounter, size);
    }

    @Test
    public void testLiteOptionalWithCauseSerialization() throws RODAException, IOException, ClassNotFoundException {
        final String aipId = CorporaConstants.SOURCE_AIP_ID;
        AIP aip = model.createAIP(aipId, corporaService,
                DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_ID),
                RodaConstants.ADMIN);
        Optional<LiteRODAObject> lightAIP = model.retrieveLiteFromObject(aip);

        // cleanup
        model.deleteAIP(aipId);

        if (lightAIP.isPresent()) {
            LiteOptionalWithCause test = LiteOptionalWithCause.of(lightAIP.get());
            Path tempFile = Files.createTempFile(basePath, "test", ".tmp");

            try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(tempFile))) {
                oos.writeObject(test);
            }

            try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(tempFile))) {
                LiteOptionalWithCause test2 = (LiteOptionalWithCause) ois.readObject();
                assertEquals(test, test2);
            }
        } else {
            fail("Should be present");
        }
    }

    private static void populate(Path basePath) throws IOException {
        Random randomno = new Random();
        int numberOfItemsByLevel = nextIntInRange(2, 3, randomno);
        int numberOfLevels = nextIntInRange(2, 3, randomno);
        populate(basePath, numberOfItemsByLevel, numberOfLevels, 0, randomno);
    }

    private static void populate(Path path, int numberOfItemsByLevel, int numberOfLevels, int currentLevel,
            Random randomno) throws IOException {
        currentLevel++;
        for (int i = 0; i < numberOfItemsByLevel; i++) {
            Path p;
            if (i % 2 == 0) {
                if (currentLevel > 1) {
                    p = Files.createFile(path.resolve(IdUtils.createUUID() + ".txt"));
                    Files.write(p, "NUNCAMAISACABA".getBytes());
                    fileCounter++;
                }
            } else {
                p = Files.createDirectory(path.resolve(IdUtils.createUUID()));
                fileCounter++;
                if (currentLevel <= numberOfLevels) {
                    populate(p, numberOfItemsByLevel, numberOfLevels, currentLevel, randomno);
                } else {
                    if (currentLevel > 1) {
                        for (int j = 0; j < numberOfItemsByLevel; j++) {
                            Path temp = Files.createFile(p.resolve(IdUtils.createUUID() + ".txt"));
                            Files.write(temp, "NUNCAMAISACABA".getBytes());
                            fileCounter++;
                        }
                    }
                }
            }
        }
    }

    static int nextIntInRange(int min, int max, Random rng) {
        if (min > max) {
            throw new IllegalArgumentException(
                    "Cannot draw random int from invalid range [" + min + ", " + max + "].");
        }
        int diff = max - min;
        if (diff >= 0 && diff != Integer.MAX_VALUE) {
            return (min + rng.nextInt(diff + 1));
        }
        int i;
        do {
            i = rng.nextInt();
        } while (i < min || i > max);
        return i;
    }

}