001package org.fcrepo.migration.handlers.ocfl; 002 003import com.fasterxml.jackson.annotation.JsonInclude; 004import com.fasterxml.jackson.databind.ObjectMapper; 005import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 006import edu.wisc.library.ocfl.api.MutableOcflRepository; 007import edu.wisc.library.ocfl.api.model.FileDetails; 008import edu.wisc.library.ocfl.api.model.ObjectVersionId; 009import edu.wisc.library.ocfl.core.OcflRepositoryBuilder; 010import edu.wisc.library.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig; 011import edu.wisc.library.ocfl.core.path.mapper.LogicalPathMappers; 012import edu.wisc.library.ocfl.core.storage.filesystem.FileSystemOcflStorage; 013import org.apache.commons.codec.digest.DigestUtils; 014import org.apache.commons.io.IOUtils; 015import org.apache.commons.lang3.SystemUtils; 016import org.fcrepo.migration.ContentDigest; 017import org.fcrepo.migration.DatastreamInfo; 018import org.fcrepo.migration.DatastreamVersion; 019import org.fcrepo.migration.DefaultObjectInfo; 020import org.fcrepo.migration.MigrationType; 021import org.fcrepo.migration.ObjectProperties; 022import org.fcrepo.migration.ObjectProperty; 023import org.fcrepo.migration.ObjectVersionReference; 024import org.fcrepo.storage.ocfl.CommitType; 025import org.fcrepo.storage.ocfl.DefaultOcflObjectSessionFactory; 026import org.fcrepo.storage.ocfl.InteractionModel; 027import org.fcrepo.storage.ocfl.OcflObjectSession; 028import org.fcrepo.storage.ocfl.OcflObjectSessionFactory; 029import org.fcrepo.storage.ocfl.PersistencePaths; 030import org.fcrepo.storage.ocfl.ResourceHeadersVersion; 031import org.fcrepo.storage.ocfl.cache.NoOpCache; 032import org.junit.Before; 033import org.junit.Rule; 034import org.junit.Test; 035import org.junit.rules.TemporaryFolder; 036import org.mockito.Mockito; 037import org.mockito.stubbing.Answer; 038 039import java.io.ByteArrayInputStream; 040import java.io.IOException; 041import java.io.InputStream; 042import java.io.UncheckedIOException; 043import java.nio.charset.StandardCharsets; 044import java.nio.file.Files; 045import java.nio.file.Path; 046import java.time.Instant; 047import java.util.List; 048import java.util.Objects; 049 050import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS; 051import static org.hamcrest.MatcherAssert.assertThat; 052import static org.hamcrest.Matchers.allOf; 053import static org.hamcrest.Matchers.containsString; 054import static org.junit.Assert.assertEquals; 055import static org.junit.Assert.assertFalse; 056import static org.junit.Assert.assertNotEquals; 057import static org.junit.Assert.assertTrue; 058import static org.mockito.Mockito.doReturn; 059import static org.mockito.Mockito.when; 060 061/** 062 * @author pwinckles 063 */ 064public class ArchiveGroupHandlerTest { 065 066 private static final String FCREPO_ROOT = "info:fedora/"; 067 private static final String USER = "fedoraAdmin"; 068 private static final String INLINE = "X"; 069 private static final String PROXY = "E"; 070 private static final String REDIRECT = "R"; 071 private static final String MANAGED = "M"; 072 private static final String DS_ACTIVE = "A"; 073 private static final String DS_INACTIVE = "I"; 074 private static final String DS_DELETED = "D"; 075 private static final String OBJ_ACTIVE = "Active"; 076 private static final String OBJ_INACTIVE = "Inactive"; 077 private static final String OBJ_DELETED = "Deleted"; 078 079 @Rule 080 public TemporaryFolder tempDir = new TemporaryFolder(); 081 082 private Path ocflRoot; 083 private Path staging; 084 085 private MutableOcflRepository ocflRepo; 086 private OcflObjectSessionFactory sessionFactory; 087 private OcflObjectSessionFactory plainSessionFactory; 088 089 private ObjectMapper objectMapper; 090 private String date; 091 092 @Before 093 public void setup() throws IOException { 094 ocflRoot = tempDir.newFolder("ocfl").toPath(); 095 staging = tempDir.newFolder("staging").toPath(); 096 097 final var logicalPathMapper = SystemUtils.IS_OS_WINDOWS ? 098 LogicalPathMappers.percentEncodingWindowsMapper() : LogicalPathMappers.percentEncodingLinuxMapper(); 099 100 ocflRepo = new OcflRepositoryBuilder() 101 .defaultLayoutConfig(new HashedNTupleLayoutConfig()) 102 .logicalPathMapper(logicalPathMapper) 103 .storage(FileSystemOcflStorage.builder().repositoryRoot(ocflRoot).build()) 104 .workDir(staging) 105 .buildMutable(); 106 107 objectMapper = new ObjectMapper() 108 .configure(WRITE_DATES_AS_TIMESTAMPS, false) 109 .registerModule(new JavaTimeModule()) 110 .setSerializationInclusion(JsonInclude.Include.NON_NULL); 111 112 sessionFactory = new DefaultOcflObjectSessionFactory(ocflRepo, staging, objectMapper, 113 new NoOpCache<>(), CommitType.NEW_VERSION, 114 "testing", USER, "info:fedora/fedoraAdmin"); 115 116 plainSessionFactory = new PlainOcflObjectSessionFactory(ocflRepo, staging, 117 "testing", USER, "info:fedora/fedoraAdmin"); 118 119 date = Instant.now().toString().substring(0, 10); 120 } 121 122 @Test 123 public void processObjectSingleVersionF6Format() throws IOException { 124 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, false); 125 126 final var pid = "obj1"; 127 final var dsId1 = "ds1"; 128 final var dsId2 = "ds2"; 129 130 final var ds1 = datastreamVersion(dsId1, true, MANAGED, "text/plain", "hello", null); 131 final var ds2 = datastreamVersion(dsId2, true, INLINE, "application/xml", "<xml>goodbye</xml>", null); 132 when(ds2.getSize()).thenReturn(100L); 133 134 handler.processObjectVersions(List.of( 135 objectVersionReference(pid, true, List.of(ds1, ds2)) 136 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 137 138 final var ocflObjectId = addPrefix(pid); 139 final var session = sessionFactory.newSession(ocflObjectId); 140 141 verifyObjectRdf(contentToString(session, ocflObjectId)); 142 verifyObjectHeaders(session, ocflObjectId); 143 144 verifyBinary(contentToString(session, ocflObjectId, dsId1), ds1); 145 verifyHeaders(session, ocflObjectId, dsId1, ds1); 146 verifyDescRdf(session, ocflObjectId, dsId1, ds1); 147 verifyDescHeaders(session, ocflObjectId, dsId1); 148 149 verifyBinary(contentToString(session, ocflObjectId, dsId2), ds2); 150 verifyHeaders(session, ocflObjectId, dsId2, ds2); 151 verifyDescRdf(session, ocflObjectId, dsId2, ds2); 152 verifyDescHeaders(session, ocflObjectId, dsId2); 153 } 154 155 @Test 156 public void processObjectMultipleVersionsF6Format() throws IOException { 157 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, false); 158 159 final var pid = "obj2"; 160 final var dsId1 = "ds3"; 161 final var dsId2 = "ds4"; 162 163 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", null); 164 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", null); 165 166 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", null); 167 168 handler.processObjectVersions(List.of( 169 objectVersionReference(pid, true, List.of(ds1V1, ds2V1)), 170 objectVersionReference(pid, false, List.of(ds2V2)) 171 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 172 173 final var ocflObjectId = addPrefix(pid); 174 final var session = sessionFactory.newSession(ocflObjectId); 175 176 verifyObjectRdf(contentToString(session, ocflObjectId)); 177 verifyObjectHeaders(session, ocflObjectId); 178 179 verifyBinary(contentToString(session, ocflObjectId, dsId1), ds1V1); 180 verifyHeaders(session, ocflObjectId, dsId1, ds1V1); 181 verifyDescRdf(session, ocflObjectId, dsId1, ds1V1); 182 verifyDescHeaders(session, ocflObjectId, dsId1); 183 184 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v1"), ds2V1); 185 verifyHeaders(session, ocflObjectId, dsId2, ds2V1, "v1"); 186 verifyDescRdf(session, ocflObjectId, dsId2, ds2V1, "v1"); 187 verifyDescHeaders(session, ocflObjectId, dsId2, "v1"); 188 189 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v2"), ds2V2); 190 verifyHeaders(session, ocflObjectId, dsId2, ds2V2, "v2"); 191 verifyDescRdf(session, ocflObjectId, dsId2, ds2V2, "v2"); 192 verifyDescHeaders(session, ocflObjectId, dsId2, "v2"); 193 } 194 195 @Test 196 public void processObjectMultipleVersionsWithDeletedDsF6Format() throws IOException { 197 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, false); 198 199 final var pid = "obj2"; 200 final var dsId1 = "ds3"; 201 final var dsId2 = "ds4"; 202 203 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", 204 DS_INACTIVE, null); 205 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", DS_DELETED, null); 206 207 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", DS_DELETED, null); 208 209 handler.processObjectVersions(List.of( 210 objectVersionReference(pid, true, List.of(ds1V1, ds2V1)), 211 objectVersionReference(pid, false, List.of(ds2V2)) 212 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 213 214 final var ocflObjectId = addPrefix(pid); 215 final var session = sessionFactory.newSession(ocflObjectId); 216 217 verifyObjectRdf(contentToString(session, ocflObjectId)); 218 verifyObjectHeaders(session, ocflObjectId); 219 220 verifyBinary(contentToString(session, ocflObjectId, dsId1), ds1V1); 221 verifyHeaders(session, ocflObjectId, dsId1, ds1V1); 222 verifyDescRdf(session, ocflObjectId, dsId1, ds1V1); 223 verifyDescHeaders(session, ocflObjectId, dsId1); 224 225 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v1"), ds2V1); 226 verifyHeaders(session, ocflObjectId, dsId2, ds2V1, "v1"); 227 verifyDescRdf(session, ocflObjectId, dsId2, ds2V1, "v1"); 228 verifyDescHeaders(session, ocflObjectId, dsId2, "v1"); 229 230 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v2"), ds2V2); 231 verifyHeaders(session, ocflObjectId, dsId2, ds2V2, "v2"); 232 verifyDescRdf(session, ocflObjectId, dsId2, ds2V2, "v2"); 233 verifyDescHeaders(session, ocflObjectId, dsId2, "v2"); 234 235 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId2)); 236 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId2)); 237 } 238 239 @Test 240 public void processObjectMultipleVersionsAndDeleteInactiveF6Format() throws IOException { 241 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, true); 242 243 final var pid = "obj2"; 244 final var dsId1 = "ds3"; 245 final var dsId2 = "ds4"; 246 247 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", 248 DS_INACTIVE, null); 249 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", DS_DELETED, null); 250 251 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", DS_DELETED, null); 252 253 handler.processObjectVersions(List.of( 254 objectVersionReference(pid, true, List.of(ds1V1, ds2V1)), 255 objectVersionReference(pid, false, List.of(ds2V2)) 256 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 257 258 final var ocflObjectId = addPrefix(pid); 259 final var session = sessionFactory.newSession(ocflObjectId); 260 261 verifyObjectRdf(contentToString(session, ocflObjectId)); 262 verifyObjectHeaders(session, ocflObjectId); 263 264 verifyBinary(contentVersionToString(session, ocflObjectId, dsId1, "v1"), ds1V1); 265 verifyHeaders(session, ocflObjectId, dsId1, ds1V1, "v1"); 266 verifyDescRdf(session, ocflObjectId, dsId1, ds1V1, "v1"); 267 verifyDescHeaders(session, ocflObjectId, dsId1, "v1"); 268 269 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v1"), ds2V1); 270 verifyHeaders(session, ocflObjectId, dsId2, ds2V1, "v1"); 271 verifyDescRdf(session, ocflObjectId, dsId2, ds2V1, "v1"); 272 verifyDescHeaders(session, ocflObjectId, dsId2, "v1"); 273 274 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v2"), ds2V2); 275 verifyHeaders(session, ocflObjectId, dsId2, ds2V2, "v2"); 276 verifyDescRdf(session, ocflObjectId, dsId2, ds2V2, "v2"); 277 verifyDescHeaders(session, ocflObjectId, dsId2, "v2"); 278 279 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId1)); 280 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId1)); 281 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId2)); 282 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId2)); 283 } 284 285 @Test 286 public void processObjectMultipleVersionsAndObjectDeletedF6Format() throws IOException { 287 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, false); 288 289 final var pid = "obj2"; 290 final var dsId1 = "ds3"; 291 final var dsId2 = "ds4"; 292 293 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", null); 294 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", null); 295 296 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", null); 297 298 handler.processObjectVersions(List.of( 299 objectVersionReference(pid, true, OBJ_DELETED, List.of(ds1V1, ds2V1)), 300 objectVersionReference(pid, false, OBJ_DELETED, List.of(ds2V2)) 301 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 302 303 final var ocflObjectId = addPrefix(pid); 304 final var session = sessionFactory.newSession(ocflObjectId); 305 306 verifyObjectRdf(contentVersionToString(session, ocflObjectId, "v1")); 307 verifyObjectHeaders(session, ocflObjectId, "v1"); 308 309 verifyBinary(contentVersionToString(session, ocflObjectId, dsId1, "v1"), ds1V1); 310 verifyHeaders(session, ocflObjectId, dsId1, ds1V1, "v1"); 311 verifyDescRdf(session, ocflObjectId, dsId1, ds1V1, "v1"); 312 verifyDescHeaders(session, ocflObjectId, dsId1, "v1"); 313 314 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v1"), ds2V1); 315 verifyHeaders(session, ocflObjectId, dsId2, ds2V1, "v1"); 316 verifyDescRdf(session, ocflObjectId, dsId2, ds2V1, "v1"); 317 verifyDescHeaders(session, ocflObjectId, dsId2, "v1"); 318 319 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v2"), ds2V2); 320 verifyHeaders(session, ocflObjectId, dsId2, ds2V2, "v2"); 321 verifyDescRdf(session, ocflObjectId, dsId2, ds2V2, "v2"); 322 verifyDescHeaders(session, ocflObjectId, dsId2, "v2"); 323 324 verifyResourceDeleted(session, ocflObjectId); 325 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId1)); 326 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId1)); 327 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId2)); 328 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId2)); 329 } 330 331 @Test 332 public void processObjectMultipleVersionsAndObjectInactiveDeletedF6Format() throws IOException { 333 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, true); 334 335 final var pid = "obj2"; 336 final var dsId1 = "ds3"; 337 final var dsId2 = "ds4"; 338 339 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", null); 340 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", null); 341 342 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", null); 343 344 handler.processObjectVersions(List.of( 345 objectVersionReference(pid, true, OBJ_INACTIVE, List.of(ds1V1, ds2V1)), 346 objectVersionReference(pid, false, OBJ_INACTIVE, List.of(ds2V2)) 347 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 348 349 final var ocflObjectId = addPrefix(pid); 350 final var session = sessionFactory.newSession(ocflObjectId); 351 352 verifyObjectRdf(contentVersionToString(session, ocflObjectId, "v1")); 353 verifyObjectHeaders(session, ocflObjectId, "v1"); 354 355 verifyBinary(contentVersionToString(session, ocflObjectId, dsId1, "v1"), ds1V1); 356 verifyHeaders(session, ocflObjectId, dsId1, ds1V1, "v1"); 357 verifyDescRdf(session, ocflObjectId, dsId1, ds1V1, "v1"); 358 verifyDescHeaders(session, ocflObjectId, dsId1, "v1"); 359 360 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v1"), ds2V1); 361 verifyHeaders(session, ocflObjectId, dsId2, ds2V1, "v1"); 362 verifyDescRdf(session, ocflObjectId, dsId2, ds2V1, "v1"); 363 verifyDescHeaders(session, ocflObjectId, dsId2, "v1"); 364 365 verifyBinary(contentVersionToString(session, ocflObjectId, dsId2, "v2"), ds2V2); 366 verifyHeaders(session, ocflObjectId, dsId2, ds2V2, "v2"); 367 verifyDescRdf(session, ocflObjectId, dsId2, ds2V2, "v2"); 368 verifyDescHeaders(session, ocflObjectId, dsId2, "v2"); 369 370 verifyResourceDeleted(session, ocflObjectId); 371 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId1)); 372 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId1)); 373 verifyResourceDeleted(session, resourceId(ocflObjectId, dsId2)); 374 verifyResourceDeleted(session, metadataId(ocflObjectId, dsId2)); 375 } 376 377 @Test 378 public void processObjectMultipleVersionsPlainFormat() throws IOException { 379 final var handler = createHandler(MigrationType.PLAIN_OCFL, false, false); 380 381 final var pid = "obj2"; 382 final var dsId1 = "ds3"; 383 final var dsId2 = "ds4"; 384 385 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", null); 386 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", null); 387 388 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", null); 389 390 handler.processObjectVersions(List.of( 391 objectVersionReference(pid, true, List.of(ds1V1, ds2V1)), 392 objectVersionReference(pid, false, List.of(ds2V2)) 393 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 394 395 final var rootResourceId = addPrefix(pid); 396 397 verifyFcrepoNotExists(rootResourceId); 398 399 verifyObjectRdf(rawContentToString(rootResourceId, PersistencePaths.rdfResource(rootResourceId, rootResourceId) 400 .getContentFilePath())); 401 402 verifyBinary(rawContentVersionToString(rootResourceId, 403 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId1)).getContentFilePath(), 404 "v1"), ds1V1); 405 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 406 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId1)).getContentFilePath(), 407 "v1"), ds1V1); 408 409 verifyBinary(rawContentVersionToString(rootResourceId, 410 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)).getContentFilePath(), 411 "v1"), ds2V1); 412 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 413 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath(), 414 "v1"), ds2V1); 415 416 verifyBinary(rawContentVersionToString(rootResourceId, 417 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)).getContentFilePath(), 418 "v2"), ds2V2); 419 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 420 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath(), 421 "v2"), ds2V2); 422 } 423 424 @Test 425 public void processObjectMultipleVersionsWithDeletedDsPlainFormat() throws IOException { 426 final var handler = createHandler(MigrationType.PLAIN_OCFL, false, false); 427 428 final var pid = "obj2"; 429 final var dsId1 = "ds3"; 430 final var dsId2 = "ds4"; 431 432 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", null); 433 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", DS_DELETED, null); 434 435 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", DS_DELETED, null); 436 437 handler.processObjectVersions(List.of( 438 objectVersionReference(pid, true, List.of(ds1V1, ds2V1)), 439 objectVersionReference(pid, false, List.of(ds2V2)) 440 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 441 442 final var rootResourceId = addPrefix(pid); 443 444 verifyFcrepoNotExists(rootResourceId); 445 446 verifyObjectRdf(rawContentToString(rootResourceId, PersistencePaths.rdfResource(rootResourceId, rootResourceId) 447 .getContentFilePath())); 448 449 verifyBinary(rawContentVersionToString(rootResourceId, 450 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId1)).getContentFilePath(), 451 "v1"), ds1V1); 452 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 453 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId1)).getContentFilePath(), 454 "v1"), ds1V1); 455 456 verifyBinary(rawContentVersionToString(rootResourceId, 457 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)).getContentFilePath(), 458 "v1"), ds2V1); 459 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 460 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath(), 461 "v1"), ds2V1); 462 463 verifyBinary(rawContentVersionToString(rootResourceId, 464 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)).getContentFilePath(), 465 "v2"), ds2V2); 466 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 467 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath(), 468 "v2"), ds2V2); 469 470 rawVerifyDoesNotExist(rootResourceId, 471 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)) 472 .getContentFilePath()); 473 rawVerifyDoesNotExist(rootResourceId, 474 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath()); 475 } 476 477 @Test 478 public void processObjectMultipleVersionsWithInactiveDeletedObjectPlainFormat() throws IOException { 479 final var handler = createHandler(MigrationType.PLAIN_OCFL, false, true); 480 481 final var pid = "obj2"; 482 final var dsId1 = "ds3"; 483 final var dsId2 = "ds4"; 484 485 final var ds1V1 = datastreamVersion(dsId1, true, MANAGED, "application/xml", "<h1>hello</h1>", null); 486 final var ds2V1 = datastreamVersion(dsId2, true, MANAGED, "text/plain", "goodbye", null); 487 488 final var ds2V2 = datastreamVersion(dsId2, false, MANAGED, "text/plain", "fedora", null); 489 490 handler.processObjectVersions(List.of( 491 objectVersionReference(pid, true, OBJ_INACTIVE, List.of(ds1V1, ds2V1)), 492 objectVersionReference(pid, false, OBJ_INACTIVE, List.of(ds2V2)) 493 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 494 495 final var rootResourceId = addPrefix(pid); 496 497 verifyFcrepoNotExists(rootResourceId); 498 499 verifyObjectRdf(rawContentVersionToString(rootResourceId, 500 PersistencePaths.rdfResource(rootResourceId, rootResourceId).getContentFilePath(), "v1")); 501 502 verifyBinary(rawContentVersionToString(rootResourceId, 503 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId1)).getContentFilePath(), 504 "v1"), ds1V1); 505 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 506 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId1)).getContentFilePath(), 507 "v1"), ds1V1); 508 509 verifyBinary(rawContentVersionToString(rootResourceId, 510 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)).getContentFilePath(), 511 "v1"), ds2V1); 512 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 513 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath(), 514 "v1"), ds2V1); 515 516 verifyBinary(rawContentVersionToString(rootResourceId, 517 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)).getContentFilePath(), 518 "v2"), ds2V2); 519 verifyPlainDescRdf(rawContentVersionToString(rootResourceId, 520 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath(), 521 "v2"), ds2V2); 522 523 rawVerifyDoesNotExist(rootResourceId, PersistencePaths.rdfResource(rootResourceId, rootResourceId) 524 .getContentFilePath()); 525 526 rawVerifyDoesNotExist(rootResourceId, 527 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId1)) 528 .getContentFilePath()); 529 rawVerifyDoesNotExist(rootResourceId, 530 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId1)).getContentFilePath()); 531 532 rawVerifyDoesNotExist(rootResourceId, 533 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)) 534 .getContentFilePath()); 535 rawVerifyDoesNotExist(rootResourceId, 536 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2)).getContentFilePath()); 537 } 538 539 @Test 540 public void processObjectSingleVersionF6FormatWithExtensions() throws IOException { 541 final var handler = createHandler(MigrationType.FEDORA_OCFL, true, false); 542 543 final var pid = "obj1"; 544 final var dsId1 = "ds1"; 545 final var dsId1Ext = "ds1.txt"; 546 final var dsId2 = "ds2"; 547 final var dsId2Ext = "ds2.rdf"; 548 final var dsId3 = "ds3"; 549 final var dsId3Ext = "ds3.jpg"; 550 551 final var ds1 = datastreamVersion(dsId1, true, MANAGED, "text/plain", "text", null); 552 final var ds2 = datastreamVersion(dsId2, true, MANAGED, "application/rdf+xml", "xml", null); 553 final var ds3 = datastreamVersion(dsId3, true, MANAGED, "image/jpeg", "image", null); 554 555 handler.processObjectVersions(List.of( 556 objectVersionReference(pid, true, List.of(ds1, ds2, ds3)) 557 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 558 559 final var ocflObjectId = addPrefix(pid); 560 final var session = sessionFactory.newSession(ocflObjectId); 561 562 verifyObjectRdf(contentToString(session, ocflObjectId)); 563 verifyObjectHeaders(session, ocflObjectId); 564 565 verifyBinary(contentToString(session, ocflObjectId, dsId1Ext), ds1); 566 verifyHeaders(session, ocflObjectId, dsId1Ext, ds1); 567 verifyDescRdf(session, ocflObjectId, dsId1Ext, ds1); 568 verifyDescHeaders(session, ocflObjectId, dsId1Ext); 569 570 verifyBinary(contentToString(session, ocflObjectId, dsId2Ext), ds2); 571 verifyHeaders(session, ocflObjectId, dsId2Ext, ds2); 572 verifyDescRdf(session, ocflObjectId, dsId2Ext, ds2); 573 verifyDescHeaders(session, ocflObjectId, dsId2Ext); 574 575 verifyBinary(contentToString(session, ocflObjectId, dsId3Ext), ds3); 576 verifyHeaders(session, ocflObjectId, dsId3Ext, ds3); 577 verifyDescRdf(session, ocflObjectId, dsId3Ext, ds3); 578 verifyDescHeaders(session, ocflObjectId, dsId3Ext); 579 } 580 581 @Test 582 public void processObjectSingleVersionPlainFormatWithExtensions() throws IOException { 583 final var handler = createHandler(MigrationType.PLAIN_OCFL, true, false); 584 585 final var pid = "obj1"; 586 final var dsId1 = "ds1"; 587 final var dsId1Ext = "ds1.txt"; 588 final var dsId2 = "ds2"; 589 final var dsId2Ext = "ds2.rdf"; 590 final var dsId3 = "ds3"; 591 final var dsId3Ext = "ds3.jpg"; 592 593 final var ds1 = datastreamVersion(dsId1, true, MANAGED, "text/plain", "text", null); 594 final var ds2 = datastreamVersion(dsId2, true, MANAGED, "application/rdf+xml", "xml", null); 595 final var ds3 = datastreamVersion(dsId3, true, MANAGED, "image/jpeg", "image", null); 596 597 handler.processObjectVersions(List.of( 598 objectVersionReference(pid, true, List.of(ds1, ds2, ds3)) 599 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 600 601 final var rootResourceId = addPrefix(pid); 602 603 verifyFcrepoNotExists(rootResourceId); 604 605 verifyObjectRdf(rawContentToString(rootResourceId, PersistencePaths.rdfResource(rootResourceId, rootResourceId) 606 .getContentFilePath())); 607 608 verifyBinary(rawContentToString(rootResourceId, 609 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId1Ext)) 610 .getContentFilePath()), 611 ds1); 612 verifyPlainDescRdf(rawContentToString(rootResourceId, 613 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId1Ext)) 614 .getContentFilePath()), 615 ds1); 616 617 verifyBinary(rawContentToString(rootResourceId, 618 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2Ext)) 619 .getContentFilePath()), 620 ds2); 621 verifyPlainDescRdf(rawContentToString(rootResourceId, 622 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId2Ext)) 623 .getContentFilePath()), 624 ds2); 625 626 verifyBinary(rawContentToString(rootResourceId, 627 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId3Ext)) 628 .getContentFilePath()), 629 ds3); 630 verifyPlainDescRdf(rawContentToString(rootResourceId, 631 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId3Ext)) 632 .getContentFilePath()), 633 ds3); 634 } 635 636 @Test 637 public void processObjectSingleVersionF6FormatWithExternalBinary() throws IOException { 638 final var handler = createHandler(MigrationType.FEDORA_OCFL, false, false); 639 640 final var pid = "obj1"; 641 final var dsId1 = "ds1"; 642 final var dsId2 = "ds2"; 643 final var dsId3 = "ds3"; 644 645 final var ds1 = datastreamVersion(dsId1, true, MANAGED, "text/plain", "hello", null); 646 final var ds2 = datastreamVersion(dsId2, true, PROXY, "text/plain", "", "https://external"); 647 final var ds3 = datastreamVersion(dsId3, true, REDIRECT, "text/plain", "", "https://redirect"); 648 649 handler.processObjectVersions(List.of( 650 objectVersionReference(pid, true, List.of(ds1, ds2, ds3)) 651 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 652 653 final var ocflObjectId = addPrefix(pid); 654 final var session = sessionFactory.newSession(ocflObjectId); 655 656 verifyObjectRdf(contentToString(session, ocflObjectId)); 657 verifyObjectHeaders(session, ocflObjectId); 658 659 verifyBinary(contentToString(session, ocflObjectId, dsId1), ds1); 660 verifyHeaders(session, ocflObjectId, dsId1, ds1); 661 verifyDescRdf(session, ocflObjectId, dsId1, ds1); 662 verifyDescHeaders(session, ocflObjectId, dsId1); 663 664 verifyContentNotExists(session, resourceId(ocflObjectId, dsId2)); 665 verifyHeaders(session, ocflObjectId, dsId2, ds2); 666 verifyDescRdf(session, ocflObjectId, dsId2, ds2); 667 verifyDescHeaders(session, ocflObjectId, dsId2); 668 669 verifyContentNotExists(session, resourceId(ocflObjectId, dsId3)); 670 verifyHeaders(session, ocflObjectId, dsId3, ds3); 671 verifyDescRdf(session, ocflObjectId, dsId3, ds3); 672 verifyDescHeaders(session, ocflObjectId, dsId3); 673 } 674 675 @Test 676 public void processObjectSingleVersionPlainFormatWithExternalBinary() throws IOException { 677 final var handler = createHandler(MigrationType.PLAIN_OCFL, false, false); 678 679 final var pid = "obj1"; 680 final var dsId1 = "ds1"; 681 final var dsId2 = "ds2"; 682 final var dsId3 = "ds3"; 683 684 final var ds1 = datastreamVersion(dsId1, true, MANAGED, "text/plain", "hello", null); 685 final var ds2 = datastreamVersion(dsId2, true, PROXY, "text/plain", "", "https://external"); 686 final var ds3 = datastreamVersion(dsId3, true, REDIRECT, "text/plain", "", "https://redirect"); 687 688 handler.processObjectVersions(List.of( 689 objectVersionReference(pid, true, List.of(ds1, ds2, ds3)) 690 ), new DefaultObjectInfo(pid, pid, Files.createTempFile(tempDir.getRoot().toPath(), "foxml", "xml"))); 691 692 final var rootResourceId = addPrefix(pid); 693 694 verifyFcrepoNotExists(rootResourceId); 695 696 verifyObjectRdf(rawContentToString(rootResourceId, PersistencePaths.rdfResource(rootResourceId, rootResourceId) 697 .getContentFilePath())); 698 699 verifyBinary(rawContentToString(rootResourceId, 700 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId1)) 701 .getContentFilePath()), 702 ds1); 703 verifyPlainDescRdf(rawContentToString(rootResourceId, 704 PersistencePaths.rdfResource(rootResourceId, metadataId(rootResourceId, dsId1)).getContentFilePath()), 705 ds1); 706 707 verifyBinary(rawContentToString(rootResourceId, 708 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId2)) 709 .getContentFilePath()), 710 ds2); 711 712 verifyBinary(rawContentToString(rootResourceId, 713 PersistencePaths.nonRdfResource(rootResourceId, resourceId(rootResourceId, dsId3)) 714 .getContentFilePath()), 715 ds3); 716 } 717 718 private void verifyBinary(final String content, final DatastreamVersion datastreamVersion) { 719 try { 720 if ("RE".contains(datastreamVersion.getDatastreamInfo().getControlGroup())) { 721 assertEquals(datastreamVersion.getExternalOrRedirectURL(), content); 722 } else { 723 assertEquals(IOUtils.toString(datastreamVersion.getContent()), content); 724 } 725 } catch (IOException e) { 726 throw new UncheckedIOException(e); 727 } 728 } 729 730 private void verifyHeaders(final OcflObjectSession session, 731 final String ocflObjectId, 732 final String dsId, 733 final DatastreamVersion datastreamVersion) { 734 verifyHeaders(session, ocflObjectId, dsId, datastreamVersion, null); 735 } 736 737 private void verifyHeaders(final OcflObjectSession session, 738 final String ocflObjectId, 739 final String dsId, 740 final DatastreamVersion datastreamVersion, 741 final String versionNumber) { 742 final var resourceId = resourceId(ocflObjectId, dsId); 743 try (final var content = session.readContent(resourceId, versionNumber)) { 744 final var headers = content.getHeaders(); 745 assertEquals(ResourceHeadersVersion.V1_0, headers.getHeadersVersion()); 746 assertEquals(resourceId, headers.getId()); 747 assertEquals(ocflObjectId, headers.getParent()); 748 assertEquals(ocflObjectId, headers.getArchivalGroupId()); 749 assertEquals(InteractionModel.NON_RDF.getUri(), headers.getInteractionModel()); 750 assertFalse("not AG", headers.isArchivalGroup()); 751 assertFalse("not root", headers.isObjectRoot()); 752 assertFalse("not deleted", headers.isDeleted()); 753 assertEquals(USER, headers.getCreatedBy()); 754 assertEquals(USER, headers.getLastModifiedBy()); 755 assertThat(headers.getLastModifiedDate().toString(), containsString(date)); 756 assertThat(headers.getMementoCreatedDate().toString(), containsString(date)); 757 assertThat(headers.getCreatedDate().toString(), containsString(date)); 758 if (INLINE.equals(datastreamVersion.getDatastreamInfo().getControlGroup())) { 759 assertNotEquals(datastreamVersion.getSize(), headers.getContentSize()); 760 } else { 761 assertEquals(datastreamVersion.getSize(), headers.getContentSize()); 762 } 763 assertEquals(datastreamVersion.getMimeType(), headers.getMimeType()); 764 assertEquals(dsId, headers.getFilename()); 765 assertEquals(DigestUtils.md5Hex( 766 String.valueOf(Instant.parse(datastreamVersion.getCreated()).toEpochMilli())).toUpperCase(), 767 headers.getStateToken()); 768 if (headers.getExternalHandling() != null) { 769 assertEquals(1, headers.getDigests().size()); 770 } else { 771 assertEquals(2, headers.getDigests().size()); 772 } 773 assertEquals(datastreamVersion.getExternalOrRedirectURL(), headers.getExternalUrl()); 774 if (Objects.equals(REDIRECT, datastreamVersion.getDatastreamInfo().getControlGroup())) { 775 assertEquals("redirect", headers.getExternalHandling()); 776 } else if (Objects.equals(PROXY, datastreamVersion.getDatastreamInfo().getControlGroup())) { 777 assertEquals("proxy", headers.getExternalHandling()); 778 } 779 } catch (Exception e) { 780 throw new RuntimeException(e); 781 } 782 } 783 784 private void verifyDescHeaders(final OcflObjectSession session, 785 final String ocflObjectId, 786 final String dsId) { 787 verifyDescHeaders(session, ocflObjectId, dsId, null); 788 } 789 790 private void verifyDescHeaders(final OcflObjectSession session, 791 final String ocflObjectId, 792 final String dsId, 793 final String versionNumber) { 794 try (final var content = session.readContent(metadataId(ocflObjectId, dsId), versionNumber)) { 795 final var headers = content.getHeaders(); 796 assertEquals(ResourceHeadersVersion.V1_0, headers.getHeadersVersion()); 797 assertEquals(metadataId(ocflObjectId, dsId), headers.getId()); 798 assertEquals(resourceId(ocflObjectId, dsId), headers.getParent()); 799 assertEquals(ocflObjectId, headers.getArchivalGroupId()); 800 assertEquals(InteractionModel.NON_RDF_DESCRIPTION.getUri(), headers.getInteractionModel()); 801 assertFalse("not AG", headers.isArchivalGroup()); 802 assertFalse("not root", headers.isObjectRoot()); 803 assertFalse("not deleted", headers.isDeleted()); 804 assertEquals(USER, headers.getCreatedBy()); 805 assertEquals(USER, headers.getLastModifiedBy()); 806 assertThat(headers.getLastModifiedDate().toString(), containsString(date)); 807 assertThat(headers.getMementoCreatedDate().toString(), containsString(date)); 808 assertThat(headers.getCreatedDate().toString(), containsString(date)); 809 } catch (Exception e) { 810 throw new RuntimeException(e); 811 } 812 } 813 814 private void verifyObjectHeaders(final OcflObjectSession session, 815 final String ocflObjectId) { 816 verifyObjectHeaders(session, ocflObjectId, null); 817 } 818 819 private void verifyObjectHeaders(final OcflObjectSession session, 820 final String ocflObjectId, 821 final String versionNumber) { 822 try (final var content = session.readContent(ocflObjectId, versionNumber)) { 823 final var headers = content.getHeaders(); 824 assertEquals(ResourceHeadersVersion.V1_0, headers.getHeadersVersion()); 825 assertEquals(ocflObjectId, headers.getId()); 826 assertEquals(FCREPO_ROOT, headers.getParent()); 827 assertEquals(InteractionModel.BASIC_CONTAINER.getUri(), headers.getInteractionModel()); 828 assertTrue("is AG", headers.isArchivalGroup()); 829 assertTrue("is root", headers.isObjectRoot()); 830 assertFalse("not deleted", headers.isDeleted()); 831 assertEquals(USER, headers.getCreatedBy()); 832 assertEquals(USER, headers.getLastModifiedBy()); 833 assertThat(headers.getLastModifiedDate().toString(), containsString(date)); 834 assertThat(headers.getMementoCreatedDate().toString(), containsString(date)); 835 assertThat(headers.getCreatedDate().toString(), containsString(date)); 836 } catch (Exception e) { 837 throw new RuntimeException(e); 838 } 839 } 840 841 private void verifyDescRdf(final OcflObjectSession session, 842 final String ocflObjectId, 843 final String dsId, 844 final DatastreamVersion datastreamVersion) { 845 verifyDescRdf(session, ocflObjectId, dsId, datastreamVersion, null); 846 } 847 848 private void verifyDescRdf(final OcflObjectSession session, 849 final String ocflObjectId, 850 final String dsId, 851 final DatastreamVersion datastreamVersion, 852 final String versionNumber) { 853 try (final var content = session.readContent(metadataId(ocflObjectId, dsId), versionNumber)) { 854 final var value = IOUtils.toString(content.getContentStream().get()); 855 assertThat(value, allOf( 856 containsString(datastreamVersion.getLabel()), 857 containsString(datastreamVersion.getFormatUri()), 858 containsString("objState") 859 )); 860 } catch (Exception e) { 861 throw new RuntimeException(e); 862 } 863 } 864 865 private void verifyPlainDescRdf(final String content, 866 final DatastreamVersion datastreamVersion) { 867 assertThat(content, allOf( 868 containsString(datastreamVersion.getLabel()), 869 containsString(datastreamVersion.getFormatUri()), 870 containsString(datastreamVersion.getMimeType()), 871 containsString(datastreamVersion.getDatastreamInfo().getDatastreamId()), 872 containsString("objState"), 873 containsString("hasMessageDigest"), 874 containsString("lastModified"), 875 containsString(date), 876 containsString("created"))); 877 } 878 879 private void verifyObjectRdf(final String content) { 880 assertThat(content, allOf( 881 containsString("lastModifiedDate"), 882 containsString(date), 883 containsString("createdDate") 884 )); 885 } 886 887 private String contentToString(final OcflObjectSession session, final String ocflObjectId) { 888 return contentVersionToString(session, ocflObjectId, null); 889 } 890 891 private String contentVersionToString(final OcflObjectSession session, 892 final String ocflObjectId, 893 final String versionNumber) { 894 try (final var content = session.readContent(ocflObjectId, versionNumber)) { 895 return IOUtils.toString(content.getContentStream().get()); 896 } catch (IOException e) { 897 throw new UncheckedIOException(e); 898 } 899 } 900 901 private String contentToString(final OcflObjectSession session, final String ocflObjectId, final String dsId) { 902 return contentVersionToString(session, ocflObjectId, dsId, null); 903 } 904 905 private String contentVersionToString(final OcflObjectSession session, 906 final String ocflObjectId, 907 final String dsId, 908 final String versionNumber) { 909 try (final var content = session.readContent(resourceId(ocflObjectId, dsId), versionNumber)) { 910 return IOUtils.toString(content.getContentStream().get()); 911 } catch (IOException e) { 912 throw new UncheckedIOException(e); 913 } 914 } 915 916 private String rawContentToString(final String ocflObjectId, final String path) { 917 try (final var content = ocflRepo.getObject(ObjectVersionId.head(ocflObjectId)) 918 .getFile(path).getStream()) { 919 return IOUtils.toString(content); 920 } catch (IOException e) { 921 throw new UncheckedIOException(e); 922 } 923 } 924 925 private String rawContentVersionToString(final String ocflObjectId, final String path, final String versionNumber) { 926 try (final var content = ocflRepo.getObject( 927 ObjectVersionId.version(ocflObjectId, versionNumber)).getFile(path).getStream()) { 928 return IOUtils.toString(content); 929 } catch (IOException e) { 930 throw new UncheckedIOException(e); 931 } 932 } 933 934 private void rawVerifyDoesNotExist(final String ocflObjectId, final String path) { 935 assertFalse(String.format("object %s not contain path %s", ocflObjectId, path), 936 ocflRepo.describeVersion(ObjectVersionId.head(ocflObjectId)).containsFile(path)); 937 } 938 939 private ArchiveGroupHandler createHandler(final MigrationType migrationType, 940 final boolean addExtensions, 941 final boolean deleteInactive) { 942 if (migrationType == MigrationType.PLAIN_OCFL) { 943 return new ArchiveGroupHandler(plainSessionFactory, migrationType, addExtensions, deleteInactive, 944 false, USER,"info:fedora/", false); 945 } else { 946 return new ArchiveGroupHandler(sessionFactory, migrationType, addExtensions, deleteInactive, false, USER, 947 "info:fedora/", false); 948 } 949 } 950 951 private ObjectVersionReference objectVersionReference(final String pid, 952 final boolean isFirst, 953 final List<DatastreamVersion> datastreamVersions) 954 throws IOException { 955 return objectVersionReference(pid, isFirst, OBJ_ACTIVE, datastreamVersions); 956 } 957 958 private ObjectVersionReference objectVersionReference(final String pid, 959 final boolean isFirst, 960 final String state, 961 final List<DatastreamVersion> datastreamVersions) 962 throws IOException { 963 final var mock = Mockito.mock(ObjectVersionReference.class); 964 when(mock.isFirstVersion()).thenReturn(isFirst); 965 if (isFirst) { 966 final var properties = objectProperties(List.of( 967 objectProperty("info:fedora/fedora-system:def/view#lastModifiedDate", Instant.now().toString()), 968 objectProperty("info:fedora/fedora-system:def/model#createdDate", Instant.now().toString()), 969 objectProperty("info:fedora/fedora-system:def/model#state", state) 970 )); 971 when(mock.getObjectProperties()).thenReturn(properties); 972 } 973 when(mock.getObject()).thenReturn(null); 974 when(mock.listChangedDatastreams()).thenReturn(datastreamVersions); 975 when(mock.getVersionDate()).thenReturn(Instant.now().toString()); 976 return mock; 977 } 978 979 private ObjectProperties objectProperties(final List<? extends ObjectProperty> properties) { 980 final var mock = Mockito.mock(ObjectProperties.class); 981 doReturn(properties).when(mock).listProperties(); 982 return mock; 983 } 984 985 private ObjectProperty objectProperty(final String name, final String value) { 986 final var mock = Mockito.mock(ObjectProperty.class); 987 when(mock.getName()).thenReturn(name); 988 when(mock.getValue()).thenReturn(value); 989 return mock; 990 } 991 992 private DatastreamVersion datastreamVersion(final String datastreamId, 993 final boolean isFirst, 994 final String controlGroup, 995 final String mimeType, 996 final String content, 997 final String externalUrl) { 998 return datastreamVersion(datastreamId, isFirst, controlGroup, mimeType, content, DS_ACTIVE, externalUrl); 999 } 1000 1001 private DatastreamVersion datastreamVersion(final String datastreamId, 1002 final boolean isFirst, 1003 final String controlGroup, 1004 final String mimeType, 1005 final String content, 1006 final String state, 1007 final String externalUrl) { 1008 final var mock = Mockito.mock(DatastreamVersion.class); 1009 final var info = datastreamInfo(datastreamId, controlGroup, state); 1010 when(mock.getDatastreamInfo()).thenReturn(info); 1011 when(mock.getMimeType()).thenReturn(mimeType); 1012 try { 1013 when(mock.getContent()).thenAnswer((Answer<InputStream>) invocation -> { 1014 return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); 1015 }); 1016 } catch (IOException e) { 1017 throw new UncheckedIOException(e); 1018 } 1019 when(mock.isFirstVersionIn(Mockito.isNull())).thenReturn(isFirst); 1020 when(mock.getCreated()).thenReturn(Instant.now().toString()); 1021 when(mock.getExternalOrRedirectURL()).thenReturn(externalUrl); 1022 when(mock.getSize()).thenReturn((long) content.length()); 1023 final var contentDigest = contentDigest(content); 1024 when(mock.getContentDigest()).thenReturn(contentDigest); 1025 when(mock.getLabel()).thenReturn(datastreamId + "-label"); 1026 when(mock.getFormatUri()).thenReturn("http://format-id"); 1027 return mock; 1028 } 1029 1030 private DatastreamInfo datastreamInfo(final String datastreamId, final String controlGroup, final String state) { 1031 final var mock = Mockito.mock(DatastreamInfo.class); 1032 when(mock.getDatastreamId()).thenReturn(datastreamId); 1033 when(mock.getControlGroup()).thenReturn(controlGroup); 1034 when(mock.getState()).thenReturn(state); 1035 return mock; 1036 } 1037 1038 private ContentDigest contentDigest(final String content) { 1039 final var mock = Mockito.mock(ContentDigest.class); 1040 when(mock.getType()).thenReturn("md5"); 1041 when(mock.getDigest()).thenReturn(DigestUtils.md5Hex(content)); 1042 return mock; 1043 } 1044 1045 private String addPrefix(final String pid) { 1046 return FCREPO_ROOT + pid; 1047 } 1048 1049 private String resourceId(final String ocflObjectId, final String dsId) { 1050 return ocflObjectId + "/" + dsId; 1051 } 1052 1053 private String metadataId(final String ocflObjectId, final String dsId) { 1054 return resourceId(ocflObjectId, dsId) + "/fcr:metadata"; 1055 } 1056 1057 private void verifyFcrepoNotExists(final String ocflObjectId) { 1058 final var count = ocflRepo.describeVersion(ObjectVersionId.head(ocflObjectId)).getFiles().stream() 1059 .map(FileDetails::getPath) 1060 .filter(file -> file.startsWith(".fcrepo/")) 1061 .count(); 1062 assertEquals(0, count); 1063 } 1064 1065 private void verifyContentNotExists(final OcflObjectSession session, final String ocflObjectId) { 1066 try (final var content = session.readContent(ocflObjectId)) { 1067 assertTrue("Content should not exist", content.getContentStream().isEmpty()); 1068 } catch (IOException e) { 1069 throw new UncheckedIOException(e); 1070 } 1071 } 1072 1073 private void verifyResourceDeleted(final OcflObjectSession session, final String ocflObjectId) { 1074 final var headers = session.readHeaders(ocflObjectId); 1075 assertTrue("resource " + ocflObjectId + " should be deleted", headers.isDeleted()); 1076 verifyContentNotExists(session, ocflObjectId); 1077 } 1078 1079}