org.codice.ddf.configuration.migration.ImportMigrationEntryImplTest.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.configuration.migration.ImportMigrationEntryImplTest.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>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 Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.configuration.migration;

import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.base.Charsets;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.Function;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.codice.ddf.migration.ImportMigrationEntry;
import org.codice.ddf.migration.MigrationException;
import org.codice.ddf.migration.MigrationOperation;
import org.codice.ddf.migration.MigrationReport;
import org.codice.ddf.util.function.BiThrowingConsumer;
import org.codice.ddf.util.function.ThrowingRunnable;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class ImportMigrationEntryImplTest extends AbstractMigrationSupport {

    private static final long MODIFIED_TIME = 13243546L;

    public static final String IMPORT_CONTENTS = "import contents";

    public static final String ENTRY_NAME = "test_name";

    public Path absolutePath;

    @Mock
    public ImportMigrationContextImpl mockContext;

    @Mock
    public ImportMigrationContextImpl mockBlankContext;

    @Mock
    public PathUtils mockPathUtils;

    public Function<String, ImportMigrationContextImpl> getContextFunction = (n) -> n == null ? mockBlankContext
            : mockContext;

    public MigrationReport report;

    public File importedFile;

    @Before
    public void setup() throws Exception {
        PathUtils pathUtils = new PathUtils();
        when(mockContext.getPathUtils()).thenReturn(pathUtils);
        when(mockBlankContext.getPathUtils()).thenReturn(pathUtils);

        givenARealMigrationReport();

        importedFile = ddfHome.resolve(createFile(ENTRY_NAME)).toFile();
        // resolving against DDF_HOME ensures that on Windows the absolute path gets the same drive as
        // DDF_HOME
        absolutePath = ddfHome.resolve(Paths.get("/opt", "ddf", ENTRY_NAME));
    }

    @Test
    public void constructorWithZipEntry() throws Exception {
        givenMockedPathUtils();
        final java.util.zip.ZipEntry zipEntry = new java.util.zip.ZipEntry("migratable/" + ENTRY_NAME);

        zipEntry.setTime(MODIFIED_TIME);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(getContextFunction, zipEntry);

        assertThat(entry.getContext(), sameInstance(mockContext));
        verifyNameAndPathInformation(entry, mockContext);
        assertThat(entry.isFile(), equalTo(true));
        assertThat(entry.isDirectory(), equalTo(false));
        assertThat(entry.getLastModifiedTime(), equalTo(MODIFIED_TIME));
    }

    @Test
    public void constructorWithZipEntryForSystemEntry() throws Exception {
        givenMockedPathUtils();
        final java.util.zip.ZipEntry zipEntry = new java.util.zip.ZipEntry(ENTRY_NAME);

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(getContextFunction, zipEntry);

        assertThat(entry.getContext(), sameInstance(mockBlankContext));
        verifyNameAndPathInformation(entry, mockBlankContext);
        assertThat(entry.isFile(), equalTo(true));
        assertThat(entry.isDirectory(), equalTo(false));
    }

    @Test
    public void constructorWithNameWhenAFile() throws Exception {
        givenMockedPathUtils();

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        assertThat(entry.getContext(), sameInstance(mockContext));
        verifyNameAndPathInformation(entry, mockContext);
        assertThat(entry.isFile(), equalTo(true));
        assertThat(entry.isDirectory(), equalTo(false));
        assertThat(entry.getLastModifiedTime(), equalTo(-1L));
    }

    @Test
    public void constructorWithNameWhenADirectory() throws Exception {
        givenMockedPathUtils();

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, false);

        assertThat(entry.getContext(), sameInstance(mockContext));
        verifyNameAndPathInformation(entry, mockContext);
        assertThat(entry.isFile(), equalTo(false));
        assertThat(entry.isDirectory(), equalTo(true));
        assertThat(entry.getLastModifiedTime(), equalTo(-1L));
    }

    @Test
    public void constructorWithPathWhenAFile() throws Exception {
        givenMockedPathUtils();

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, Paths.get(ENTRY_NAME),
                true);

        assertThat(entry.getContext(), sameInstance(mockContext));
        verifyNameAndPathInformation(entry, mockContext);
        assertThat(entry.isFile(), equalTo(true));
        assertThat(entry.isDirectory(), equalTo(false));
        assertThat(entry.getLastModifiedTime(), equalTo(-1L));
    }

    @Test
    public void constructorWithPathWhenADirectory() throws Exception {
        givenMockedPathUtils();

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, Paths.get(ENTRY_NAME),
                false);

        assertThat(entry.getContext(), sameInstance(mockContext));
        verifyNameAndPathInformation(entry, mockContext);
        assertThat(entry.isFile(), equalTo(false));
        assertThat(entry.isDirectory(), equalTo(true));
        assertThat(entry.getLastModifiedTime(), equalTo(-1L));
    }

    @Test
    public void getInputStream() throws Exception {
        final java.util.zip.ZipEntry zipEntry = new java.util.zip.ZipEntry("migratable/" + ENTRY_NAME);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(getContextFunction, zipEntry);
        InputStream mockInputStream = mock(InputStream.class);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(mockInputStream);

        Optional<InputStream> optionalInput = entry.getInputStream();

        assertThat(optionalInput, isPresent());
        assertThat(optionalInput.get(), sameInstance(mockInputStream));
    }

    @Test
    public void getLastModifiedTime() {
        givenMockedPathUtils();
        final java.util.zip.ZipEntry mockZipEntry = mock(java.util.zip.ZipEntry.class);
        when(mockZipEntry.getTime()).thenReturn(10L);
        when(mockZipEntry.getName()).thenReturn(ENTRY_NAME);

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(getContextFunction, mockZipEntry);

        assertThat(entry.getLastModifiedTime(), equalTo(10L));
    }

    @Test
    public void restoreWhenRequired() throws Exception {
        InputStream inputStream = IOUtils.toInputStream(IMPORT_CONTENTS, Charsets.UTF_8);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(inputStream);

        assertThat(entry.restore(true), equalTo(true));

        assertThat(FileUtils.readFileToString(importedFile, Charsets.UTF_8), equalTo(IMPORT_CONTENTS));
    }

    @Test
    public void restoreWhenOptional() throws Exception {
        InputStream inputStream = IOUtils.toInputStream(IMPORT_CONTENTS, Charsets.UTF_8);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(inputStream);

        assertThat(entry.restore(false), equalTo(true));

        assertThat(FileUtils.readFileToString(importedFile, Charsets.UTF_8), equalTo(IMPORT_CONTENTS));
    }

    @Test
    public void restoreWithFilterWhenRequiredAndMatching() throws Exception {
        InputStream inputStream = IOUtils.toInputStream(IMPORT_CONTENTS, Charsets.UTF_8);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(inputStream);

        assertThat(entry.restore(true, p -> true), equalTo(true));

        assertThat(FileUtils.readFileToString(importedFile, Charsets.UTF_8), equalTo(IMPORT_CONTENTS));
    }

    @Test
    public void restoreWithFilterWhenOptionalAndMatching() throws Exception {
        InputStream inputStream = IOUtils.toInputStream(IMPORT_CONTENTS, Charsets.UTF_8);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(inputStream);

        assertThat(entry.restore(false, p -> true), equalTo(true));

        assertThat(FileUtils.readFileToString(importedFile, Charsets.UTF_8), equalTo(IMPORT_CONTENTS));
    }

    @Test
    public void restoreWithFilterWhenRequiredAndNotMatching() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        importedFile.delete();

        assertThat(entry.restore(true, p -> false), equalTo(false));

        assertThat(importedFile.exists(), equalTo(false));

        verify(mockContext, Mockito.never()).getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean());
    }

    @Test
    public void restoreWithFilterWhenOptionalAndNotMatching() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        importedFile.delete();

        assertThat(entry.restore(false, p -> false), equalTo(true));

        assertThat(importedFile.exists(), equalTo(false));

        verify(mockContext, Mockito.never()).getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean());
    }

    @Test
    public void restoreWhenRequiredEntryWasNotImported() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(null);

        assertThat(entry.restore(true), equalTo(false));

        verifyReportHasMatchingError(report, "was not exported");
    }

    @Test
    public void restoreWithFilterWhenRequiredEntryWasNotImportedAndMatching() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(null);

        assertThat(entry.restore(true, p -> true), equalTo(false));

        verifyReportHasMatchingError(report, "was not exported");
    }

    @Test
    public void restoreWithFilterWhenRequiredEntryWasNotImportedAndNotMatching() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        importedFile.delete();

        assertThat(entry.restore(true, p -> false), equalTo(false));

        assertThat(importedFile.exists(), equalTo(false));

        verify(mockContext, Mockito.never()).getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean());
    }

    @Test
    public void restoreWhenOptionalEntryWasNotImported() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(null);

        assertThat(entry.restore(false), equalTo(true));

        assertThat("The file has not been deleted.", importedFile.exists(), is(false));
    }

    @Test
    public void restoreWithFilterWhenOptionalEntryWasNotImportedAndMatching() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(null);

        assertThat(entry.restore(false, p -> true), equalTo(true));

        assertThat("The file has not been deleted.", importedFile.exists(), is(false));
    }

    @Test
    public void restoreWithFilterWhenOptionalEntryWasNotImportedAndNotMatching() throws Exception {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        assertThat(entry.restore(false, p -> false), equalTo(true));

        assertThat("The file has been deleted.", importedFile.exists(), is(true));
    }

    @Test
    public void restoreWhenFileIsReadOnly() throws Exception {
        final Path importedPath = Paths.get(importedFile.getName());
        final PathUtils pathUtils = mock(PathUtils.class);
        final Path absolutePath = mock(Path.class);
        final File absoluteFile = spy(new File(importedFile.getPath()));
        InputStream inputStream = IOUtils.toInputStream(IMPORT_CONTENTS, Charsets.UTF_8);

        when(mockContext.getPathUtils()).thenReturn(pathUtils);
        when(pathUtils.resolveAgainstDDFHome(Mockito.eq(importedPath))).thenReturn(absolutePath);
        when(absolutePath.toFile()).thenReturn(absoluteFile);

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(inputStream);
        when(absoluteFile.canWrite()).thenReturn(false, false, true);
        // must reverse mockito calls otherwise using when() will actually call the setWritable() on the
        // spy and affect the file system
        doReturn(true).when(absoluteFile).setWritable(true);
        doReturn(true).when(absoluteFile).setWritable(false);

        entry.restore(true);

        final InOrder inOrder = inOrder(absoluteFile);

        inOrder.verify(absoluteFile).setWritable(true);
        inOrder.verify(absoluteFile).setWritable(false);

        assertThat(FileUtils.readFileToString(importedFile, Charsets.UTF_8), equalTo(IMPORT_CONTENTS));
    }

    @Test
    public void restoreWhenFileIsReadOnlyAndCannotMakeItWriteable() throws Exception {
        final Path importedPath = Paths.get(importedFile.getName());
        final PathUtils pathUtils = mock(PathUtils.class);
        final Path absolutePath = mock(Path.class);
        final File absoluteFile = spy(new File(importedFile.getPath()));
        InputStream inputStream = IOUtils.toInputStream(IMPORT_CONTENTS, Charsets.UTF_8);
        MigrationReport mockReport = mock(MigrationReport.class);
        when(mockReport.wasIOSuccessful(any(ThrowingRunnable.class))).thenAnswer(it -> {
            ((ThrowingRunnable) it.getArgument(0)).run();
            throw new IOException();
        });
        when(mockContext.getReport()).thenReturn(mockReport);

        when(mockContext.getPathUtils()).thenReturn(pathUtils);
        when(pathUtils.resolveAgainstDDFHome(Mockito.eq(importedPath))).thenReturn(absolutePath);
        when(absolutePath.toFile()).thenReturn(absoluteFile);

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, importedFile.getName(),
                true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean())).thenReturn(inputStream);
        when(absoluteFile.canWrite()).thenReturn(false, false, true);
        // must reverse mockito calls otherwise using when() will actually call the setWritable() on the
        // spy and affect the file system
        doReturn(false).when(absoluteFile).setWritable(true);

        entry.restore(true);

        final InOrder inOrder = inOrder(absoluteFile);

        inOrder.verify(absoluteFile).setWritable(true);

        assertThat("The restored flag is set to false", entry.restored, is(false));
        verify(mockReport).record(any(MigrationException.class));
    }

    @Test
    public void testRestoreWithFilterWhenFilterIsNull() throws Exception {
        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage(Matchers.containsString("null path filter"));

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        entry.restore(true, null);
    }

    @Test
    public void testRestoreWithFilterVerifyingFilterReceivingEntryPath() throws Exception {
        final PathMatcher filter = mock(PathMatcher.class);

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        when(filter.matches(entry.getPath())).thenReturn(false);

        entry.restore(false, filter);

        verify(filter).matches(entry.getPath());
    }

    @Test
    public void restoreWithConsumer() throws Exception {
        MigrationReport mockReport = mock(MigrationReport.class);
        when(mockReport.wasIOSuccessful(any(ThrowingRunnable.class))).thenAnswer(it -> {
            ((ThrowingRunnable) it.getArgument(0)).run();
            return true;
        });
        when(mockContext.getReport()).thenReturn(mockReport);
        BiThrowingConsumer<MigrationReport, Optional<InputStream>, IOException> mockConsumer = mock(
                BiThrowingConsumer.class);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean()))
                .thenReturn(mock(InputStream.class));

        entry.restore(mockConsumer);

        assertThat("The restored flag is set to true", entry.restored, is(true));
        verify(mockConsumer).accept(any(MigrationReport.class), any(Optional.class));
    }

    @Test
    public void restoreWithConsumerHandlesIOException() throws Exception {
        MigrationReport mockReport = mock(MigrationReport.class);
        when(mockReport.wasIOSuccessful(any(ThrowingRunnable.class))).thenAnswer(it -> {
            ((ThrowingRunnable) it.getArgument(0)).run();
            throw new IOException();
        });
        when(mockContext.getReport()).thenReturn(mockReport);
        BiThrowingConsumer<MigrationReport, Optional<InputStream>, IOException> mockConsumer = mock(
                BiThrowingConsumer.class);
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);

        when(mockContext.getInputStreamFor(Mockito.same(entry), Mockito.anyBoolean()))
                .thenReturn(mock(InputStream.class));

        entry.restore(mockConsumer);

        assertThat("The restored flag is set to false", entry.restored, is(false));
        verify(mockConsumer).accept(any(MigrationReport.class), any(Optional.class));
        verify(mockReport).record(any(MigrationException.class));
    }

    @Test
    public void getPropertyReferencedEntry() {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);
        ImportMigrationJavaPropertyReferencedEntryImpl mockRefEntry = mock(
                ImportMigrationJavaPropertyReferencedEntryImpl.class);
        entry.addPropertyReferenceEntry(ENTRY_NAME, mockRefEntry);

        final Optional<ImportMigrationEntry> optionalEntry = entry.getPropertyReferencedEntry(ENTRY_NAME);
        assertThat(optionalEntry, isPresent());
        assertThat(optionalEntry.get(), sameInstance(mockRefEntry));
    }

    @Test
    public void isMigratable() {
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, ENTRY_NAME, true);
        assertThat("The entry is migratable.", entry.isMigratable(), is(true));
    }

    @Test
    public void isNotMigratableWithAbsolutePath() {
        // resolving against DDF_HOME ensures that the absolute path gets the same drive on windows
        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext,
                ddfHome.resolve(Paths.get(File.separatorChar + ENTRY_NAME)).toString(), true);

        assertThat("The entry is not migratable.", entry.isMigratable(), is(false));
        reportHasWarningWithMessage(entry.getReport(), "is outside");
    }

    @Test
    public void isNotMigratableWithSymbolicLink() throws Exception {
        Path symlink = createSoftLink("symbolic-link", Paths.get("path1/"));

        final ImportMigrationEntryImpl entry = new ImportMigrationEntryImpl(mockContext, symlink, true);

        assertThat("The entry is not migratable.", entry.isMigratable(), is(false));
        reportHasWarningWithMessage(entry.getReport(), "symbolic link");
    }

    private void givenMockedPathUtils() {
        when(mockContext.getPathUtils()).thenReturn(mockPathUtils);
        when(mockBlankContext.getPathUtils()).thenReturn(mockPathUtils);
        when(mockPathUtils.resolveAgainstDDFHome(any(Path.class))).thenReturn(absolutePath);
    }

    private void givenARealMigrationReport() {
        report = new MigrationReportImpl(MigrationOperation.IMPORT, Optional.empty());
        when(mockContext.getReport()).thenReturn(report);
    }

    private void verifyNameAndPathInformation(ImportMigrationEntryImpl entry,
            ImportMigrationContextImpl expectedContext) throws IOException {
        assertThat(entry.getName(), equalTo(ENTRY_NAME));
        assertThat(entry.getPath().toString(), equalTo(ENTRY_NAME));
        assertThat(entry.getAbsolutePath(), sameInstance(absolutePath));
        assertThat(entry.getFile().getAbsolutePath(), equalTo(absolutePath.toString()));
        verify(expectedContext).getPathUtils();
        verify(mockPathUtils).resolveAgainstDDFHome(any(Path.class));
    }

    private void reportHasWarningWithMessage(MigrationReport report, String message) {
        report.warnings().filter((w) -> w.getMessage().contains(message)).findFirst()
                .orElseThrow(() -> new AssertionError("There is no matching warning in the migration report"));
    }
}