org.commonjava.indy.promote.data.PromotionManagerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.indy.promote.data.PromotionManagerTest.java

Source

/**
 * Copyright (C) 2011-2018 Red Hat, Inc. (https://github.com/Commonjava/indy)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.commonjava.indy.promote.data;

import org.apache.commons.io.IOUtils;
import org.commonjava.cdi.util.weft.Locker;
import org.commonjava.cdi.util.weft.PoolWeftExecutorService;
import org.commonjava.cdi.util.weft.WeftExecutorService;
import org.commonjava.indy.IndyWorkflowException;
import org.commonjava.indy.audit.ChangeSummary;
import org.commonjava.indy.conf.DefaultIndyConfiguration;
import org.commonjava.indy.content.ContentDigester;
import org.commonjava.indy.content.ContentGenerator;
import org.commonjava.indy.content.ContentManager;
import org.commonjava.indy.content.DirectContentAccess;
import org.commonjava.indy.content.DownloadManager;
import org.commonjava.indy.content.IndyLocationExpander;
import org.commonjava.indy.core.content.DefaultContentDigester;
import org.commonjava.indy.core.content.DefaultContentManager;
import org.commonjava.indy.core.content.DefaultDirectContentAccess;
import org.commonjava.indy.core.content.DefaultDownloadManager;
import org.commonjava.indy.core.inject.ExpiringMemoryNotFoundCache;
import org.commonjava.indy.data.IndyDataException;
import org.commonjava.indy.data.StoreDataManager;
import org.commonjava.indy.mem.data.MemoryStoreDataManager;
import org.commonjava.indy.model.core.HostedRepository;
import org.commonjava.indy.model.core.StoreKey;
import org.commonjava.indy.model.core.io.IndyObjectMapper;
import org.commonjava.indy.promote.conf.PromoteConfig;
import org.commonjava.indy.promote.model.PathsPromoteRequest;
import org.commonjava.indy.promote.model.PathsPromoteResult;
import org.commonjava.indy.promote.validate.PromoteValidationsManager;
import org.commonjava.indy.promote.validate.PromotionValidationTools;
import org.commonjava.indy.promote.validate.PromotionValidator;
import org.commonjava.indy.promote.validate.ValidationRuleParser;
import org.commonjava.indy.subsys.datafile.DataFileManager;
import org.commonjava.indy.subsys.datafile.change.DataFileEventManager;
import org.commonjava.indy.subsys.infinispan.CacheHandle;
import org.commonjava.indy.subsys.template.ScriptEngine;
import org.commonjava.indy.test.fixture.core.MockContentAdvisor;
import org.commonjava.indy.test.fixture.core.MockInstance;
import org.commonjava.maven.galley.event.EventMetadata;
import org.commonjava.maven.galley.io.SpecialPathManagerImpl;
import org.commonjava.maven.galley.io.checksum.TransferMetadata;
import org.commonjava.maven.galley.maven.rel.MavenModelProcessor;
import org.commonjava.maven.galley.model.Transfer;
import org.commonjava.maven.galley.model.TransferOperation;
import org.commonjava.maven.galley.nfc.MemoryNotFoundCache;
import org.commonjava.maven.galley.testing.maven.GalleyMavenFixture;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

import static org.commonjava.indy.pkg.maven.model.MavenPackageTypeDescriptor.MAVEN_PKG_KEY;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

public class PromotionManagerTest {

    @Rule
    public TemporaryFolder temp = new TemporaryFolder();

    private ContentManager contentManager;

    private DownloadManager downloadManager;

    private StoreDataManager storeManager;

    private GalleyMavenFixture galleyParts;

    private PromotionManager manager;

    private DataFileManager dataManager;

    private PromoteValidationsManager validationsManager;

    private PromotionValidator validator;

    private Executor executor;

    private static final String FAKE_BASE_URL = "";

    private static DefaultCacheManager cacheManager;

    private static Cache<String, TransferMetadata> contentMetadata;

    @BeforeClass
    public static void setupClass() {
        cacheManager = new DefaultCacheManager(new ConfigurationBuilder().simpleCache(true).build());

        contentMetadata = cacheManager.getCache("content-metadata", true);

    }

    @Before
    public void setup() throws Exception {
        contentMetadata.clear();

        galleyParts = new GalleyMavenFixture(true, temp);
        galleyParts.initMissingComponents();

        storeManager = new MemoryStoreDataManager(true);

        final DefaultIndyConfiguration indyConfig = new DefaultIndyConfiguration();
        indyConfig.setNotFoundCacheTimeoutSeconds(1);
        final ExpiringMemoryNotFoundCache nfc = new ExpiringMemoryNotFoundCache(indyConfig);

        WeftExecutorService rescanService = new PoolWeftExecutorService("test-rescan-executor",
                (ThreadPoolExecutor) Executors.newCachedThreadPool(), 2, 10f, false, null, null);

        downloadManager = new DefaultDownloadManager(storeManager, galleyParts.getTransferManager(),
                new IndyLocationExpander(storeManager), new MockInstance<>(new MockContentAdvisor()), nfc,
                rescanService);

        WeftExecutorService contentAccessService = new PoolWeftExecutorService("test-content-access-executor",
                (ThreadPoolExecutor) Executors.newCachedThreadPool(), 2, 10f, false, null, null);
        DirectContentAccess dca = new DefaultDirectContentAccess(downloadManager, contentAccessService);

        ContentDigester contentDigester = new DefaultContentDigester(dca,
                new CacheHandle<String, TransferMetadata>("content-metadata", contentMetadata));

        contentManager = new DefaultContentManager(storeManager, downloadManager, new IndyObjectMapper(true),
                new SpecialPathManagerImpl(), new MemoryNotFoundCache(), contentDigester,
                Collections.<ContentGenerator>emptySet());

        dataManager = new DataFileManager(temp.newFolder("data"), new DataFileEventManager());
        validationsManager = new PromoteValidationsManager(dataManager, new PromoteConfig(),
                new ValidationRuleParser(new ScriptEngine(dataManager), new IndyObjectMapper(true)));

        WeftExecutorService validateService = new PoolWeftExecutorService("test-validate-executor",
                (ThreadPoolExecutor) Executors.newCachedThreadPool(), 2, 10f, false, null, null);
        MavenModelProcessor modelProcessor = new MavenModelProcessor();
        validator = new PromotionValidator(validationsManager,
                new PromotionValidationTools(contentManager, storeManager, galleyParts.getPomReader(),
                        galleyParts.getMavenMetadataReader(), modelProcessor, galleyParts.getTypeMapper(),
                        galleyParts.getTransferManager(), contentDigester),
                storeManager, downloadManager, validateService);

        PromoteConfig config = new PromoteConfig();

        WeftExecutorService svc = new PoolWeftExecutorService("test-executor",
                (ThreadPoolExecutor) Executors.newCachedThreadPool(), 2, 10f, false, null, null);

        manager = new PromotionManager(validator, contentManager, downloadManager, storeManager,
                new Locker<StoreKey>(), new Locker<>(), config, nfc, svc, svc);

        executor = Executors.newCachedThreadPool();
    }

    /**
     * On collision, the promotion manager should skip the second file to be promoted (instead of overwriting the
     * existing one). This assumes no overwrite attribute is available for setting in the promotion request (or that
     * it is available but isn't set...and defaults to false).
     * @throws Exception
     */
    @Test
    public void promoteAllByPath_CollidingPaths_VerifySecondSkipped() throws Exception {
        final HostedRepository source1 = new HostedRepository(MAVEN_PKG_KEY, "source1");
        final HostedRepository source2 = new HostedRepository(MAVEN_PKG_KEY, "source2");
        storeManager.storeArtifactStore(source1, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());
        storeManager.storeArtifactStore(source2, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        String originalString = "This is a test";

        final String path = "/path/path";
        contentManager.store(source1, path, new ByteArrayInputStream(originalString.getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        contentManager.store(source2, path, new ByteArrayInputStream("This is another test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        PathsPromoteResult result = manager
                .promotePaths(new PathsPromoteRequest(source1.getKey(), target.getKey(), path), FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source1.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        Set<String> pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        Set<String> skipped = result.getSkippedPaths();
        assertThat(skipped == null || skipped.isEmpty(), equalTo(true));

        Set<String> completed = result.getCompletedPaths();
        assertThat(completed, notNullValue());
        assertThat(completed.size(), equalTo(1));

        assertThat(result.getError(), nullValue());

        Transfer ref = downloadManager.getStorageReference(target, path);
        assertThat(ref.exists(), equalTo(true));
        try (InputStream in = ref.openInputStream()) {
            String value = IOUtils.toString(in);
            assertThat(value, equalTo(originalString));
        }

        result = manager.promotePaths(new PathsPromoteRequest(source1.getKey(), target.getKey(), path),
                FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source1.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        skipped = result.getSkippedPaths();
        assertThat(skipped, notNullValue());
        assertThat(skipped.size(), equalTo(1));

        completed = result.getCompletedPaths();
        assertThat(completed == null || completed.isEmpty(), equalTo(true));

        assertThat(result.getError(), nullValue());

        ref = downloadManager.getStorageReference(target, path);
        assertThat(ref.exists(), equalTo(true));
        try (InputStream in = ref.openInputStream()) {
            String value = IOUtils.toString(in);
            assertThat(value, equalTo(originalString));
        }
    }

    @Test
    @Ignore("volatile, owing to galley fs locks")
    public void promoteAllByPath_RaceToPromote_FirstLocksTargetStore() throws Exception {
        Random rand = new Random();
        final HostedRepository[] sources = { new HostedRepository(MAVEN_PKG_KEY, "source1"),
                new HostedRepository(MAVEN_PKG_KEY, "source2") };
        final String[] paths = { "/path/path1", "/path/path2", "/path3", "/path/path/4" };
        Stream.of(sources).forEach((source) -> {
            try {
                storeManager.storeArtifactStore(source, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"),
                        false, true, new EventMetadata());

                Stream.of(paths).forEach((path) -> {
                    byte[] buf = new byte[1024 * 1024 * 2];
                    rand.nextBytes(buf);
                    try {
                        contentManager.store(source, path, new ByteArrayInputStream(buf), TransferOperation.UPLOAD,
                                new EventMetadata());
                    } catch (IndyWorkflowException e) {
                        e.printStackTrace();
                        Assert.fail("failed to store generated file to: " + source + path);
                    }
                });
            } catch (IndyDataException e) {
                e.printStackTrace();
                Assert.fail("failed to store hosted repository: " + source);
            }
        });

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        PathsPromoteResult[] results = new PathsPromoteResult[2];
        CountDownLatch cdl = new CountDownLatch(2);

        AtomicInteger counter = new AtomicInteger(0);
        Stream.of(sources).forEach((source) -> {
            int idx = counter.getAndIncrement();
            executor.execute(() -> {
                try {
                    results[idx] = manager.promotePaths(
                            new PathsPromoteRequest(source.getKey(), target.getKey(), paths), FAKE_BASE_URL);
                } catch (Exception e) {
                    e.printStackTrace();
                    Assert.fail("Promotion from source: " + source + " failed.");
                } finally {
                    cdl.countDown();
                }
            });

            try {
                Thread.sleep(25);
            } catch (InterruptedException e) {
                Assert.fail("Test interrupted");
            }
        });

        assertThat("Promotions failed to finish.", cdl.await(30, TimeUnit.SECONDS), equalTo(true));

        // first one should succeed.
        PathsPromoteResult result = results[0];
        assertThat(result.getRequest().getSource(), equalTo(sources[0].getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        Set<String> pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        Set<String> skipped = result.getSkippedPaths();
        assertThat(skipped == null || skipped.isEmpty(), equalTo(true));

        Set<String> completed = result.getCompletedPaths();
        assertThat(completed, notNullValue());
        assertThat(completed.size(), equalTo(paths.length));

        assertThat(result.getError(), nullValue());

        Stream.of(paths).forEach((path) -> {
            HostedRepository src = sources[0];
            Transfer sourceRef = downloadManager.getStorageReference(src, path);
            Transfer targetRef = downloadManager.getStorageReference(target, path);
            assertThat(targetRef.exists(), equalTo(true));
            try (InputStream sourceIn = sourceRef.openInputStream();
                    InputStream targetIn = targetRef.openInputStream()) {
                int s = -1, t = -1;
                while ((s = sourceIn.read()) == (t = targetIn.read())) {
                    if (s == -1) {
                        break;
                    }
                }

                if (s != -1 && s != t) {
                    Assert.fail(path + " doesn't match between source: " + src + " and target: " + target);
                }
            } catch (IOException e) {
                e.printStackTrace();
                Assert.fail("Failed to compare contents of: " + path + " between source: " + src + " and target: "
                        + target);
            }
        });

        // second one should be completely skipped.
        result = results[1];
        assertThat(result.getRequest().getSource(), equalTo(sources[1].getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        skipped = result.getSkippedPaths();
        assertThat(skipped, notNullValue());
        assertThat(skipped.size(), equalTo(paths.length));

        completed = result.getCompletedPaths();
        assertThat(completed == null || completed.isEmpty(), equalTo(true));

        assertThat(result.getError(), nullValue());
    }

    @Test
    public void promoteAllByPath_PushTwoArtifactsToHostedRepo_VerifyCopiedToOtherHostedRepo() throws Exception {
        final HostedRepository source = new HostedRepository(MAVEN_PKG_KEY, "source");
        storeManager.storeArtifactStore(source, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final String first = "/first/path";
        final String second = "/second/path";
        contentManager.store(source, first, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        contentManager.store(source, second, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final PathsPromoteResult result = manager
                .promotePaths(new PathsPromoteRequest(source.getKey(), target.getKey()), FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        final Set<String> pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        final Set<String> completed = result.getCompletedPaths();
        assertThat(completed, notNullValue());
        assertThat(completed.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        Transfer ref = downloadManager.getStorageReference(target, first);
        assertThat(ref.exists(), equalTo(true));

        ref = downloadManager.getStorageReference(target, second);
        assertThat(ref.exists(), equalTo(true));
    }

    @Test
    public void promoteAllByPath_PushTwoArtifactsToHostedRepo_DryRun_VerifyPendingPathsPopulated()
            throws Exception {
        final HostedRepository source = new HostedRepository(MAVEN_PKG_KEY, "source");
        storeManager.storeArtifactStore(source, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final String first = "/first/path";
        final String second = "/second/path";
        contentManager.store(source, first, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        contentManager.store(source, second, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final PathsPromoteResult result = manager.promotePaths(
                new PathsPromoteRequest(source.getKey(), target.getKey()).setDryRun(true), FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        final Set<String> completed = result.getCompletedPaths();
        assertThat(completed == null || completed.isEmpty(), equalTo(true));

        final Set<String> pending = result.getPendingPaths();
        assertThat(pending, notNullValue());
        assertThat(pending.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        Transfer ref = downloadManager.getStorageReference(target, first);
        assertThat(ref.exists(), equalTo(false));

        ref = downloadManager.getStorageReference(target, second);
        assertThat(ref.exists(), equalTo(false));
    }

    @Test
    public void promoteAllByPath_PurgeSource_PushTwoArtifactsToHostedRepo_VerifyCopiedToOtherHostedRepo()
            throws Exception {
        final HostedRepository source = new HostedRepository(MAVEN_PKG_KEY, "source");
        storeManager.storeArtifactStore(source, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final String first = "/first/path";
        final String second = "/second/path";
        contentManager.store(source, first, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        contentManager.store(source, second, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final PathsPromoteResult result = manager.promotePaths(
                new PathsPromoteRequest(source.getKey(), target.getKey()).setPurgeSource(true), FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        final Set<String> pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        final Set<String> completed = result.getCompletedPaths();
        assertThat(completed, notNullValue());
        assertThat(completed.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        Transfer ref = downloadManager.getStorageReference(target, first);
        assertThat(ref.exists(), equalTo(true));

        ref = downloadManager.getStorageReference(target, second);
        assertThat(ref.exists(), equalTo(true));

        // source artifacts should be deleted.
        ref = downloadManager.getStorageReference(source, first);
        assertThat(ref.exists(), equalTo(false));

        ref = downloadManager.getStorageReference(source, second);
        assertThat(ref.exists(), equalTo(false));
    }

    @Test
    public void rollback_PushTwoArtifactsToHostedRepo_PromoteSuccessThenRollback() throws Exception {
        final HostedRepository source = new HostedRepository(MAVEN_PKG_KEY, "source");
        storeManager.storeArtifactStore(source, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final String first = "/first/path";
        final String second = "/second/path";
        contentManager.store(source, first, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        contentManager.store(source, second, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        PathsPromoteResult result = manager.promotePaths(new PathsPromoteRequest(source.getKey(), target.getKey()),
                FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        Set<String> pending = result.getPendingPaths();
        assertThat("should be null or empty: " + pending, pending == null || pending.isEmpty(), equalTo(true));

        Set<String> completed = result.getCompletedPaths();
        assertThat(completed, notNullValue());
        assertThat(completed.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        result = manager.rollbackPathsPromote(result);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        completed = result.getCompletedPaths();
        assertThat(completed == null || completed.isEmpty(), equalTo(true));

        pending = result.getPendingPaths();
        assertThat(pending, notNullValue());
        assertThat(pending.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        Transfer ref = downloadManager.getStorageReference(target, first);
        assertThat(ref.exists(), equalTo(false));

        ref = downloadManager.getStorageReference(target, second);
        assertThat(ref.exists(), equalTo(false));
    }

    @Test
    public void rollback_PurgeSource_PushTwoArtifactsToHostedRepo_PromoteSuccessThenRollback_VerifyContentInSource()
            throws Exception {
        final HostedRepository source = new HostedRepository(MAVEN_PKG_KEY, "source");
        storeManager.storeArtifactStore(source, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        final String first = "/first/path";
        final String second = "/second/path";
        contentManager.store(source, first, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        contentManager.store(source, second, new ByteArrayInputStream("This is a test".getBytes()),
                TransferOperation.UPLOAD, new EventMetadata());

        final HostedRepository target = new HostedRepository(MAVEN_PKG_KEY, "target");
        storeManager.storeArtifactStore(target, new ChangeSummary(ChangeSummary.SYSTEM_USER, "test setup"), false,
                true, new EventMetadata());

        PathsPromoteResult result = manager.promotePaths(
                new PathsPromoteRequest(source.getKey(), target.getKey()).setPurgeSource(true), FAKE_BASE_URL);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        Set<String> pending = result.getPendingPaths();
        assertThat(pending == null || pending.isEmpty(), equalTo(true));

        Set<String> completed = result.getCompletedPaths();
        assertThat(completed, notNullValue());
        assertThat(completed.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        result = manager.rollbackPathsPromote(result);

        assertThat(result.getRequest().getSource(), equalTo(source.getKey()));
        assertThat(result.getRequest().getTarget(), equalTo(target.getKey()));

        completed = result.getCompletedPaths();
        assertThat(completed == null || completed.isEmpty(), equalTo(true));

        pending = result.getPendingPaths();
        assertThat(pending, notNullValue());
        assertThat(pending.size(), equalTo(2));

        assertThat(result.getError(), nullValue());

        Transfer ref = downloadManager.getStorageReference(target, first);
        assertThat(ref.exists(), equalTo(false));

        ref = downloadManager.getStorageReference(target, second);
        assertThat(ref.exists(), equalTo(false));

        ref = downloadManager.getStorageReference(source, first);
        assertThat(ref.exists(), equalTo(true));

        ref = downloadManager.getStorageReference(source, second);
        assertThat(ref.exists(), equalTo(true));
    }
}