001package org.fcrepo.migration.pidlist; 002 003import static org.junit.Assert.assertEquals; 004 005import java.io.File; 006import java.util.Map; 007import java.util.Set; 008import java.util.stream.Collectors; 009import javax.xml.stream.XMLStreamException; 010 011import edu.wisc.library.ocfl.api.OcflRepository; 012import edu.wisc.library.ocfl.api.model.DigestAlgorithm; 013import edu.wisc.library.ocfl.api.model.FileDetails; 014import edu.wisc.library.ocfl.api.model.ObjectDetails; 015import edu.wisc.library.ocfl.core.OcflRepositoryBuilder; 016import edu.wisc.library.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig; 017import edu.wisc.library.ocfl.core.path.mapper.LogicalPathMappers; 018import edu.wisc.library.ocfl.core.storage.OcflStorageBuilder; 019import org.apache.commons.io.FileUtils; 020import org.apache.commons.lang3.SystemUtils; 021import org.fcrepo.migration.MigrationType; 022import org.fcrepo.migration.Migrator; 023import org.fcrepo.migration.OcflSessionFactoryFactoryBean; 024import org.fcrepo.migration.ResourceMigrationType; 025import org.fcrepo.migration.foxml.LegacyFSIDResolver; 026import org.fcrepo.migration.foxml.NativeFoxmlDirectoryObjectSource; 027import org.fcrepo.migration.handlers.ObjectAbstractionStreamingFedoraObjectHandler; 028import org.fcrepo.migration.handlers.ocfl.ArchiveGroupHandler; 029import org.fcrepo.storage.ocfl.OcflObjectSessionFactory; 030import org.junit.Before; 031import org.junit.Test; 032 033/** 034 * Integration tests to test migration of head only datastream cases 035 * 036 * @author mikejritter 037 */ 038public class HeadOnlyIT { 039 040 private final String user = "fedoraAdmin"; 041 private final String idPrefix = "info:fedora/"; 042 private final String testPid = idPrefix + "example:1"; 043 private final boolean disableChecksum = false; 044 private final boolean disableDc = false; 045 046 private final DigestAlgorithm digestAlgorithm = DigestAlgorithm.sha512; 047 private final MigrationType migrationType = MigrationType.FEDORA_OCFL; 048 049 private LegacyFSIDResolver idResolver; 050 private NativeFoxmlDirectoryObjectSource objectSource; 051 private OcflObjectSessionFactory ocflObjectSessionFactory; 052 053 private File storage; 054 private File staging; 055 private File workingDir; 056 057 @Before 058 public void setup() throws Exception { 059 // Create directories expected in this test 060 storage = new File("target/test/ocfl/head-it/storage"); 061 staging = new File("target/test/ocfl/head-it/staging"); 062 workingDir = new File("target/test/ocfl/head-it/work"); 063 064 if (storage.exists()) { 065 FileUtils.forceDelete(storage); 066 } 067 if (staging.exists()) { 068 FileUtils.forceDelete(staging); 069 } 070 if (workingDir.exists()) { 071 FileUtils.forceDelete(workingDir); 072 } 073 074 storage.mkdirs(); 075 staging.mkdirs(); 076 workingDir.mkdirs(); 077 078 // Init Fedora3 classes 079 final var f3Hostname = "fedora.info"; 080 081 final var f3ObjectDir = new File("src/test/resources/legacyFS-multiple-versions/objects/2015/0430/16/01"); 082 final var f3DatastreamDir = 083 new File("src/test/resources/legacyFS-multiple-versions/datastreams/2015/0430/16/01"); 084 085 idResolver = new LegacyFSIDResolver(f3DatastreamDir); 086 objectSource = new NativeFoxmlDirectoryObjectSource(f3ObjectDir, idResolver, f3Hostname); 087 088 // Init OCFL classes 089 final var userUri = idPrefix + user; 090 ocflObjectSessionFactory = 091 new OcflSessionFactoryFactoryBean(storage.toPath(), staging.toPath(), migrationType, user, userUri, 092 digestAlgorithm, disableChecksum).getObject(); 093 } 094 095 @Test 096 public void testMigrateHeadOnly() throws XMLStreamException { 097 final var agh = archiveGroupHandler(true); 098 final var handler = new ObjectAbstractionStreamingFedoraObjectHandler(agh); 099 final var migrator = new Migrator(); 100 migrator.setSource(objectSource); 101 migrator.setHandler(handler); 102 103 migrator.run(); 104 105 // check that there's only single version for each datastream 106 final var ocflRepository = repository(); 107 final var objectDetails = ocflRepository.describeObject(testPid); 108 final var fileVersions = collectDatastreamVersions(objectDetails); 109 110 fileVersions.values().forEach(versions -> assertEquals(1, versions.size())); 111 } 112 113 @Test 114 public void testMigrateHeadOnlyDisabled() throws XMLStreamException { 115 final var agh = archiveGroupHandler(false); 116 final var handler = new ObjectAbstractionStreamingFedoraObjectHandler(agh); 117 final var migrator = new Migrator(); 118 migrator.setSource(objectSource); 119 migrator.setHandler(handler); 120 121 migrator.run(); 122 final var ocflRepository = repository(); 123 final var objectDetails = ocflRepository.describeObject(testPid); 124 final var fileVersions = collectDatastreamVersions(objectDetails); 125 126 // from the foxml, two datastreams have versions: DS1 and DC 127 final var dcVersions = fileVersions.get("/DC"); 128 final var ds1Versions = fileVersions.get("/DS1"); 129 130 assertEquals(2, dcVersions.size()); 131 assertEquals(2, ds1Versions.size()); 132 } 133 134 private ArchiveGroupHandler archiveGroupHandler(final boolean headOnly) { 135 final boolean foxmlFile = false; 136 final boolean deleteInactive = false; 137 final boolean datastreamExtensions = false; 138 final ResourceMigrationType resourceMigrationType = ResourceMigrationType.ARCHIVAL; 139 return new ArchiveGroupHandler(ocflObjectSessionFactory, migrationType, resourceMigrationType, 140 datastreamExtensions, deleteInactive, foxmlFile, user, idPrefix, 141 headOnly, disableChecksum, disableDc); 142 } 143 144 private OcflRepository repository() { 145 final var ocflStorage = OcflStorageBuilder.builder() 146 .fileSystem(storage.toPath()) 147 .build(); 148 149 final var logicalPathMapper = SystemUtils.IS_OS_WINDOWS ? LogicalPathMappers.percentEncodingWindowsMapper() 150 : LogicalPathMappers.percentEncodingLinuxMapper(); 151 152 return new OcflRepositoryBuilder().storage(ocflStorage) 153 .defaultLayoutConfig(new HashedNTupleLayoutConfig()) 154 .logicalPathMapper(logicalPathMapper) 155 .workDir(staging.toPath()) 156 .build(); 157 } 158 159 private Map<String, Set<String>> collectDatastreamVersions(final ObjectDetails objectDetails) { 160 return objectDetails.getVersionMap() 161 .values().stream() 162 .flatMap(details -> details.getFiles().stream()) 163 .map(FileDetails::getStorageRelativePath) 164 .filter(string -> !string.contains(".fcrepo") && !string.contains(".nt")) 165 .collect(Collectors.groupingBy(s -> s.substring(s.lastIndexOf("/")), Collectors.toSet())); 166 } 167}