001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.kernel.impl; 019 020import static java.time.ZoneOffset.UTC; 021import static java.util.stream.Collectors.toList; 022import static org.fcrepo.kernel.api.services.VersionService.MEMENTO_LABEL_FORMATTER; 023import static org.junit.Assert.assertEquals; 024import static org.junit.Assert.assertFalse; 025import static org.junit.Assert.assertNotEquals; 026import static org.junit.Assert.assertNotNull; 027import static org.junit.Assert.assertNull; 028import static org.junit.Assert.assertTrue; 029import static org.mockito.Mockito.when; 030 031import java.time.Instant; 032import java.util.ArrayList; 033import java.util.HashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.UUID; 037import java.util.concurrent.TimeUnit; 038 039import javax.inject.Inject; 040 041import org.fcrepo.kernel.api.Transaction; 042import org.fcrepo.kernel.api.identifiers.FedoraId; 043import org.fcrepo.kernel.api.models.FedoraResource; 044 045import org.flywaydb.test.FlywayTestExecutionListener; 046import org.flywaydb.test.annotation.FlywayTest; 047import org.junit.Before; 048import org.junit.Rule; 049import org.junit.Test; 050import org.junit.runner.RunWith; 051import org.mockito.Mock; 052import org.mockito.junit.MockitoJUnit; 053import org.mockito.junit.MockitoRule; 054import org.springframework.test.context.ContextConfiguration; 055import org.springframework.test.context.TestExecutionListeners; 056import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 057import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; 058 059/** 060 * @author peichman 061 */ 062@RunWith(SpringJUnit4ClassRunner.class) 063@ContextConfiguration("/containmentIndexTest.xml") 064@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, FlywayTestExecutionListener.class }) 065public class ContainmentIndexImplTest { 066 067 @Mock 068 private FedoraResource parent1; 069 070 @Mock 071 private FedoraResource child1; 072 073 @Mock 074 private FedoraResource parent2; 075 076 @Mock 077 private FedoraResource child2; 078 079 private Transaction transaction1; 080 081 private Transaction transaction2; 082 083 private Transaction shortLivedTx; 084 085 @Inject 086 private ContainmentIndexImpl containmentIndex; 087 088 @Rule 089 public MockitoRule rule = MockitoJUnit.rule().silent(); 090 091 private final Map<String, FedoraResource> id_to_resource = new HashMap<>(); 092 private final Map<String, Transaction> id_to_transaction = new HashMap<>(); 093 094 @Before 095 @FlywayTest 096 public void setUp() { 097 id_to_resource.put("parent1", parent1); 098 id_to_resource.put("parent2", parent2); 099 id_to_resource.put("child1", child1); 100 id_to_resource.put("child2", child2); 101 102 transaction1 = TestTransactionHelper.mockTransaction("transaction1", false); 103 transaction2 = TestTransactionHelper.mockTransaction("transaction2", false); 104 shortLivedTx = TestTransactionHelper.mockTransaction("shortLived", true); 105 106 id_to_transaction.put("transaction1", transaction1); 107 id_to_transaction.put("transaction2", transaction2); 108 } 109 110 /** 111 * Utility method to make it easier to stub the getFedoraId() method and avoid MockitoHints. 112 * @param id The resource|transaction ID/name 113 */ 114 private void stubObject(final String id) { 115 // Use unique ids for resources and transactions. 116 final String uuid = UUID.randomUUID().toString(); 117 if (id_to_resource.containsKey(id)) { 118 final FedoraId fID = FedoraId.create(uuid); 119 when(id_to_resource.get(id).getFedoraId()).thenReturn(fID); 120 } else if (id_to_transaction.containsKey(id)) { 121 when(id_to_transaction.get(id).getId()).thenReturn(uuid); 122 } 123 } 124 125 @Test 126 public void testAddChildInTransaction() { 127 stubObject("parent1"); 128 stubObject("child1"); 129 stubObject("transaction1"); 130 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 131 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 132 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 133 assertEquals(child1.getFedoraId().getFullId(), 134 containmentIndex.getContains(transaction1, parent1.getFedoraId()).findFirst().get()); 135 assertEquals(parent1.getFedoraId().getFullId(), 136 containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 137 // outside of the transaction, the containment shouldn't show up 138 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 139 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 140 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 141 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 142 } 143 144 @Test 145 public void testAddRemoveChildInSameTransaction() { 146 stubObject("parent1"); 147 stubObject("child1"); 148 stubObject("transaction1"); 149 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 150 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 151 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 152 assertEquals(0, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 153 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 154 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 155 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 156 assertEquals(parent1.getFedoraId().getFullId(), 157 containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 158 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 159 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 160 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 161 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 162 assertEquals(0, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 163 } 164 165 @Test 166 public void testAddRemoveChildInTwoTransactions() { 167 stubObject("parent1"); 168 stubObject("child1"); 169 stubObject("transaction1"); 170 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 171 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 172 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 173 assertEquals(0, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 174 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 175 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 176 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 177 assertEquals(parent1.getFedoraId().getFullId(), 178 containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 179 containmentIndex.commitTransaction(transaction1); 180 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 181 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 182 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 183 assertEquals(1, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 184 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 185 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 186 containmentIndex.commitTransaction(transaction1); 187 assertEquals(1, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 188 assertEquals(1, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 189 } 190 191 @Test 192 public void testAddRemovePurgeChildInTransaction() { 193 stubObject("parent1"); 194 stubObject("child1"); 195 stubObject("transaction1"); 196 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 197 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 198 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 199 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 200 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 201 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 202 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 203 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 204 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 205 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 206 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 207 assertEquals(0, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 208 containmentIndex.purgeResource(transaction1, child1.getFedoraId()); 209 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 210 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 211 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 212 assertEquals(0, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 213 } 214 215 @Test 216 public void testAddRemovePurgeChildThreeTransaction() { 217 stubObject("parent1"); 218 stubObject("child1"); 219 stubObject("transaction1"); 220 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 221 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 222 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 223 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 224 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 225 containmentIndex.commitTransaction(transaction1); 226 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 227 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 228 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 229 containmentIndex.commitTransaction(transaction1); 230 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 231 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 232 assertEquals(1, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 233 assertEquals(1, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 234 containmentIndex.purgeResource(transaction1, child1.getFedoraId()); 235 containmentIndex.commitTransaction(transaction1); 236 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 237 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 238 assertEquals(0, containmentIndex.getContainsDeleted(shortLivedTx, parent1.getFedoraId()).count()); 239 assertEquals(0, containmentIndex.getContainsDeleted(transaction1, parent1.getFedoraId()).count()); 240 } 241 242 @Test 243 public void testRollbackTransaction() { 244 stubObject("parent1"); 245 stubObject("child1"); 246 stubObject("transaction1"); 247 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 248 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 249 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 250 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 251 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 252 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 253 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 254 assertEquals(child1.getFedoraId().getFullId(), 255 containmentIndex.getContains(transaction1, parent1.getFedoraId()).findFirst().get()); 256 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 257 assertEquals(parent1.getFedoraId().getFullId(), 258 containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 259 containmentIndex.rollbackTransaction(transaction1); 260 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 261 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 262 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 263 assertNull(containmentIndex.getContainedBy(transaction1, child1.getFedoraId())); 264 } 265 266 @Test 267 public void testCommitTransaction() { 268 stubObject("parent1"); 269 stubObject("child2"); 270 stubObject("transaction1"); 271 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 272 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 273 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child2.getFedoraId()); 274 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 275 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 276 assertEquals(child2.getFedoraId().getFullId(), 277 containmentIndex.getContains(transaction1, parent1.getFedoraId()).findFirst().get()); 278 assertEquals(parent1.getFedoraId().getFullId(), 279 containmentIndex.getContainedBy(transaction1, child2.getFedoraId())); 280 assertNull(containmentIndex.getContainedBy(shortLivedTx, child2.getFedoraId())); 281 containmentIndex.commitTransaction(transaction1); 282 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 283 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 284 assertEquals(child2.getFedoraId().getFullId(), 285 containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).findFirst().get()); 286 assertEquals(parent1.getFedoraId().getFullId(), 287 containmentIndex.getContainedBy(transaction1, child2.getFedoraId())); 288 assertEquals(child2.getFedoraId().getFullId(), 289 containmentIndex.getContains(transaction1, parent1.getFedoraId()).findFirst().get()); 290 assertEquals(parent1.getFedoraId().getFullId(), 291 containmentIndex.getContainedBy(shortLivedTx, child2.getFedoraId())); 292 } 293 294 @Test 295 public void testSwapContained() { 296 stubObject("parent1"); 297 stubObject("child1"); 298 stubObject("child2"); 299 stubObject("transaction1"); 300 stubObject("transaction2"); 301 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 302 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 303 containmentIndex.commitTransaction(transaction1); 304 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 305 assertEquals(child1.getFedoraId().getFullId(), 306 containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).findFirst().get()); 307 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child2.getFedoraId()); 308 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 309 // Still the same outside 310 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 311 assertEquals(child1.getFedoraId().getFullId(), 312 containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).findFirst().get()); 313 // Still the same in a different transaction 314 assertEquals(1, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 315 assertEquals(child1.getFedoraId().getFullId(), 316 containmentIndex.getContains(transaction2, parent1.getFedoraId()).findFirst().get()); 317 // Inside it has changed 318 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 319 assertEquals(child2.getFedoraId().getFullId(), 320 containmentIndex.getContains(transaction1, parent1.getFedoraId()).findFirst().get()); 321 containmentIndex.commitTransaction(transaction1); 322 // After commit() it is the same outside transactions. 323 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 324 assertEquals(child2.getFedoraId().getFullId(), 325 containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).findFirst().get()); 326 // And now the same in a different transaction 327 assertEquals(1, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 328 assertEquals(child2.getFedoraId().getFullId(), 329 containmentIndex.getContains(transaction2, parent1.getFedoraId()).findFirst().get()); 330 } 331 332 @Test 333 public void testOnlyCommitOne() throws Exception { 334 stubObject("parent1"); 335 stubObject("child1"); 336 stubObject("child2"); 337 stubObject("transaction1"); 338 stubObject("transaction2"); 339 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 340 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 341 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child2.getFedoraId()); 342 containmentIndex.commitTransaction(transaction1); 343 assertEquals(2, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 344 // Delete one object in separate transactions. 345 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 346 containmentIndex.removeContainedBy(transaction2, parent1.getFedoraId(), child2.getFedoraId()); 347 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 348 assertEquals(1, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 349 containmentIndex.commitTransaction(transaction1); 350 // Wait a second because containment end time is second accuracy. 351 TimeUnit.SECONDS.sleep(1); 352 // Now only one record was removed 353 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 354 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 355 // Except in the second transaction as it should now have 0 356 assertEquals(0, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 357 containmentIndex.commitTransaction(transaction2); 358 // Wait a second because containment end time is second accuracy. 359 TimeUnit.SECONDS.sleep(1); 360 // Now all are gone 361 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 362 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 363 assertEquals(0, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 364 } 365 366 @Test 367 public void testTwoTransactionDeleteSameChild() { 368 stubObject("parent1"); 369 stubObject("child1"); 370 stubObject("child2"); 371 stubObject("transaction1"); 372 stubObject("transaction2"); 373 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 374 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 375 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child2.getFedoraId()); 376 containmentIndex.commitTransaction(transaction1); 377 assertEquals(2, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 378 // Delete one object in separate transactions. 379 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 380 containmentIndex.removeContainedBy(transaction2, parent1.getFedoraId(), child1.getFedoraId()); 381 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 382 assertEquals(1, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 383 containmentIndex.commitTransaction(transaction1); 384 // Now only one record was removed 385 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 386 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 387 assertEquals(1, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 388 containmentIndex.commitTransaction(transaction2); 389 // No change as the first transaction already committed. 390 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 391 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 392 assertEquals(1, containmentIndex.getContains(transaction2, parent1.getFedoraId()).count()); 393 } 394 395 @Test 396 public void testContainedByTwoSameTransactionException() { 397 stubObject("parent1"); 398 stubObject("parent2"); 399 stubObject("child1"); 400 stubObject("transaction1"); 401 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 402 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent2.getFedoraId()).count()); 403 // Add it to the first parent. 404 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 405 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 406 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent2.getFedoraId()).count()); 407 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 408 assertEquals(0, containmentIndex.getContains(transaction1, parent2.getFedoraId()).count()); 409 // When you add it to the second parent, it is altered and the first relationship is overwritten. 410 containmentIndex.addContainedBy(transaction1, parent2.getFedoraId(), child1.getFedoraId()); 411 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 412 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent2.getFedoraId()).count()); 413 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 414 assertEquals(1, containmentIndex.getContains(transaction1, parent2.getFedoraId()).count()); 415 containmentIndex.commitTransaction(transaction1); 416 // This should be rolled back so the additions should still be in the transaction operation table. 417 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 418 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent2.getFedoraId()).count()); 419 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 420 assertEquals(1, containmentIndex.getContains(transaction1, parent2.getFedoraId()).count()); 421 } 422 423 @Test 424 public void testExistsOutsideTransaction() { 425 stubObject("parent1"); 426 stubObject("child1"); 427 stubObject("transaction1"); 428 stubObject("transaction2"); 429 assertFalse(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 430 assertFalse(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 431 containmentIndex.addContainedBy(transaction2, parent1.getFedoraId(), child1.getFedoraId()); 432 containmentIndex.commitTransaction(transaction2); 433 assertTrue(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 434 assertTrue(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 435 } 436 437 @Test 438 public void testExistsInsideTransaction() { 439 stubObject("parent1"); 440 stubObject("child1"); 441 stubObject("transaction1"); 442 stubObject("transaction2"); 443 assertFalse(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 444 assertFalse(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 445 assertFalse(containmentIndex.resourceExists(transaction2, child1.getFedoraId(), false)); 446 // Only visible in the transaction. 447 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 448 assertFalse(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 449 assertTrue(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 450 assertFalse(containmentIndex.resourceExists(transaction2, child1.getFedoraId(), false)); 451 // Rollback transaction. 452 containmentIndex.rollbackTransaction(transaction1); 453 assertFalse(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 454 assertFalse(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 455 assertFalse(containmentIndex.resourceExists(transaction2, child1.getFedoraId(), false)); 456 // Add again in transaction. 457 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 458 assertFalse(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 459 assertTrue(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 460 assertFalse(containmentIndex.resourceExists(transaction2, child1.getFedoraId(), false)); 461 // Commit and visible everywhere. 462 containmentIndex.commitTransaction(transaction1); 463 assertTrue(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 464 assertTrue(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 465 assertTrue(containmentIndex.resourceExists(transaction2, child1.getFedoraId(), false)); 466 } 467 468 @Test 469 public void testRemoveResource() { 470 stubObject("parent1"); 471 stubObject("child1"); 472 stubObject("transaction1"); 473 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 474 containmentIndex.commitTransaction(transaction1); 475 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 476 assertEquals(parent1.getFedoraId().getFullId(), 477 containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 478 containmentIndex.removeResource(transaction1, child1.getFedoraId()); 479 containmentIndex.commitTransaction(transaction1); 480 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 481 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 482 } 483 484 @Test 485 public void testRemoveNotFromTransaction() { 486 stubObject("parent1"); 487 stubObject("child1"); 488 stubObject("parent2"); 489 stubObject("transaction1"); 490 stubObject("transaction2"); 491 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 492 containmentIndex.addContainedBy(transaction2, parent1.getFedoraId(), child1.getFedoraId()); 493 containmentIndex.commitTransaction(transaction2); 494 containmentIndex.addContainedBy(transaction1, parent2.getFedoraId(), child1.getFedoraId()); 495 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 496 assertEquals(parent1.getFedoraId().getFullId(), 497 containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 498 assertEquals(1, containmentIndex.getContains(transaction1, parent2.getFedoraId()).count()); 499 assertEquals(child1.getFedoraId().getFullId(), 500 containmentIndex.getContains(transaction1, parent2.getFedoraId()).findFirst().get()); 501 containmentIndex.removeResource(transaction2, child1.getFedoraId()); 502 containmentIndex.commitTransaction(transaction2); 503 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 504 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 505 assertEquals(1, containmentIndex.getContains(transaction1, parent2.getFedoraId()).count()); 506 assertEquals(child1.getFedoraId().getFullId(), 507 containmentIndex.getContains(transaction1, parent2.getFedoraId()).findFirst().get()); 508 } 509 510 @Test 511 public void testCommitRemoveFromTransaction() { 512 stubObject("parent1"); 513 stubObject("child1"); 514 stubObject("transaction1"); 515 stubObject("transaction2"); 516 containmentIndex.addContainedBy(transaction2, parent1.getFedoraId(), child1.getFedoraId()); 517 containmentIndex.commitTransaction(transaction2); 518 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 519 assertEquals(parent1.getFedoraId().getFullId(), 520 containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 521 containmentIndex.removeResource(transaction1, child1.getFedoraId()); 522 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 523 assertEquals(parent1.getFedoraId().getFullId(), 524 containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 525 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 526 containmentIndex.commitTransaction(transaction1); 527 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 528 assertNull(containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 529 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 530 } 531 532 /** 533 * Ensure match the id without a trailing slash. 534 */ 535 @Test 536 public void testResourceExistsFedoraIDNoTrailingSlash() { 537 stubObject("parent1"); 538 stubObject("child1"); 539 stubObject("transaction1"); 540 final FedoraId fedoraID = FedoraId.create(child1.getFedoraId().getFullId()); 541 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 542 containmentIndex.commitTransaction(transaction1); 543 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 544 assertEquals(parent1.getFedoraId().getFullId(), 545 containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 546 assertTrue(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 547 assertTrue(containmentIndex.resourceExists(shortLivedTx, fedoraID, false)); 548 } 549 550 /** 551 * Ensure match the id with a trailing slash. 552 */ 553 @Test 554 public void testResourceExistsFedoraIDTrailingSlash() { 555 stubObject("parent1"); 556 stubObject("child1"); 557 stubObject("transaction1"); 558 final FedoraId fedoraID = FedoraId.create(child1.getFedoraId().getFullId() + "/"); 559 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 560 containmentIndex.commitTransaction(transaction1); 561 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 562 assertEquals(parent1.getFedoraId().getFullId(), 563 containmentIndex.getContainedBy(shortLivedTx, child1.getFedoraId())); 564 assertTrue(containmentIndex.resourceExists(shortLivedTx, child1.getFedoraId(), false)); 565 assertTrue(containmentIndex.resourceExists(shortLivedTx, fedoraID, false)); 566 } 567 568 @Test 569 public void clearIndexWhenReset() { 570 stubObject("parent1"); 571 stubObject("child1"); 572 stubObject("transaction1"); 573 574 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 575 576 assertTrue(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 577 578 containmentIndex.reset(); 579 580 assertFalse(containmentIndex.resourceExists(transaction1, child1.getFedoraId(), false)); 581 } 582 583 @Test 584 public void testHasResourcesStartingFailure() { 585 stubObject("parent1"); 586 stubObject("child1"); 587 stubObject("transaction1"); 588 // Nothing exists. 589 assertFalse(containmentIndex.hasResourcesStartingWith(shortLivedTx, parent1.getFedoraId())); 590 // Add the single resource. 591 containmentIndex.addContainedBy(transaction1, FedoraId.getRepositoryRootId(), parent1.getFedoraId()); 592 containmentIndex.commitTransaction(transaction1); 593 // Still no similar paths. 594 assertFalse(containmentIndex.hasResourcesStartingWith(shortLivedTx, parent1.getFedoraId())); 595 // Add a contained resource that does NOT share the URI path. 596 assertFalse(child1.getFedoraId().getFullId().startsWith(parent1.getFedoraId().getFullId())); 597 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 598 containmentIndex.commitTransaction(transaction1); 599 // Still no similar paths. 600 assertFalse(containmentIndex.hasResourcesStartingWith(shortLivedTx, parent1.getFedoraId())); 601 } 602 603 @Test 604 public void testHasResourcesStartingSuccess() { 605 stubObject("parent1"); 606 final var subPathId = parent1.getFedoraId().resolve("a/layer/down"); 607 stubObject("transaction1"); 608 // Add a resource. 609 containmentIndex.addContainedBy(transaction1, FedoraId.getRepositoryRootId(), subPathId); 610 // That resource's ID starts with the ID we are checking. 611 assertTrue(subPathId.getFullId().startsWith(parent1.getFedoraId().getFullId())); 612 assertTrue(containmentIndex.hasResourcesStartingWith(transaction1, parent1.getFedoraId())); 613 } 614 615 @Test 616 public void testDeletedResourceExists() { 617 stubObject("parent1"); 618 stubObject("transaction1"); 619 containmentIndex.addContainedBy(transaction1, FedoraId.getRepositoryRootId(), parent1.getFedoraId()); 620 containmentIndex.commitTransaction(transaction1); 621 assertTrue(containmentIndex.resourceExists(shortLivedTx, parent1.getFedoraId(), false)); 622 assertTrue(containmentIndex.resourceExists(shortLivedTx, parent1.getFedoraId(), true)); 623 containmentIndex.removeContainedBy(transaction1, FedoraId.getRepositoryRootId(), parent1.getFedoraId()); 624 containmentIndex.commitTransaction(transaction1); 625 assertFalse(containmentIndex.resourceExists(shortLivedTx, parent1.getFedoraId(), false)); 626 assertTrue(containmentIndex.resourceExists(shortLivedTx, parent1.getFedoraId(), true)); 627 containmentIndex.purgeResource(transaction1, parent1.getFedoraId()); 628 containmentIndex.commitTransaction(transaction1); 629 assertFalse(containmentIndex.resourceExists(shortLivedTx, parent1.getFedoraId(), false)); 630 assertFalse(containmentIndex.resourceExists(shortLivedTx, parent1.getFedoraId(), true)); 631 } 632 633 @Test 634 public void testMementosContainment() throws Exception { 635 stubObject("parent1"); 636 stubObject("child1"); 637 stubObject("child2"); 638 stubObject("transaction1"); 639 640 // Parent contains child1 and child2 641 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 642 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), child2.getFedoraId()); 643 containmentIndex.commitTransaction(transaction1); 644 TimeUnit.SECONDS.sleep(1); 645 assertEquals(2, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 646 // get the current instant and make a FedoraId for a memento at this instant. 647 final var bothTime = Instant.now(); 648 final var mementoId = parent1.getFedoraId().resolve("fcr:versions/" + 649 bothTime.atZone(UTC).format(MEMENTO_LABEL_FORMATTER)); 650 // Wait. 651 TimeUnit.SECONDS.sleep(2); 652 // Delete child1 653 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), child1.getFedoraId()); 654 assertEquals(2, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 655 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 656 containmentIndex.commitTransaction(transaction1); 657 TimeUnit.SECONDS.sleep(1); 658 // Child1 is gone in the current view. 659 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 660 // Child1 remains in the memento view. 661 assertEquals(2, containmentIndex.getContains(shortLivedTx, mementoId).count()); 662 // purging child 1 663 containmentIndex.purgeResource(transaction1, child1.getFedoraId()); 664 // stays the same as we haven't committed yet. 665 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 666 assertEquals(2, containmentIndex.getContains(shortLivedTx, mementoId).count()); 667 containmentIndex.commitTransaction(transaction1); 668 // Now the memento loses track of child1. 669 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 670 assertEquals(1, containmentIndex.getContains(shortLivedTx, mementoId).count()); 671 } 672 673 @Test 674 public void testChecksum() throws Exception { 675 stubObject("parent1"); 676 stubObject("transaction1"); 677 // Need to add a containment record for the parent to hold the updated value. 678 containmentIndex.addContainedBy(transaction1, FedoraId.getRepositoryRootId(), parent1.getFedoraId()); 679 final var empty = containmentIndex.containmentLastUpdated(shortLivedTx, parent1.getFedoraId()); 680 assertNull(empty); 681 // Wait a half second as the ETag is based on the highest value of any child's start_time or end_time. 682 TimeUnit.MILLISECONDS.sleep(500); 683 final var firstBorn = parent1.getFedoraId().resolve("child1"); 684 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), firstBorn); 685 assertEquals(1, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 686 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 687 final var first = containmentIndex.containmentLastUpdated(transaction1, parent1.getFedoraId()); 688 assertNotNull(first); 689 assertNotEquals(empty, first); 690 691 containmentIndex.commitTransaction(transaction1); 692 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 693 assertEquals(first, containmentIndex.containmentLastUpdated(shortLivedTx, parent1.getFedoraId())); 694 695 // Wait half a second, all these children will share a start_time. 696 TimeUnit.SECONDS.sleep(1); 697 for (var i = 0; i < 30; i += 1) { 698 final var kidId = parent1.getFedoraId().resolve("child-" + i); 699 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), kidId); 700 } 701 assertEquals(31, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 702 final var allTime = containmentIndex.containmentLastUpdated(transaction1, parent1.getFedoraId()); 703 assertNotEquals(empty, allTime); 704 assertNotEquals(first, allTime); 705 706 containmentIndex.rollbackTransaction(transaction1); 707 assertEquals(1, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 708 assertEquals(first, containmentIndex.containmentLastUpdated(shortLivedTx, parent1.getFedoraId())); 709 710 TimeUnit.SECONDS.sleep(1); 711 712 containmentIndex.removeContainedBy(transaction1, parent1.getFedoraId(), firstBorn); 713 assertEquals(0, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 714 assertNotEquals(first, containmentIndex.containmentLastUpdated(transaction1, parent1.getFedoraId())); 715 assertNotEquals(allTime, containmentIndex.containmentLastUpdated(transaction1, parent1.getFedoraId())); 716 717 containmentIndex.commitTransaction(transaction1); 718 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 719 final var last = containmentIndex.containmentLastUpdated(shortLivedTx, parent1.getFedoraId()); 720 assertNotNull(last); 721 assertNotEquals(first, last); 722 assertNotEquals(allTime, last); 723 } 724 725 @Test 726 public void testLargeContainment() { 727 stubObject("transaction1"); 728 stubObject("parent1"); 729 containmentIndex.setContainsLimit(5); 730 final List<String> expectedChildren = new ArrayList<>(10); 731 for (var i = 0; i < 10; i += 1) { 732 final FedoraId childId = parent1.getFedoraId().resolve("child_" + i); 733 expectedChildren.add(childId.getFullId()); 734 containmentIndex.addContainedBy(transaction1, parent1.getFedoraId(), childId); 735 } 736 assertEquals(10, containmentIndex.getContains(transaction1, parent1.getFedoraId()).count()); 737 assertEquals(0, containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()).count()); 738 containmentIndex.commitTransaction(transaction1); 739 final var foundChildren = containmentIndex.getContains(shortLivedTx, parent1.getFedoraId()) 740 .collect(toList()); 741 assertEquals(10, foundChildren.size()); 742 assertEquals(expectedChildren, foundChildren); 743 } 744} 745 746