nl.mpi.lamus.workspace.upload.implementation.LamusWorkspaceUploaderTest.java Source code

Java tutorial

Introduction

Here is the source code for nl.mpi.lamus.workspace.upload.implementation.LamusWorkspaceUploaderTest.java

Source

/*
 * Copyright (C) 2013 Max Planck Institute for Psycholinguistics
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.mpi.lamus.workspace.upload.implementation;

import eu.clarin.cmdi.validator.CMDIValidatorInitException;
import nl.mpi.lamus.workspace.importing.implementation.FileImportProblem;
import nl.mpi.lamus.workspace.importing.implementation.ImportProblem;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import nl.mpi.archiving.corpusstructure.core.NodeNotFoundException;
import nl.mpi.handle.util.HandleParser;
import nl.mpi.handle.util.implementation.HandleConstants;
import nl.mpi.lamus.archive.ArchiveFileHelper;
import nl.mpi.lamus.archive.ArchiveFileLocationProvider;
import nl.mpi.lamus.dao.WorkspaceDao;
import nl.mpi.lamus.exception.DisallowedPathException;
import nl.mpi.lamus.exception.MetadataValidationException;
import nl.mpi.lamus.exception.WorkspaceNodeNotFoundException;
import nl.mpi.lamus.filesystem.WorkspaceDirectoryHandler;
import nl.mpi.lamus.typechecking.TypecheckedResults;
import nl.mpi.lamus.exception.TypeCheckerException;
import nl.mpi.lamus.exception.WorkspaceException;
import nl.mpi.lamus.filesystem.WorkspaceFileHandler;
import nl.mpi.lamus.workspace.factory.WorkspaceNodeFactory;
import nl.mpi.lamus.workspace.importing.NodeDataRetriever;
import nl.mpi.lamus.workspace.model.WorkspaceNode;
import nl.mpi.lamus.workspace.model.WorkspaceNodeStatus;
import nl.mpi.lamus.workspace.model.WorkspaceNodeType;
import nl.mpi.lamus.workspace.model.implementation.LamusWorkspaceNode;
import nl.mpi.lamus.metadata.MetadataApiBridge;
import nl.mpi.lamus.metadata.validation.WorkspaceFileValidator;
import nl.mpi.lamus.metadata.validation.implementation.MetadataValidationIssue;
import nl.mpi.lamus.metadata.validation.implementation.MetadataValidationIssueSeverity;
import nl.mpi.lamus.typechecking.testing.ValidationIssueCollectionMatcher;
import nl.mpi.lamus.workspace.model.NodeUtil;
import nl.mpi.lamus.workspace.model.Workspace;
import nl.mpi.lamus.workspace.upload.WorkspaceUploadHelper;
import nl.mpi.lamus.workspace.upload.WorkspaceUploader;
import nl.mpi.metadata.api.MetadataAPI;
import nl.mpi.metadata.api.MetadataException;
import nl.mpi.metadata.api.model.MetadataDocument;
import nl.mpi.metadata.api.type.MetadataDocumentType;

import org.apache.commons.fileupload.FileItem;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.jmock.Expectations;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
import org.jmock.auto.Mock;
import org.jmock.integration.junit4.JUnitRuleMockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.rules.TemporaryFolder;

import org.jmock.lib.concurrent.Synchroniser;

/**
 *
 * @author guisil
 */
public class LamusWorkspaceUploaderTest {

    @Rule
    public JUnitRuleMockery context = new JUnitRuleMockery() {
        {
            setThreadingPolicy(new Synchroniser());
            setImposteriser(ClassImposteriser.INSTANCE);
        }
    };

    @Rule
    public TemporaryFolder testFolder = new TemporaryFolder();

    @Mock
    NodeDataRetriever mockNodeDataRetriever;
    @Mock
    WorkspaceDirectoryHandler mockWorkspaceDirectoryHandler;
    @Mock
    WorkspaceFileHandler mockWorkspaceFileHandler;
    @Mock
    WorkspaceNodeFactory mockWorkspaceNodeFactory;
    @Mock
    WorkspaceDao mockWorkspaceDao;
    @Mock
    WorkspaceUploadHelper mockWorkspaceUploadHelper;
    @Mock
    MetadataAPI mockMetadataAPI;
    @Mock
    MetadataApiBridge mockMetadataApiBridge;
    @Mock
    WorkspaceFileValidator mockWorkspaceFileValidator;
    @Mock
    ArchiveFileLocationProvider mockArchiveFileLocationProvider;
    @Mock
    ArchiveFileHelper mockArchiveFileHelper;
    @Mock
    NodeUtil mockNodeUtil;
    @Mock
    HandleParser mockHandleParser;

    @Mock
    Workspace mockWorkspace;
    @Mock
    FileItem mockFileItem;
    @Mock
    InputStream mockInputStream;
    @Mock
    FileInputStream mockFileInputStream;
    @Mock
    File mockUploadedFile;
    @Mock
    File mockWorkspaceTopNodeFile;
    @Mock
    WorkspaceNode mockWorkspaceTopNode;
    @Mock
    TypecheckedResults mockTypecheckedResults;

    @Mock
    MetadataDocument mockMetadataDocument;
    @Mock
    MetadataDocumentType mockMetadataDocumentType;

    @Mock
    ZipInputStream mockZipInputStream;
    @Mock
    ZipEntry mockFirstZipEntry;
    @Mock
    ZipEntry mockSecondZipEntry;
    @Mock
    ZipEntry mockThirdZipEntry;

    @Mock
    File mockFile1;
    @Mock
    File mockFile2;

    @Mock
    MetadataValidationIssue mockValidationIssue1;
    @Mock
    MetadataValidationIssue mockValidationIssue2;
    @Mock
    MetadataValidationException mockValidationException;
    @Mock
    CMDIValidatorInitException mockCMDIValidatorInitException;
    @Mock
    ImportProblem mockUploadProblem;

    @Factory
    public static Matcher<Collection<MetadataValidationIssue>> equivalentValidationIssueCollection(
            Collection<MetadataValidationIssue> collection) {
        return new ValidationIssueCollectionMatcher(collection);
    }

    private WorkspaceUploader uploader;

    private File baseDirectory;
    private File workspaceBaseDirectory;// = new File("/lamus/workspaces");
    private final String workspaceUploadDirectoryName = "upload";

    private final int workspaceID = 1;
    private File workspaceDirectory;// = new File(workspaceBaseDirectory, "" + workspaceID);
    private File workspaceUploadDirectory;// = new File(workspaceDirectory, workspaceUploadDirectoryName);

    private final String handlePrefixWithSlash = "11142/";
    private final String handleProxyPlusPrefixWithSlash = HandleConstants.HDL_SHORT_PROXY + ":"
            + handlePrefixWithSlash;

    public LamusWorkspaceUploaderTest() {
    }

    @BeforeClass
    public static void setUpClass() {
    }

    @AfterClass
    public static void tearDownClass() {
    }

    @Before
    public void setUp() throws IOException {
        uploader = new LamusWorkspaceUploader(mockNodeDataRetriever, mockWorkspaceDirectoryHandler,
                mockWorkspaceFileHandler, mockWorkspaceNodeFactory, mockWorkspaceDao, mockWorkspaceUploadHelper,
                mockMetadataAPI, mockMetadataApiBridge, mockWorkspaceFileValidator, mockArchiveFileLocationProvider,
                mockArchiveFileHelper, mockNodeUtil, mockHandleParser);

        baseDirectory = testFolder.newFolder("lamus");
        workspaceBaseDirectory = new File(baseDirectory, "workspace");
        workspaceDirectory = new File(workspaceBaseDirectory, "" + workspaceID);
        workspaceUploadDirectory = new File(workspaceDirectory, workspaceUploadDirectoryName);
    }

    @After
    public void tearDown() {
    }

    @Test
    public void getUploadDirectory() {

        context.checking(new Expectations() {
            {
                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
            }
        });

        File result = uploader.getWorkspaceUploadDirectory(workspaceID);

        assertEquals("Retrieved file different from expected", workspaceUploadDirectory, result);
    }

    @Test
    public void uploadFile_nameNotValid() throws IOException, DisallowedPathException {

        final String filename = "file with spaces.txt";
        final String correctedFilename = "file_with_spaces.txt";

        context.checking(new Expectations() {
            {

                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(filename)), with(any(String.class)));
                will(returnValue(correctedFilename));
            }
        });

        try {
            uploader.uploadFileIntoWorkspace(workspaceID, mockInputStream, filename);
            fail("should have thrown exception");
        } catch (DisallowedPathException ex) {
            assertEquals("Problematic filename different from expected", filename, ex.getProblematicPath());
        }
    }

    @Test
    public void uploadFile_nameNotAllowed() throws IOException, DisallowedPathException {

        final String filename = "temp";
        final DisallowedPathException expectedException = new DisallowedPathException(filename,
                "path is not allowed and so on");

        context.checking(new Expectations() {
            {

                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(filename)), with(any(String.class)));
                will(returnValue(filename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(filename);
                will(throwException(expectedException));
            }
        });

        try {
            uploader.uploadFileIntoWorkspace(workspaceID, mockInputStream, filename);
            fail("should have thrown exception");
        } catch (DisallowedPathException ex) {
            assertEquals("Exception different from expected", expectedException, ex);
        }
    }

    @Test
    public void uploadFile_noNameChange() throws IOException, DisallowedPathException {

        final String filename = "file.cmdi";
        final File expectedFile = new File(workspaceUploadDirectory, filename);

        context.checking(new Expectations() {
            {

                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(filename)), with(any(String.class)));
                will(returnValue(filename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(filename);
                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockInputStream, expectedFile);
            }
        });

        File resultingFile = uploader.uploadFileIntoWorkspace(workspaceID, mockInputStream, filename);

        assertEquals("Resulting file different from expected", expectedFile, resultingFile);
    }

    @Test
    public void uploadFile_nameChanged() throws IOException, DisallowedPathException {

        final String filename = "file.cmdi";
        final File expectedFile = new File(workspaceUploadDirectory, filename);

        context.checking(new Expectations() {
            {

                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(filename)), with(any(String.class)));
                will(returnValue(filename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(filename);
                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockInputStream, expectedFile);
            }
        });

        File resultingFile = uploader.uploadFileIntoWorkspace(workspaceID, mockInputStream, filename);

        assertEquals("Resulting file different from expected", expectedFile, resultingFile);
    }

    @Test
    public void uploadFile_ThrowsException() throws IOException, DisallowedPathException {

        final String filename = "file.cmdi";
        final File expectedFile = new File(workspaceUploadDirectory, filename);

        final IOException expectedException = new IOException("some exception message");

        context.checking(new Expectations() {
            {

                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(filename)), with(any(String.class)));
                will(returnValue(filename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(filename);
                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockInputStream, expectedFile);
                will(throwException(expectedException));
            }
        });

        try {
            uploader.uploadFileIntoWorkspace(workspaceID, mockInputStream, filename);
            fail("should have thrown exception");
        } catch (IOException ex) {
            assertEquals("Exception different from expected", expectedException, ex);
        }
    }

    @Test
    public void uploadZipFile_IsDirectory() throws IOException, DisallowedPathException {

        final String firstEntryName = "directory";

        final ZipUploadResult expectedResult = new ZipUploadResult();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryName)),
                        with(any(String.class)));
                will(returnValue(firstEntryName));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                oneOf(mockWorkspaceDirectoryHandler).createDirectoryInWorkspace(workspaceID, firstEntryName);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_IsDirectory_EndingWithSlash() throws IOException, DisallowedPathException {

        final String firstEntryName = "directory/";
        final String firstEntryNameWithoutSlash = "directory";

        final ZipUploadResult expectedResult = new ZipUploadResult();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryNameWithoutSlash)),
                        with(any(String.class)));
                will(returnValue(firstEntryNameWithoutSlash));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                oneOf(mockWorkspaceDirectoryHandler).createDirectoryInWorkspace(workspaceID, firstEntryName);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_IsNotDirectory() throws IOException, DisallowedPathException {

        final String firstEntryName = "file.cmdi";
        final File firstEntryFile = new File(workspaceUploadDirectory, firstEntryName);

        final ZipUploadResult expectedResult = new ZipUploadResult();
        expectedResult.addSuccessfulUpload(firstEntryFile);

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryName)),
                        with(any(String.class)));
                will(returnValue(firstEntryName));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockZipInputStream, firstEntryFile);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_IsNotDirectory_fileNeedsRenaming() throws IOException, DisallowedPathException {

        final String firstEntryName = "file.cmdi";
        final File firstEntryFile = new File(workspaceUploadDirectory, firstEntryName);

        final ZipUploadResult expectedResult = new ZipUploadResult();
        expectedResult.addSuccessfulUpload(firstEntryFile);

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryName)),
                        with(any(String.class)));
                will(returnValue(firstEntryName));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockZipInputStream, firstEntryFile);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_DirectoryAndFile() throws IOException, DisallowedPathException {

        final String firstEntryName = "directory/";
        final String firstEntryNameWithoutSlash = "directory";
        final File createdDirectory = new File(workspaceUploadDirectory, firstEntryName);
        final String secondEntryName = "directory/file.cmdi";
        final String secondEntryFilename = "file.cmdi";
        final File createdFile = new File(workspaceUploadDirectory, secondEntryName);

        final ZipUploadResult expectedResult = new ZipUploadResult();
        expectedResult.addSuccessfulUpload(createdFile);

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryNameWithoutSlash)),
                        with(any(String.class)));
                will(returnValue(firstEntryNameWithoutSlash));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                oneOf(mockWorkspaceDirectoryHandler).createDirectoryInWorkspace(workspaceID, firstEntryName);
                will(new CreateDirectoryOnInvokeAction(createdDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockSecondZipEntry));

                // second loop iteration

                allowing(mockSecondZipEntry).getName();
                will(returnValue(secondEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(secondEntryFilename)),
                        with(any(String.class)));
                will(returnValue(secondEntryFilename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(secondEntryName);
                allowing(mockSecondZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockZipInputStream, createdFile);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_DirectoryAndFile_Windows() throws IOException, DisallowedPathException {

        final String entryName = "directory/file.cmdi";
        final File createdFile = new File(workspaceUploadDirectory, entryName);

        final ZipUploadResult expectedResult = new ZipUploadResult();
        expectedResult.addSuccessfulUpload(createdFile);

        context.checking(new Expectations() {
            {
                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                oneOf(mockFirstZipEntry).getName();
                will(returnValue(entryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(createdFile.getName())),
                        with(any(String.class)));
                will(returnValue(createdFile.getName()));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(entryName);
                oneOf(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));

                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockZipInputStream, createdFile);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_withOneFailedUpload() throws IOException, DisallowedPathException {

        final String firstEntryName = "directory/";
        final String firstEntryNameWithoutSlash = "directory";
        final File existingDirectory = new File(workspaceUploadDirectory, firstEntryName);
        existingDirectory.mkdirs();
        final String secondEntryName = "directory/file.cmdi";
        final String secondEntryFilename = "file.cmdi";
        final File existingFile = new File(workspaceUploadDirectory, secondEntryName);
        existingFile.createNewFile();

        final ZipUploadResult expectedResult = new ZipUploadResult();
        expectedResult.addFailedUpload(
                new FileImportProblem(existingFile, "A file with the same path already exists", null));

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryNameWithoutSlash)),
                        with(any(String.class)));
                will(returnValue(firstEntryNameWithoutSlash));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockSecondZipEntry));

                // second loop iteration

                allowing(mockSecondZipEntry).getName();
                will(returnValue(secondEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(secondEntryFilename)),
                        with(any(String.class)));
                will(returnValue(secondEntryFilename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(secondEntryName);
                allowing(mockSecondZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_withOneSuccessfulAndOneFailedUpload() throws IOException, DisallowedPathException {

        final String firstEntryName = "directory/";
        final String firstEntryNameWithoutSlash = "directory";
        final File existingDirectory = new File(workspaceUploadDirectory, firstEntryName);
        existingDirectory.mkdirs();
        final String secondEntryName = "directory/file1.cmdi";
        final String secondEntryFilename = "file1.cmdi";
        final File existingFile = new File(workspaceUploadDirectory, secondEntryName);
        existingFile.createNewFile();
        final String thirdEntryName = "directory/file2.cmdi";
        final String thirdEntryFilename = "file2.cmdi";
        final File createdFile = new File(workspaceUploadDirectory, thirdEntryName);

        final ZipUploadResult expectedResult = new ZipUploadResult();
        expectedResult.addFailedUpload(
                new FileImportProblem(existingFile, "A file with the same path already exists", null));
        expectedResult.addSuccessfulUpload(createdFile);

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryNameWithoutSlash)),
                        with(any(String.class)));
                will(returnValue(firstEntryNameWithoutSlash));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockSecondZipEntry));

                // second loop iteration

                allowing(mockSecondZipEntry).getName();
                will(returnValue(secondEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(secondEntryFilename)),
                        with(any(String.class)));
                will(returnValue(secondEntryFilename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(secondEntryName);
                allowing(mockSecondZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockThirdZipEntry));

                // third loop iteration

                allowing(mockThirdZipEntry).getName();
                will(returnValue(thirdEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(thirdEntryFilename)),
                        with(any(String.class)));
                will(returnValue(thirdEntryFilename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(thirdEntryName);
                allowing(mockThirdZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockZipInputStream, createdFile);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(null));
            }
        });

        ZipUploadResult result = uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);

        assertEquals("Result different from expected", expectedResult, result);
    }

    @Test
    public void uploadZipFile_File_NameNotValid() throws IOException, DisallowedPathException {

        final String firstEntryName = "file with spaces.txt";
        final String correctedEntryName = "file_with_spaces.txt";

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryName)),
                        with(any(String.class)));
                will(returnValue(correctedEntryName));
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                // nothing was created, so nothing has to be deleted
            }
        });

        try {
            uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);
            fail("should have thrown exception");
        } catch (DisallowedPathException ex) {
            assertEquals("Problematic filename different from expected", firstEntryName, ex.getProblematicPath());
        }
    }

    @Test
    public void uploadZipFile_DirectoryAndFile_NameNotAllowed() throws IOException, DisallowedPathException {

        final String firstEntryName = "temp/";
        final String firstEntryNameWithoutSlash = "temp";

        final DisallowedPathException expectedException = new DisallowedPathException(firstEntryName,
                "Path not allowed and so on...");

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryNameWithoutSlash)),
                        with(any(String.class)));
                will(returnValue(firstEntryNameWithoutSlash));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                will(throwException(expectedException));
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));
                // nothing was created, so nothing has to be deleted
            }
        });

        try {
            uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);
            fail("should have thrown exception");
        } catch (DisallowedPathException ex) {
            assertEquals("Exception different from expected", expectedException, ex);
        }
    }

    @Test
    public void uploadZipFile_MoreThanOneDirectoryAndFile_NameNotAllowed()
            throws IOException, DisallowedPathException {

        final String firstEntryName = "dir";
        final File createdDirectory = new File(workspaceUploadDirectory, firstEntryName);
        final String secondEntryName = "dir/file.cmdi";
        final String secondEntryFilename = "file.cmdi";
        final File createdFile = new File(workspaceUploadDirectory, secondEntryName);
        final String thirdEntryName = "temp/";
        final String thindEntryNameWithoutSlash = "temp";

        final DisallowedPathException expectedException = new DisallowedPathException(firstEntryName,
                "Path not allowed and so on...");

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockFirstZipEntry));
                allowing(mockFirstZipEntry).getName();
                will(returnValue(firstEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(firstEntryName)),
                        with(any(String.class)));
                will(returnValue(firstEntryName));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(firstEntryName);
                allowing(mockFirstZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                oneOf(mockWorkspaceDirectoryHandler).createDirectoryInWorkspace(workspaceID, firstEntryName);
                will(new CreateDirectoryOnInvokeAction(createdDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockSecondZipEntry));

                // second loop iteration

                allowing(mockSecondZipEntry).getName();
                will(returnValue(secondEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(secondEntryFilename)),
                        with(any(String.class)));
                will(returnValue(secondEntryFilename));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(secondEntryName);
                allowing(mockSecondZipEntry).isDirectory();
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).copyInputStreamToTargetFile(mockZipInputStream, createdFile);
                oneOf(mockZipInputStream).getNextEntry();
                will(returnValue(mockThirdZipEntry));

                // third loop iteration

                allowing(mockThirdZipEntry).getName();
                will(returnValue(thirdEntryName));
                oneOf(mockArchiveFileHelper).correctPathElement(with(equal(thindEntryNameWithoutSlash)),
                        with(any(String.class)));
                will(returnValue(thindEntryNameWithoutSlash));
                oneOf(mockWorkspaceDirectoryHandler).ensurePathIsAllowed(thirdEntryName);
                will(throwException(expectedException));
                allowing(mockThirdZipEntry).isDirectory();
                will(returnValue(Boolean.TRUE));

                // folder and file were created, so will have to be deleted in this case
                oneOf(mockWorkspaceFileHandler).deleteFile(createdFile);
                oneOf(mockWorkspaceFileHandler).deleteFile(createdDirectory);
            }
        });

        try {
            uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);
            fail("should have thrown exception");
        } catch (DisallowedPathException ex) {
            assertEquals("Exception different from expected", expectedException, ex);
        }
    }

    @Test
    public void uploadZipFile_ThrowsException() throws IOException, DisallowedPathException {

        final IOException expectedException = new IOException("some exception message");

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDirectoryHandler).getUploadDirectoryForWorkspace(workspaceID);
                will(returnValue(workspaceUploadDirectory));
                oneOf(mockZipInputStream).getNextEntry();
                will(throwException(expectedException));
            }
        });

        try {
            uploader.uploadZipFileIntoWorkspace(workspaceID, mockZipInputStream);
            fail("should have thrown exception");
        } catch (IOException ex) {
            assertEquals("Exception different from expected", expectedException, ex);
        }
    }

    @Test
    public void processOneUploadedResourceFile() throws IOException, WorkspaceNodeNotFoundException,
            URISyntaxException, WorkspaceException, NodeNotFoundException, TypeCheckerException {

        final String filename = "someFile.txt";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileNodeType = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype = "text/plain";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileNodeType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode);

        //only one file in the collection, so only one loop cycle

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockFile1).getName();
                will(returnValue(filename));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype);
                will(returnValue(fileNodeType));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, null,
                        uploadedFileURL, null, null, fileMimetype, fileNodeType, WorkspaceNodeStatus.UPLOADED,
                        Boolean.FALSE);
                will(returnValue(uploadedNode));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processOneUploadedResourceFile_IsInOrphansDirectory()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException {

        final String filename = "someFile.txt";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileNodeType = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype = "text/plain";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileNodeType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode);

        //only one file in the collection, so only one loop cycle

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockFile1).getName();
                will(returnValue(filename));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype);
                will(returnValue(fileNodeType));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.TRUE));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, uploadedFileURI,
                        uploadedFileURL, null, null, fileMimetype, fileNodeType, WorkspaceNodeStatus.UPLOADED,
                        Boolean.FALSE);
                will(returnValue(uploadedNode));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processOneUploadedMetadataFile_NoValidationIssues()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException, Exception {

        final String filename = "someFile.cmdi";
        final String documentName = "SomeFile";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final String uploadedFileRawHandle = UUID.randomUUID().toString();
        final URI uploadedFileArchiveURI = URI.create(handlePrefixWithSlash + uploadedFileRawHandle);
        final URI completeFileArchiveURI = URI.create(handleProxyPlusPrefixWithSlash + uploadedFileRawHandle);
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final URI schemaLocation = URI.create("http://some/location/schema.xsd");
        final WorkspaceNodeType fileNodeType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(documentName);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileNodeType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode);

        //only one file in the collection, so only one loop cycle

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                allowing(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);

                oneOf(mockWorkspaceFileValidator).triggerSchematronValidationForFile(workspaceID, mockFile1);

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype);
                will(returnValue(fileNodeType));

                oneOf(mockMetadataApiBridge).getSelfHandleFromDocument(mockMetadataDocument);
                will(returnValue(uploadedFileArchiveURI));
                oneOf(mockHandleParser).prepareAndValidateHandleWithHdlPrefix(uploadedFileArchiveURI);
                will(returnValue(completeFileArchiveURI));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockMetadataDocument).getDocumentType();
                will(returnValue(mockMetadataDocumentType));
                oneOf(mockMetadataDocumentType).getSchemaLocation();
                will(returnValue(schemaLocation));
                oneOf(mockMetadataApiBridge).getDocumentNameForProfile(mockMetadataDocument, schemaLocation);
                will(returnValue(documentName));
                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, completeFileArchiveURI,
                        null, uploadedFileURL, schemaLocation, documentName, fileMimetype, fileNodeType,
                        WorkspaceNodeStatus.UPLOADED, Boolean.FALSE);
                will(returnValue(uploadedNode));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processOneUploadedMetadataFile_NoValidationIssues_NoMappedName()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException, Exception {

        final String filename = "someFile.cmdi";
        final String documentName = "SomeFile";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final String uploadedFileRawHandle = UUID.randomUUID().toString();
        final URI uploadedFileArchiveURI = URI.create(handlePrefixWithSlash + uploadedFileRawHandle);
        final URI completeFileArchiveURI = URI.create(handleProxyPlusPrefixWithSlash + uploadedFileRawHandle);
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final URI schemaLocation = URI.create("http://some/location/schema.xsd");
        final WorkspaceNodeType fileNodeType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(documentName);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileNodeType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode);

        //only one file in the collection, so only one loop cycle

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                allowing(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);

                oneOf(mockWorkspaceFileValidator).triggerSchematronValidationForFile(workspaceID, mockFile1);

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype);
                will(returnValue(fileNodeType));

                oneOf(mockMetadataApiBridge).getSelfHandleFromDocument(mockMetadataDocument);
                will(returnValue(uploadedFileArchiveURI));
                oneOf(mockHandleParser).prepareAndValidateHandleWithHdlPrefix(uploadedFileArchiveURI);
                will(returnValue(completeFileArchiveURI));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockMetadataDocument).getDocumentType();
                will(returnValue(mockMetadataDocumentType));
                oneOf(mockMetadataDocumentType).getSchemaLocation();
                will(returnValue(schemaLocation));

                oneOf(mockMetadataApiBridge).getDocumentNameForProfile(mockMetadataDocument, schemaLocation);
                will(returnValue(null));
                oneOf(mockMetadataDocument).getDisplayValue();
                will(returnValue(documentName));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, completeFileArchiveURI,
                        null, uploadedFileURL, schemaLocation, documentName, fileMimetype, fileNodeType,
                        WorkspaceNodeStatus.UPLOADED, Boolean.FALSE);
                will(returnValue(uploadedNode));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processOneUploadedMetadataFile_NoValidationIssues_InvalidSelfHandle()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException, Exception {

        final String filename = "someFile.cmdi";
        final String documentName = "SomeFile";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final String uploadedFileRawHandle = UUID.randomUUID().toString();
        final URI uploadedFileArchiveURI = URI.create("INVALID/" + uploadedFileRawHandle);
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final URI schemaLocation = URI.create("http://some/location/schema.xsd");
        final WorkspaceNodeType fileNodeType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileNodeType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode);

        //only one file in the collection, so only one loop cycle

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                allowing(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);

                oneOf(mockWorkspaceFileValidator).triggerSchematronValidationForFile(workspaceID, mockFile1);

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype);
                will(returnValue(fileNodeType));

                oneOf(mockMetadataApiBridge).getSelfHandleFromDocument(mockMetadataDocument);
                will(returnValue(uploadedFileArchiveURI));
                oneOf(mockHandleParser).prepareAndValidateHandleWithHdlPrefix(uploadedFileArchiveURI);
                will(throwException(new IllegalArgumentException()));
                oneOf(mockMetadataApiBridge).removeSelfHandleAndSaveDocument(uploadedFileURL);

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockMetadataDocument).getDocumentType();
                will(returnValue(mockMetadataDocumentType));
                oneOf(mockMetadataDocumentType).getSchemaLocation();
                will(returnValue(schemaLocation));
                oneOf(mockMetadataApiBridge).getDocumentNameForProfile(mockMetadataDocument, schemaLocation);
                will(returnValue(documentName));
                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, null,
                        uploadedFileURL, schemaLocation, documentName, fileMimetype, fileNodeType,
                        WorkspaceNodeStatus.UPLOADED, Boolean.FALSE);
                will(returnValue(uploadedNode));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processOneUploadedMetadataFile_withOneValidationIssue_Error()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException, Exception {

        final String filename = "someFile.cmdi";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URI uploadedFileArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<MetadataValidationIssue> issues = new ArrayList<>();
        issues.add(mockValidationIssue1);

        final String assertionErrorMessage = "[CMDI Archive Restriction] the CMD profile of this record is not allowed in the archive.";
        final String validationIssuesString = "Validation issue for file '" + filename + "' - "
                + MetadataValidationIssueSeverity.ERROR.toString() + ": " + assertionErrorMessage + ".\n";

        final MetadataValidationException expectedException = new MetadataValidationException(
                validationIssuesString, workspaceID, null);
        expectedException.addValidationIssues(issues);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                allowing(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);

                oneOf(mockWorkspaceFileValidator).triggerSchematronValidationForFile(workspaceID, mockFile1);
                will(throwException(expectedException));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesToString(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(validationIssuesString));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesContainErrors(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).deleteFile(mockFile1);

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should have one element", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", validationIssuesString.trim(),
                ((FileImportProblem) problem).getErrorMessage().trim());
    }

    @Test
    public void processOneUploadedMetadataFile_withTwoValidationIssues_Error()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException, Exception {

        final String filename = "someFile.cmdi";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URI uploadedFileArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<MetadataValidationIssue> issues = new ArrayList<>();
        issues.add(mockValidationIssue1);
        issues.add(mockValidationIssue2);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        final String assertionErrorMessage1 = "[CMDI Archive Restriction] the CMD profile of this record is not allowed in the archive.";
        final String assertionErrorMessage2 = "[CMDI Archive Restriction] Something completely different went wrong.";
        final String validationIssuesString = "Validation issue for file '" + filename + "' - "
                + MetadataValidationIssueSeverity.ERROR.toString() + ": " + assertionErrorMessage1 + ".\n"
                + "Validation issue for file '" + filename + "' - "
                + MetadataValidationIssueSeverity.ERROR.toString() + ": " + assertionErrorMessage2 + ".\n";

        final MetadataValidationException expectedException = new MetadataValidationException(
                validationIssuesString, workspaceID, null);
        expectedException.addValidationIssues(issues);

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                allowing(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);

                oneOf(mockWorkspaceFileValidator).triggerSchematronValidationForFile(workspaceID, mockFile1);
                will(throwException(expectedException));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesToString(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(validationIssuesString));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesContainErrors(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).deleteFile(mockFile1);

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should have one element", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", validationIssuesString.trim(),
                ((FileImportProblem) problem).getErrorMessage().trim());
    }

    @Test
    public void processOneUploadedMetadataFile_withOneValidationIssue_Warning()
            throws URISyntaxException, MalformedURLException, WorkspaceNodeNotFoundException, NodeNotFoundException,
            TypeCheckerException, MetadataValidationException, WorkspaceException, IOException, MetadataException,
            CMDIValidatorInitException {

        final String filename = "someFile.cmdi";
        final String documentName = "SomeFile";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final String uploadedFileRawHandle = UUID.randomUUID().toString();
        final URI uploadedFileArchiveURI = URI.create(handlePrefixWithSlash + uploadedFileRawHandle);
        final URI completeFileArchiveURI = URI.create(handleProxyPlusPrefixWithSlash + uploadedFileRawHandle);
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final URI schemaLocation = URI.create("http://some/location/schema.xsd");
        final WorkspaceNodeType fileNodeType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileNodeType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<MetadataValidationIssue> issues = new ArrayList<>();
        issues.add(mockValidationIssue1);

        final String assertionErrorMessage = "[CMDI Best Practice] /cmd:CMD/cmd:Components/*/cmd:Title shouldn't be empty.";
        final String validationIssuesString = "Validation issue for file '" + filename + "' - "
                + MetadataValidationIssueSeverity.WARN.toString() + ": " + assertionErrorMessage + ".\n";

        final MetadataValidationException expectedException = new MetadataValidationException(
                validationIssuesString, workspaceID, null);
        expectedException.addValidationIssues(issues);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode);

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                allowing(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);

                oneOf(mockWorkspaceFileValidator).triggerSchematronValidationForFile(workspaceID, mockFile1);
                will(throwException(expectedException));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesToString(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(validationIssuesString));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesContainErrors(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(Boolean.FALSE));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype);
                will(returnValue(fileNodeType));

                oneOf(mockMetadataApiBridge).getSelfHandleFromDocument(mockMetadataDocument);
                will(returnValue(uploadedFileArchiveURI));
                oneOf(mockHandleParser).prepareAndValidateHandleWithHdlPrefix(uploadedFileArchiveURI);
                will(returnValue(completeFileArchiveURI));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockMetadataDocument).getDocumentType();
                will(returnValue(mockMetadataDocumentType));
                oneOf(mockMetadataDocumentType).getSchemaLocation();
                will(returnValue(schemaLocation));
                oneOf(mockMetadataApiBridge).getDocumentNameForProfile(mockMetadataDocument, schemaLocation);
                will(returnValue(documentName));
                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, completeFileArchiveURI,
                        null, uploadedFileURL, schemaLocation, documentName, fileMimetype, fileNodeType,
                        WorkspaceNodeStatus.UPLOADED, Boolean.FALSE);
                will(returnValue(uploadedNode));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode);

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processOneUploadedMetadataFile_MetadataFileNotValid()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException, Exception {

        final String filename = "someFile.cmdi";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URI uploadedFileArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<MetadataValidationIssue> issues = new ArrayList<>();
        issues.add(mockValidationIssue1);

        final String validationIssuesString = "Metadata file [" + filename + "] is invalid";

        final MetadataValidationException expectedException = new MetadataValidationException(
                validationIssuesString, workspaceID, null);
        expectedException.addValidationIssues(issues);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        final String expectedErrorMessage = "Metadata file [" + filename + "] is invalid";

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockFile1).getName();
                will(returnValue(filename));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);
                will(throwException(expectedException));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesToString(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(validationIssuesString));
                oneOf(mockWorkspaceFileValidator)
                        .validationIssuesContainErrors(with(equivalentValidationIssueCollection(issues)));
                will(returnValue(Boolean.TRUE));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).deleteFile(mockFile1);

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", expectedErrorMessage,
                ((FileImportProblem) problem).getErrorMessage());
    }

    @Test
    public void processOneUploadedMetadataFile_ValidatorException() throws MalformedURLException,
            WorkspaceNodeNotFoundException, NodeNotFoundException, TypeCheckerException, IOException,
            MetadataException, CMDIValidatorInitException, MetadataValidationException, WorkspaceException {

        final String filename = "someFile.cmdi";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URI uploadedFileArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.METADATA;
        final String fileMimetype = "text/x-cmdi-xml";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);
        uploadedNode.setArchiveURI(uploadedFileArchiveURI);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        final String expectedErrorMessage = "Problems with the metadata validation when processing [" + filename
                + "]";

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockFile1).getName();
                will(returnValue(filename));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockMetadataAPI).getMetadataDocument(uploadedFileURL);
                will(returnValue(mockMetadataDocument));

                oneOf(mockWorkspaceFileValidator).triggerSchemaValidationForFile(workspaceID, mockFile1);
                will(throwException(mockCMDIValidatorInitException));
                ignoring(mockCMDIValidatorInitException);
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).deleteFile(mockFile1);

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", expectedErrorMessage,
                ((FileImportProblem) problem).getErrorMessage());
    }

    @Test
    public void processTwoUploadedFiles() throws IOException, WorkspaceNodeNotFoundException, URISyntaxException,
            WorkspaceException, NodeNotFoundException, TypeCheckerException {

        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final String filename1 = "someFile.txt";
        final File uploadedFile1 = new File(workspaceUploadDirectory, filename1);
        final URI uploadedFileURI1 = uploadedFile1.toURI();
        final URL uploadedFileURL1 = uploadedFileURI1.toURL();
        final WorkspaceNodeType fileNodeType1 = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype1 = "text/plain";

        final String filename2 = "someOtherFile.jpg";
        final File uploadedFile2 = new File(workspaceUploadDirectory, filename2);
        final URI uploadedFileURI2 = uploadedFile2.toURI();
        final URL uploadedFileURL2 = uploadedFileURI2.toURL();
        final WorkspaceNodeType fileNodeType2 = WorkspaceNodeType.RESOURCE_IMAGE;
        final String fileMimetype2 = "image/jpeg";

        final WorkspaceNode uploadedNode1 = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode1.setName(filename1);
        uploadedNode1.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode1.setType(fileNodeType1);
        uploadedNode1.setFormat(fileMimetype1);
        uploadedNode1.setWorkspaceURL(uploadedFileURL1);

        final WorkspaceNode uploadedNode2 = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode2.setName(filename2);
        uploadedNode2.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode2.setType(fileNodeType2);
        uploadedNode2.setFormat(fileMimetype2);
        uploadedNode2.setWorkspaceURL(uploadedFileURL2);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);
        uploadedFiles.add(mockFile2);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode1);
        uploadedNodes.add(uploadedNode2);

        //two files in the collection, so two loop cycles

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //first loop cycle

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI1));
                oneOf(mockFile1).getName();
                will(returnValue(filename1));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL1, filename1);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile1).getName();
                will(returnValue(filename1));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype1));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype1);
                will(returnValue(fileNodeType1));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, null,
                        uploadedFileURL1, null, null, fileMimetype1, fileNodeType1, WorkspaceNodeStatus.UPLOADED,
                        Boolean.FALSE);
                will(returnValue(uploadedNode1));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode1);

                //second loop cycle

                oneOf(mockFile2).toURI();
                will(returnValue(uploadedFileURI2));
                oneOf(mockFile2).getName();
                will(returnValue(filename2));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL2, filename2);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile2).getName();
                will(returnValue(filename2));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype2));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype2);
                will(returnValue(fileNodeType2));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile2);
                will(returnValue(Boolean.FALSE));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, null,
                        uploadedFileURL2, null, null, fileMimetype2, fileNodeType2, WorkspaceNodeStatus.UPLOADED,
                        Boolean.FALSE);
                will(returnValue(uploadedNode2));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode2);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.isEmpty());
    }

    @Test
    public void processTwoUploadedFiles_LinkingFailed() throws IOException, WorkspaceNodeNotFoundException,
            URISyntaxException, WorkspaceException, NodeNotFoundException, TypeCheckerException {

        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final String filename1 = "someFile.txt";
        final File uploadedFile1 = new File(workspaceUploadDirectory, filename1);
        final URI uploadedFileURI1 = uploadedFile1.toURI();
        final URL uploadedFileURL1 = uploadedFileURI1.toURL();
        final WorkspaceNodeType fileNodeType1 = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype1 = "text/plain";

        final String filename2 = "someOtherFile.jpg";
        final File uploadedFile2 = new File(workspaceUploadDirectory, filename2);
        final URI uploadedFileURI2 = uploadedFile2.toURI();
        final URL uploadedFileURL2 = uploadedFileURI2.toURL();
        final WorkspaceNodeType fileNodeType2 = WorkspaceNodeType.RESOURCE_IMAGE;
        final String fileMimetype2 = "image/jpeg";

        final WorkspaceNode uploadedNode1 = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode1.setName(filename1);
        uploadedNode1.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode1.setType(fileNodeType1);
        uploadedNode1.setFormat(fileMimetype1);
        uploadedNode1.setWorkspaceURL(uploadedFileURL1);

        final WorkspaceNode uploadedNode2 = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode2.setName(filename2);
        uploadedNode2.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode2.setType(fileNodeType2);
        uploadedNode2.setFormat(fileMimetype2);
        uploadedNode2.setWorkspaceURL(uploadedFileURL2);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);
        uploadedFiles.add(mockFile2);

        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();
        uploadedNodes.add(uploadedNode1);
        uploadedNodes.add(uploadedNode2);

        //two files in the collection, so two loop cycles

        final Collection<ImportProblem> failedLinks = new ArrayList<>();
        failedLinks.add(mockUploadProblem);

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //first loop cycle

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI1));
                oneOf(mockFile1).getName();
                will(returnValue(filename1));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL1, filename1);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile1).getName();
                will(returnValue(filename1));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype1));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype1);
                will(returnValue(fileNodeType1));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, null,
                        uploadedFileURL1, null, null, fileMimetype1, fileNodeType1, WorkspaceNodeStatus.UPLOADED,
                        Boolean.FALSE);
                will(returnValue(uploadedNode1));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode1);

                //second loop cycle

                oneOf(mockFile2).toURI();
                will(returnValue(uploadedFileURI2));
                oneOf(mockFile2).getName();
                will(returnValue(filename2));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL2, filename2);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.TRUE));
                oneOf(mockFile2).getName();
                will(returnValue(filename2));

                oneOf(mockTypecheckedResults).getCheckedMimetype();
                will(returnValue(fileMimetype2));
                oneOf(mockNodeUtil).convertMimetype(fileMimetype2);
                will(returnValue(fileNodeType2));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile2);
                will(returnValue(Boolean.FALSE));

                oneOf(mockWorkspaceNodeFactory).getNewWorkspaceNodeFromFile(workspaceID, null, null,
                        uploadedFileURL2, null, null, fileMimetype2, fileNodeType2, WorkspaceNodeStatus.UPLOADED,
                        Boolean.FALSE);
                will(returnValue(uploadedNode2));

                oneOf(mockWorkspaceDao).addWorkspaceNode(uploadedNode2);

                //check links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertFalse("Collection with failed uploads should not be empty", result.isEmpty());
        assertTrue("Collection with failed uploads different from expected", result.containsAll(failedLinks));
    }

    @Test
    public void processUploadedFileWorkspaceException() throws IOException, WorkspaceNodeNotFoundException,
            URISyntaxException, WorkspaceException, NodeNotFoundException, TypeCheckerException {

        final String filename = "someFile.txt";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype = "text/plain";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);

        Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        final String expectedErrorMessage = "Error retrieving archive URL from the top node of workspace "
                + workspaceID;
        final NodeNotFoundException expectedException = new NodeNotFoundException(workspaceTopNodeArchiveURI,
                "some exception message");

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(throwException(expectedException));
            }
        });

        try {
            uploader.processUploadedFiles(workspaceID, uploadedFiles);
            fail("should have thrown exception");
        } catch (WorkspaceException ex) {
            assertEquals("Message different from expected", expectedErrorMessage, ex.getMessage());
            assertEquals("Workspace ID different from expected", workspaceID, ex.getWorkspaceID());
            assertEquals("Cause different from expected", expectedException, ex.getCause());
        }
    }

    @Test
    public void processUploadedFileUrlException() throws IOException, WorkspaceNodeNotFoundException,
            URISyntaxException, WorkspaceException, NodeNotFoundException, TypeCheckerException {

        final String filename = "someFile.txt";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final WorkspaceNodeType fileType = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype = "text/plain";
        final String uploadedFilePath = uploadedFile.getPath();
        final URI uriWhichIsNotUrl = URI.create("node:0");

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        //no successful uploads
        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        final String expectedErrorMessage = "Error retrieving URL from file " + uploadedFile.getPath();

        //only one file in the collection, so only one loop cycle

        final Collection<ImportProblem> failedLinks = new ArrayList<>();

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uriWhichIsNotUrl));

                oneOf(mockFile1).getPath();
                will(returnValue(uploadedFilePath));

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
                will(returnValue(failedLinks));
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", expectedErrorMessage,
                ((FileImportProblem) problem).getErrorMessage());
    }

    @Test
    public void processUploadedFileUnarchivable() throws IOException, WorkspaceNodeNotFoundException,
            URISyntaxException, WorkspaceException, NodeNotFoundException, TypeCheckerException {

        final String filename = "someFile.txt";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype = "text/plain";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        //no successful uploads
        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        String partExpectedErrorMessage = "File [" + filename + "] not archivable: ";

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockFile1).getName();
                will(returnValue(filename));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.FALSE));
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.FALSE));
                oneOf(mockWorkspaceFileHandler).deleteFile(mockFile1);

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", partExpectedErrorMessage,
                ((FileImportProblem) problem).getErrorMessage());
    }

    @Test
    public void processUploadedFileUnarchivable_IsInOrphansDirectory()
            throws IOException, WorkspaceNodeNotFoundException, URISyntaxException, WorkspaceException,
            NodeNotFoundException, TypeCheckerException {

        final String filename = "someFile.txt";
        final URI workspaceTopNodeArchiveURI = URI
                .create(handleProxyPlusPrefixWithSlash + UUID.randomUUID().toString());
        final File workspaceTopNodeArchiveFile = new File("/archive/some/node.cmdi");
        final File uploadedFile = new File(workspaceUploadDirectory, filename);
        final URI uploadedFileURI = uploadedFile.toURI();
        final URL uploadedFileURL = uploadedFileURI.toURL();
        final WorkspaceNodeType fileType = WorkspaceNodeType.RESOURCE_WRITTEN;
        final String fileMimetype = "text/plain";

        final WorkspaceNode uploadedNode = new LamusWorkspaceNode(workspaceID, null, null);
        uploadedNode.setName(filename);
        uploadedNode.setStatus(WorkspaceNodeStatus.UPLOADED);
        uploadedNode.setType(fileType);
        uploadedNode.setFormat(fileMimetype);
        uploadedNode.setWorkspaceURL(uploadedFileURL);

        final Collection<File> uploadedFiles = new ArrayList<>();
        uploadedFiles.add(mockFile1);

        //no successful uploads
        final Collection<WorkspaceNode> uploadedNodes = new ArrayList<>();

        String partExpectedErrorMessage = "File [" + filename + "] not archivable: ";

        //only one file in the collection, so only one loop cycle

        context.checking(new Expectations() {
            {

                oneOf(mockWorkspaceDao).getWorkspace(workspaceID);
                will(returnValue(mockWorkspace));
                oneOf(mockWorkspaceDao).getWorkspaceTopNode(workspaceID);
                will(returnValue(mockWorkspaceTopNode));
                oneOf(mockWorkspaceTopNode).getArchiveURI();
                will(returnValue(workspaceTopNodeArchiveURI));
                oneOf(mockNodeDataRetriever).getNodeLocalFile(workspaceTopNodeArchiveURI);
                will(returnValue(workspaceTopNodeArchiveFile));

                //loop

                oneOf(mockFile1).toURI();
                will(returnValue(uploadedFileURI));
                oneOf(mockFile1).getName();
                will(returnValue(filename));
                oneOf(mockNodeDataRetriever).triggerResourceFileCheck(uploadedFileURL, filename);
                will(returnValue(mockTypecheckedResults));

                oneOf(mockNodeDataRetriever).isCheckedResourceArchivable(with(same(mockTypecheckedResults)),
                        with(same(workspaceTopNodeArchiveFile)), with(any(StringBuilder.class)));
                will(returnValue(Boolean.FALSE));
                oneOf(mockFile1).getName();
                will(returnValue(filename));

                oneOf(mockArchiveFileLocationProvider).isFileInOrphansDirectory(mockFile1);
                will(returnValue(Boolean.TRUE));

                //still calls method to process links
                oneOf(mockWorkspaceUploadHelper).assureLinksInWorkspace(mockWorkspace, uploadedNodes);
            }
        });

        Collection<ImportProblem> result = uploader.processUploadedFiles(workspaceID, uploadedFiles);

        assertNotNull("Collection with failed uploads should not be null", result);
        assertTrue("Collection with failed uploads should be empty", result.size() == 1);

        ImportProblem problem = result.iterator().next();

        assertTrue("Upload problem different from expected", problem instanceof FileImportProblem);
        assertEquals("File added to the upload problem is different from expected", mockFile1,
                ((FileImportProblem) problem).getProblematicFile());
        assertEquals("Reason for failure of file upload is different from expected", partExpectedErrorMessage,
                ((FileImportProblem) problem).getErrorMessage());
    }

    public class CreateDirectoryOnInvokeAction implements Action {
        private File result;

        public CreateDirectoryOnInvokeAction(File result) {
            this.result = result;
        }

        public Object invoke(Invocation invocation) throws Throwable {
            result.mkdirs();
            return result;
        }

        public void describeTo(Description description) {
            description.appendText("returns ");
            description.appendValue(result);
        }
    }
}