package de.is24.infrastructure.gridfs.http.gridfs;
import de.is24.infrastructure.gridfs.http.category.LocalExecutionOnly;
import de.is24.infrastructure.gridfs.http.domain.YumEntry;
import de.is24.infrastructure.gridfs.http.exception.BadRequestException;
import de.is24.infrastructure.gridfs.http.exception.GridFSFileAlreadyExistsException;
import de.is24.infrastructure.gridfs.http.exception.GridFSFileNotFoundException;
import de.is24.infrastructure.gridfs.http.exception.InvalidRpmHeaderException;
import de.is24.infrastructure.gridfs.http.jaxb.Data;
import de.is24.infrastructure.gridfs.http.mongo.IntegrationTestContext;
import de.is24.infrastructure.gridfs.http.storage.FileDescriptor;
import de.is24.infrastructure.gridfs.http.storage.FileStorageItem;
import org.bson.types.ObjectId;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Date;
import static de.is24.infrastructure.gridfs.http.mongo.DatabaseStructure.MARKED_AS_DELETED_KEY;
import static de.is24.infrastructure.gridfs.http.storage.StorageTestUtils.METADATA_PATH;
import static de.is24.infrastructure.gridfs.http.storage.StorageTestUtils.PRIMARY_XMl_PATH;
import static de.is24.infrastructure.gridfs.http.storage.StorageTestUtils.REPOMD_PATH;
import static de.is24.infrastructure.gridfs.http.utils.RepositoryUtils.uniqueRepoName;
import static de.is24.infrastructure.gridfs.http.utils.RpmUtils.RPM_FILE_SIZE;
import static de.is24.infrastructure.gridfs.http.utils.RpmUtils.streamOf;
import static java.lang.System.currentTimeMillis;
import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.springframework.data.mongodb.core.query.Query.query;
import static org.springframework.data.mongodb.gridfs.GridFsCriteria.whereFilename;
@Category(LocalExecutionOnly.class)
public class StorageServiceIT {
public static final String INVALIDE_REPO_NAME = "hall//fdg";
public static final String NOARCH = "noarch";
public static final String SRC = "src";
public static final String TEST_FILE_OPEN_SHA256 = "068b5ccfeff0d2d4b3d792336f7dffcc0a9f7f82b81435940707014807e0fd2b";
public static final String TEST_FILE_BZ2_SHA256 = "6e2b1e4a5d95b2c0cdec718fa381a41d159750e7a5285a6eff4a4207589c2dc0";
public static final String TESTING_ARCH = "testing";
private static final String REPODATA = "repodata";
public static final String INVALID_RPM = "invalid.rpm";
private static final String VALID_NOARCH_RPM = "valid.noarch.rpm";
private static final String VALID_SOURCE_RPM = "valid.src.rpm";
private static final String VALID_NOARCH_RPM_SHA256 =
"70506d08285fa0c0120ccd4a1a8fe5537c41838b08655ac4c8dc9de8afb16a2f";
private static final String VALID_SOURCE_RPM_SHA256 =
"e687aee5c288062f9f79fd18308ff52f6fb46df38d768d9c71f037f8c8833394";
private static final String VALID_FILENAME_WITHOUT_VERSION = "test-artifact";
private static final String VALID_NOARCH_RPM_PATH_WITHOUT_VERSION = "/" + NOARCH + "/" +
VALID_FILENAME_WITHOUT_VERSION;
public static final String NOARCH_RPM_VERSION = "-1.2-1.noarch.rpm";
private static final String VALID_FILENAME = VALID_FILENAME_WITHOUT_VERSION + NOARCH_RPM_VERSION;
private static final String VALID_NOARCH_RPM_PATH = VALID_NOARCH_RPM_PATH_WITHOUT_VERSION + NOARCH_RPM_VERSION;
private static final String VALID_SOURCE_RPM_PATH = "/src/yum-repo-client-1.1-273.src.rpm";
@ClassRule
public static IntegrationTestContext context = new IntegrationTestContext();
private long startTime = Long.MAX_VALUE;
private Date testStart = new Date();
@Test
public void propagateRpm() throws Exception {
assertPropagateRpm(VALID_NOARCH_RPM_PATH);
}
@Test
public void propagateRpmWithoutVersion() throws Exception {
assertPropagateRpm(VALID_NOARCH_RPM_PATH_WITHOUT_VERSION);
}
private void assertPropagateRpm(String rpmPathInRepo) throws Exception {
String sourceRepo = context.storageTestUtils().givenFullRepository();
String destinationRepo = context.storageTestUtils().givenFullRepository();
startTime = currentTimeMillis();
context.gridFsService().propagateRpm(sourceRepo + rpmPathInRepo, destinationRepo);
assertOldRpmIsOverwritten(destinationRepo);
FileStorageItem rpm = findNoarchRpm(destinationRepo);
assertThat(rpm, notNullValue());
assertFileMetaData(destinationRepo, rpm);
assertYumEntryHas(rpm, destinationRepo);
assertRepoWasModifiedAfterStartTime(sourceRepo);
assertRepoWasModifiedAfterStartTime(destinationRepo);
}
@Test
public void propagateRepository() throws Exception {
String sourceRepo = context.storageTestUtils().givenFullRepository();
String destinationRepo = context.storageTestUtils().givenFullRepository();
startTime = currentTimeMillis();
context.gridFsService().propagateRepository(sourceRepo, destinationRepo);
assertOldRpmIsOverwritten(destinationRepo);
assertRepoMetadataExists(sourceRepo);
FileStorageItem rpm = findNoarchRpm(destinationRepo);
assertThat(rpm, notNullValue());
assertFileMetaData(destinationRepo, rpm);
assertYumEntryHas(rpm, destinationRepo);
assertRepoMetadataExists(destinationRepo);
assertRepoWasModifiedAfterStartTime(sourceRepo);
assertRepoWasModifiedAfterStartTime(destinationRepo);
}
@Test
public void propagateRepositoryWithDeletedFiles() throws Exception {
String sourceRepo = context.storageTestUtils().givenFullRepository();
FileDescriptor descriptor = new FileDescriptor(sourceRepo, NOARCH, "myDeleteFile.rpm");
context.fileStorageService().markForDeletionByPath(descriptor.getFilename());
String destinationRepo = context.storageTestUtils().givenFullRepository();
startTime = currentTimeMillis();
context.gridFsService().propagateRepository(sourceRepo, destinationRepo);
assertOldRpmIsOverwritten(destinationRepo);
assertRepoMetadataExists(sourceRepo);
FileStorageItem rpm = findNoarchRpm(destinationRepo);
assertThat(rpm, notNullValue());
assertFileMetaData(destinationRepo, rpm);
assertYumEntryHas(rpm, destinationRepo);
assertRepoMetadataExists(destinationRepo);
assertRepoWasModifiedAfterStartTime(sourceRepo);
assertRepoWasModifiedAfterStartTime(destinationRepo);
}
@Test(expected = InvalidRpmHeaderException.class)
public void throwExceptionForEmptyRpm() throws Exception {
String reponame = uniqueRepoName();
context.gridFsService().storeRpm(reponame, new ByteArrayInputStream(new byte[0]));
}
@Test(expected = InvalidRpmHeaderException.class)
public void throwExceptionForInvalidRpm() throws Exception {
String reponame = uniqueRepoName();
context.gridFsService().storeRpm(reponame, streamOf(INVALID_RPM));
}
@Test
public void storeValidNoarchRpm() throws Exception {
startTime = currentTimeMillis();
String reponame = uniqueRepoName();
context.gridFsService().storeRpm(reponame, streamOf(VALID_NOARCH_RPM));
FileStorageItem file = findNoarchRpm(reponame);
assertThat(file, notNullValue());
assertFileMetaData(reponame, file);
assertThat(file.getChecksumSha256(), is(VALID_NOARCH_RPM_SHA256));
assertThat(file.getUploadDate(), notNullValue());
assertThat(file.getContentType(), is("application/x-rpm"));
assertThat(file.getSize(), is((long) RPM_FILE_SIZE));
assertYumEntryForValidNoArchRpm(reponame, file);
assertRepoWasModifiedAfterStartTime(reponame);
}
@Test
public void regenerateAllFileMetaData() throws Exception {
String reponame = uniqueRepoName();
context.gridFsService().storeRpm(reponame, streamOf(VALID_NOARCH_RPM));
FileStorageItem file = findNoarchRpm(reponame);
context.yumEntriesRepository().delete((ObjectId) file.getId());
context.gridFsService().regenerateMetadataForAllFiles();
assertYumEntryForValidNoArchRpm(reponame, file);
}
@Test
public void storeValidSourceRpm() throws Exception {
startTime = currentTimeMillis();
String reponame = uniqueRepoName();
context.gridFsService().storeRpm(reponame, streamOf(VALID_SOURCE_RPM));
FileStorageItem file = context.fileStorageService().findBy(new FileDescriptor(reponame + VALID_SOURCE_RPM_PATH));
assertThat(file, notNullValue());
assertThat(file.getRepo(), is(reponame));
assertThat(file.getArch(), is(SRC));
assertThat(file.getChecksumSha256(), is(VALID_SOURCE_RPM_SHA256));
assertThat(file.getContentType(), is("application/x-rpm"));
assertThat(file.getSize(), is(9644L));
YumEntry yumEntry = context.yumEntriesRepository().findOne((ObjectId) file.getId());
assertThat(yumEntry.getRepo(), is(reponame));
assertThat(yumEntry.getYumPackage().getArch(), is(SRC));
assertThat(yumEntry.getYumPackage().getName(), is("yum-repo-client"));
assertThat(yumEntry.getYumPackage().getSize().getPackaged(), is(9644));
assertRepoWasModifiedAfterStartTime(reponame);
}
@Test(expected = GridFSFileAlreadyExistsException.class)
public void forbidOverwritingExistingRpm() throws Exception {
String reponame = uniqueRepoName();
context.gridFsService().storeRpm(reponame, streamOf(VALID_NOARCH_RPM));
context.gridFsService().storeRpm(reponame, streamOf(VALID_NOARCH_RPM));
}
@Test(expected = BadRequestException.class)
public void throwExceptionForBadReponameOnStore() throws Exception {
String reponame = INVALIDE_REPO_NAME;
context.gridFsService().storeRpm(reponame, streamOf(VALID_NOARCH_RPM));
}
@Test(expected = BadRequestException.class)
public void throwExceptionForBadReponameOnDelete() throws Exception {
context.gridFsService().deleteRepository(INVALIDE_REPO_NAME);
}
@Test
public void deleteEmptyRepository() throws Exception {
String reponame = uniqueRepoName();
context.gridFsService().deleteRepository(reponame);
assertThat(context.repoEntriesRepository().findFirstByName(reponame), nullValue());
}
@Test
public void deleteRepository() throws Exception {
String reponame = context.storageTestUtils().givenFullRepository();
context.gridFsService().storeRpm(reponame, streamOf(VALID_SOURCE_RPM));
context.gridFsService().deleteRepository(reponame);
assertThatFileIsMarkedForDeletion(createValidNoarchRPMDescriptorInRepo(reponame));
assertThat(context.yumEntriesRepository().findByRepo(reponame).size(), is(0));
assertThat(context.repoEntriesRepository().findFirstByName(reponame), nullValue());
}
private FileDescriptor createValidNoarchRPMDescriptorInRepo(String reponame) {
return new FileDescriptor(reponame, NOARCH, VALID_FILENAME);
}
@Test
public void deleteRpm() throws Exception {
String reponame = context.storageTestUtils().givenFullRepository();
startTime = currentTimeMillis();
FileDescriptor descriptor = new FileDescriptor(reponame, NOARCH,
VALID_FILENAME_WITHOUT_VERSION + NOARCH_RPM_VERSION);
context.gridFsService().delete(descriptor);
assertThat(context.fileStorageService().findBy(descriptor), nullValue());
assertThat(context.yumEntriesRepository().findByRepo(reponame).size(), is(0));
assertRepoWasModifiedAfterStartTime(reponame);
}
@Test
public void metaDataForDeletionIsSetByFilenameRegex() throws Exception {
final String reponame = uniqueRepoName();
FileDescriptor matchingDescriptor = new FileDescriptor(reponame, TESTING_ARCH,
"willmatch-filename.rpm");
context.storageTestUtils().givenFileWithDescriptor(matchingDescriptor);
FileDescriptor noMatchDescriptor = new FileDescriptor(reponame, TESTING_ARCH, "no_match");
context.storageTestUtils().givenFileWithDescriptor(noMatchDescriptor);
context.fileStorageService().markForDeletionByFilenameRegex(".*-filename");
assertThatFileIsMarkedForDeletion(matchingDescriptor);
FileStorageItem storageItem = context.fileStorageService().findBy(noMatchDescriptor);
assertThat(((GridFsFileStorageItem) storageItem).getDbFile()
.getMetaData().get(MARKED_AS_DELETED_KEY),
is(nullValue()));
}
@Test
public void metaDataForDeletionIsSetByPath() throws Exception {
final String repoName = context.storageTestUtils().givenFullRepository();
FileDescriptor descriptor = createValidNoarchRPMDescriptorInRepo(repoName);
context.fileStorageService().markForDeletionByPath(descriptor.getPath());
assertThatFileIsMarkedForDeletion(descriptor);
}
private void assertThatFileIsMarkedForDeletion(FileDescriptor descriptor) {
FileStorageItem storageItem = context.fileStorageService().findBy(descriptor);
Date deletionDate = storageItem.getDateOfMarkAsDeleted();
assertThat(deletionDate, is(notNullValue()));
assertThat(deletionDate, greaterThanOrEqualTo(testStart));
assertThat(deletionDate, lessThanOrEqualTo(new Date()));
}
@Test
public void deleteFiles() throws Exception {
String repo = context.storageTestUtils().givenFullRepository();
FileDescriptor descriptor = new FileDescriptor(repo, TESTING_ARCH, "any-filename");
context.gridFsTemplate().store(asStream("/test-for-delete-file.txt"), descriptor.getPath());
FileStorageItem file = context.fileStorageService().findBy(descriptor);
context.gridFsService().delete(asList(file));
assertThat(context.fileStorageService().findBy(descriptor), nullValue());
}
@Test
public void storeRepodataAsBz2() throws Exception {
String reponame = uniqueRepoName();
Data data = context.gridFsService()
.storeRepodataDbBz2(reponame, new File(getClass().getResource("/test-for-bz2.txt").toURI()), "primary");
assertThat(data.getSize(), is(68L));
assertThat(data.getChecksum().getChecksum(), is(TEST_FILE_BZ2_SHA256));
assertThat(data.getOpenSize(), is(33L));
assertThat(data.getOpenChecksum().getChecksum(), is(TEST_FILE_OPEN_SHA256));
FileDescriptor descriptor = new FileDescriptor(reponame, REPODATA,
"primary-" + TEST_FILE_BZ2_SHA256 + ".sqlite.bz2");
FileStorageItem storageItem = context.fileStorageService().findBy(descriptor);
assertThat(storageItem, notNullValue());
assertThat(storageItem.getRepo(), is(reponame));
assertThat(storageItem.getArch(), is("repodata"));
assertThat(storageItem.getSize(), is(68L));
assertThat(storageItem.getChecksumSha256(), is(TEST_FILE_BZ2_SHA256));
assertThat(storageItem.getUploadDate(), notNullValue());
}
@Test(expected = GridFSFileNotFoundException.class)
public void dontMoveFileMarkedForDeletion() throws Exception {
String sourceRepo = uniqueRepoName();
String destinationRepo = uniqueRepoName();
context.gridFsService().storeRpm(sourceRepo, streamOf(VALID_NOARCH_RPM));
FileStorageItem file = findNoarchRpm(sourceRepo);
context.fileStorageService().markForDeletionByPath(file.getFilename());
context.gridFsService().propagateRpm(file.getFilename(), destinationRepo);
}
private void assertRepoWasModifiedAfterStartTime(String reponame) {
assertThat(context.repoEntriesRepository().findFirstByName(reponame).getLastModified().getTime(),
greaterThan(startTime));
}
private void assertYumEntryForValidNoArchRpm(String reponame, FileStorageItem file) {
YumEntry yumEntry = context.yumEntriesRepository().findOne((ObjectId) file.getId());
assertThat(yumEntry, notNullValue());
assertThat(yumEntry.getRepo(), is(reponame));
assertThat(yumEntry.getYumPackage().getArch(), is(NOARCH));
assertThat(yumEntry.getYumPackage().getName(), is("test-artifact"));
assertThat(yumEntry.getYumPackage().getSize().getPackaged(), is(RPM_FILE_SIZE));
assertThat(yumEntry.getYumPackage().getChecksum().getType(), is("sha256"));
assertThat(yumEntry.getYumPackage().getChecksum().getChecksum(), is(VALID_NOARCH_RPM_SHA256));
assertThat(yumEntry.getYumPackage().getTime().getFile(), notNullValue());
assertThat(yumEntry.getYumPackage().getPackageFormat().getHeaderStart(), is(280));
assertThat(yumEntry.getYumPackage().getPackageFormat().getHeaderEnd(), is(1308));
}
private void assertOldRpmIsOverwritten(String destinationRepo) {
assertThat(context.gridFsTemplate()
.find(query(whereFilename().is(destinationRepo + VALID_NOARCH_RPM_PATH)))
.size(), is(1));
}
private void assertFileMetaData(String destinationRepo, FileStorageItem rpm) {
assertThat(rpm.getRepo(), is(destinationRepo));
assertThat(rpm.getArch(), is(NOARCH));
}
private void assertYumEntryHas(FileStorageItem rpm, String destinationRepo) {
YumEntry entry = context.yumEntriesRepository().findOne((ObjectId) rpm.getId());
assertThat(entry.getRepo(), is(destinationRepo));
}
private void assertRepoMetadataExists(String sourceRepo) {
assertThat(context.gridFsTemplate().findOne(query(whereFilename().is(sourceRepo + REPOMD_PATH))), notNullValue());
assertThat(context.gridFsTemplate().findOne(query(whereFilename().is(sourceRepo + PRIMARY_XMl_PATH))),
notNullValue());
assertThat(context.gridFsTemplate().findOne(query(whereFilename().is(sourceRepo + METADATA_PATH))), notNullValue());
}
private FileStorageItem findNoarchRpm(String destinationRepo) {
return context.fileStorageService().findBy(new FileDescriptor(destinationRepo + VALID_NOARCH_RPM_PATH));
}
private InputStream asStream(String resource) {
return getClass().getResourceAsStream(resource);
}
}