Fix Item & Bundle tests which check order of results. H2 2.x + Hibernate 5.x returns results ordered by UUID when unspecified.

This commit is contained in:
Tim Donohue
2022-03-02 13:52:24 -06:00
parent 62c0e28f54
commit 967e36af7a
7 changed files with 131 additions and 101 deletions

View File

@@ -18,6 +18,7 @@ import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.Group;
@@ -26,8 +27,6 @@ import org.dspace.eperson.Group;
*/
public class BitstreamBuilder extends AbstractDSpaceObjectBuilder<Bitstream> {
public static final String ORIGINAL = "ORIGINAL";
private Bitstream bitstream;
private Item item;
private Group readerGroup;
@@ -158,12 +157,12 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder<Bitstream> {
}
private Bundle getOriginalBundle(Item item) throws SQLException, AuthorizeException {
List<Bundle> bundles = itemService.getBundles(item, ORIGINAL);
List<Bundle> bundles = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME);
Bundle targetBundle = null;
if (bundles.size() < 1) {
// not found, create a new one
targetBundle = bundleService.create(context, item, ORIGINAL);
targetBundle = bundleService.create(context, item, Constants.CONTENT_BUNDLE_NAME);
} else {
// put bitstreams into first bundle
targetBundle = bundles.iterator().next();

View File

@@ -16,6 +16,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.codec.CharEncoding;
@@ -144,47 +146,48 @@ public class BitstreamControllerIT extends AbstractControllerIntegrationTest {
String bitstreamContent = "ThisIsSomeDummyText";
Bundle bundle1 = BundleBuilder.createBundle(context, publicItem1)
List<Bundle> bundles = new ArrayList();
bundles.add(BundleBuilder.createBundle(context, publicItem1)
.withName("TEST FIRST BUNDLE")
.build();
.build());
Bitstream bitstream = null;
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
bitstream = BitstreamBuilder.
createBitstream(context, bundle1, is)
createBitstream(context, bundles.get(0), is)
.withName("Bitstream")
.withDescription("description")
.withMimeType("text/plain")
.build();
}
Bundle bundle2 = BundleBuilder.createBundle(context, publicItem1)
bundles.add(BundleBuilder.createBundle(context, publicItem1)
.withName("TEST SECOND BUNDLE")
.withBitstream(bitstream)
.build();
.build());
context.restoreAuthSystemState();
// While in DSpace code, Bundles are *unordered*, in Hibernate v5 + H2 v2.x, they are returned sorted by UUID.
// So, we reorder this list of created Bundles by UUID to get their expected return order.
// NOTE: Once on Hibernate v6, this might need "toString()" removed as it may sort UUIDs based on RFC 4412.
Comparator<Bundle> compareByUUID = Comparator.comparing(b -> b.getID().toString());
bundles.sort(compareByUUID);
String token = getAuthToken(admin.getEmail(), password);
// Expect only the first Bundle to be returned
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/bundle")
.param("projection", "full"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.is(
BundleMatcher.matchBundle(bundle1.getName(),
bundle1.getID(),
bundle1.getHandle(),
bundle1.getType(),
bundle1.getBitstreams())
))).andExpect(jsonPath("$", Matchers.not(
BundleMatcher.matchBundle(bundle2.getName(),
bundle2.getID(),
bundle2.getHandle(),
bundle2.getType(),
bundle2.getBitstreams())
BundleMatcher.matchBundle(bundles.get(0).getName(),
bundles.get(0).getID(),
bundles.get(0).getHandle(),
bundles.get(0).getType(),
bundles.get(0).getBitstreams())
)));

View File

@@ -20,6 +20,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.InputStream;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import org.apache.commons.codec.CharEncoding;
@@ -45,6 +47,7 @@ import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
@@ -67,6 +70,10 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest
@Autowired
private GroupService groupService;
@Autowired
private ItemService itemService;
@Test
public void findAllTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
@@ -1480,11 +1487,19 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest
.build();
}
Bundle secondBundle = BundleBuilder.createBundle(context, publicItem1)
.withName("second bundle")
.withBitstream(bitstream).build();
// Add default content bundle to list of bundles
List<Bundle> bundles = itemService.getBundles(publicItem1, Constants.CONTENT_BUNDLE_NAME);
Bundle bundle = bitstream.getBundles().get(0);
// Add this bitstream to a second bundle & append to list of bundles
bundles.add(BundleBuilder.createBundle(context, publicItem1)
.withName("second bundle")
.withBitstream(bitstream).build());
// While in DSpace code, Bundles are *unordered*, in Hibernate v5 + H2 v2.x, they are returned sorted by UUID.
// So, we reorder this list of created Bundles by UUID to get their expected return order.
// NOTE: Once on Hibernate v6, this might need "toString()" removed as it may sort UUIDs based on RFC 4412.
Comparator<Bundle> compareByUUID = Comparator.comparing(b -> b.getID().toString());
bundles.sort(compareByUUID);
//Get bundle should contain the first bundle in the list
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/bundle"))
@@ -1492,10 +1507,10 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$",
BundleMatcher.matchProperties(
bundle.getName(),
bundle.getID(),
bundle.getHandle(),
bundle.getType()
bundles.get(0).getName(),
bundles.get(0).getID(),
bundles.get(0).getHandle(),
bundles.get(0).getType()
)
));
}

View File

@@ -397,26 +397,24 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
public void getBitstreamsForBundle() throws Exception {
context.turnOffAuthorisationSystem();
bundle1 = BundleBuilder.createBundle(context, item)
.withName("testname")
.build();
String bitstreamContent = "Dummy content";
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
bitstream1 = BitstreamBuilder.createBitstream(context, item, is)
bitstream1 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName())
.withName("Bitstream")
.withDescription("Description")
.withMimeType("text/plain")
.build();
bitstream2 = BitstreamBuilder.createBitstream(context, item, is)
bitstream2 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName())
.withName("Bitstream2")
.withDescription("Description2")
.withMimeType("text/plain")
.build();
}
bundle1 = BundleBuilder.createBundle(context, item)
.withName("testname")
.withBitstream(bitstream1)
.withBitstream(bitstream2)
.build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/bundles/" + bundle1.getID() + "/bitstreams")
@@ -465,26 +463,24 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
public void patchMoveBitstreams() throws Exception {
context.turnOffAuthorisationSystem();
bundle1 = BundleBuilder.createBundle(context, item)
.withName("testname")
.build();
String bitstreamContent = "Dummy content";
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
bitstream1 = BitstreamBuilder.createBitstream(context, item, is)
bitstream1 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName())
.withName("Bitstream")
.withDescription("Description")
.withMimeType("text/plain")
.build();
bitstream2 = BitstreamBuilder.createBitstream(context, item, is)
bitstream2 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName())
.withName("Bitstream2")
.withDescription("Description2")
.withMimeType("text/plain")
.build();
}
bundle1 = BundleBuilder.createBundle(context, item)
.withName("testname")
.withBitstream(bitstream1)
.withBitstream(bitstream2)
.build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/bundles/" + bundle1.getID() + "/bitstreams")

View File

@@ -2421,9 +2421,11 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.build();
List<Item> items = new ArrayList();
// This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC
// and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 )
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID());
// Hibernate 5.x's org.hibernate.dialect.H2Dialect sorts UUIDs as if they are Strings.
// So, we must compare UUIDs as if they are strings.
// In Hibernate 6, the H2Dialect has been updated with native UUID type support, at which point
// we'd need to update the below comparator to compare them as java.util.UUID (which sorts based on RFC 4412).
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID().toString());
Item item0 = ItemBuilder.createItem(context, collection).withTitle("Item 0").build();
items.add(item0);

View File

@@ -123,9 +123,11 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
List<Item> items = new ArrayList();
// This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC
// and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 )
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID());
// Hibernate 5.x's org.hibernate.dialect.H2Dialect sorts UUIDs as if they are Strings.
// So, we must compare UUIDs as if they are strings.
// In Hibernate 6, the H2Dialect has been updated with native UUID type support, at which point
// we'd need to update the below comparator to compare them as java.util.UUID (which sorts based on RFC 4412).
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID().toString());
//2. Three public items that are readable by Anonymous with different subjects
Item publicItem1 = ItemBuilder.createItem(context, col1)
@@ -204,9 +206,11 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.build();
List<Item> items = new ArrayList();
// This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC
// and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 )
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID());
// Hibernate 5.x's org.hibernate.dialect.H2Dialect sorts UUIDs as if they are Strings.
// So, we must compare UUIDs as if they are strings.
// In Hibernate 6, the H2Dialect has been updated with native UUID type support, at which point
// we'd need to update the below comparator to compare them as java.util.UUID (which sorts based on RFC 4412).
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID().toString());
//2. Three public items that are readable by Anonymous with different subjects
Item publicItem1 = ItemBuilder.createItem(context, col1)
@@ -3213,16 +3217,23 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
Item item = ItemBuilder.createItem(context, collection).withTitle("Item").build();
Bundle bundle0 = BundleBuilder.createBundle(context, item).withName("Bundle 0").build();
Bundle bundle1 = BundleBuilder.createBundle(context, item).withName("Bundle 1").build();
Bundle bundle2 = BundleBuilder.createBundle(context, item).withName("Bundle 2").build();
Bundle bundle3 = BundleBuilder.createBundle(context, item).withName("Bundle 3").build();
Bundle bundle4 = BundleBuilder.createBundle(context, item).withName("Bundle 4").build();
Bundle bundle5 = BundleBuilder.createBundle(context, item).withName("Bundle 5").build();
Bundle bundle6 = BundleBuilder.createBundle(context, item).withName("Bundle 6").build();
Bundle bundle7 = BundleBuilder.createBundle(context, item).withName("Bundle 7").build();
Bundle bundle8 = BundleBuilder.createBundle(context, item).withName("Bundle 8").build();
Bundle bundle9 = BundleBuilder.createBundle(context, item).withName("Bundle 9").build();
List<Bundle> bundles = new ArrayList();
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 0").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 1").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 2").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 3").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 4").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 5").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 6").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 7").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 8").build());
bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 9").build());
// While in DSpace code, Bundles are *unordered*, in Hibernate v5 + H2 v2.x, they are returned sorted by UUID.
// So, we reorder this list of created Bundles by UUID to get their expected pagination ordering.
// NOTE: Once on Hibernate v6, this might need "toString()" removed as it may sort UUIDs based on RFC 4412.
Comparator<Bundle> compareByUUID = Comparator.comparing(b -> b.getID().toString());
bundles.sort(compareByUUID);
context.restoreAuthSystemState();
@@ -3232,11 +3243,16 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$", ItemMatcher.matchItemProperties(item)))
.andExpect(jsonPath("$._embedded.bundles._embedded.bundles",Matchers.containsInAnyOrder(
BundleMatcher.matchProperties(bundle0.getName(), bundle0.getID(), bundle0.getHandle(), bundle0.getType()),
BundleMatcher.matchProperties(bundle1.getName(), bundle1.getID(), bundle1.getHandle(), bundle1.getType()),
BundleMatcher.matchProperties(bundle2.getName(), bundle2.getID(), bundle2.getHandle(), bundle2.getType()),
BundleMatcher.matchProperties(bundle3.getName(), bundle3.getID(), bundle3.getHandle(), bundle3.getType()),
BundleMatcher.matchProperties(bundle4.getName(), bundle4.getID(), bundle4.getHandle(), bundle4.getType())
BundleMatcher.matchProperties(bundles.get(0).getName(), bundles.get(0).getID(), bundles.get(0).getHandle(),
bundles.get(0).getType()),
BundleMatcher.matchProperties(bundles.get(1).getName(), bundles.get(1).getID(), bundles.get(1).getHandle(),
bundles.get(1).getType()),
BundleMatcher.matchProperties(bundles.get(2).getName(), bundles.get(2).getID(), bundles.get(2).getHandle(),
bundles.get(2).getType()),
BundleMatcher.matchProperties(bundles.get(3).getName(), bundles.get(3).getID(), bundles.get(3).getHandle(),
bundles.get(3).getType()),
BundleMatcher.matchProperties(bundles.get(4).getName(), bundles.get(4).getID(), bundles.get(4).getHandle(),
bundles.get(4).getType())
)))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items/" + item.getID())))
.andExpect(jsonPath("$._embedded.bundles.page.size", is(5)))

View File

@@ -469,40 +469,39 @@ public class IIIFControllerIT extends AbstractControllerIntegrationTest {
.withMimeType("image/tiff")
.build();
}
context.restoreAuthSystemState();
// expect structures elements with label and canvas id.
// Expected structures elements based on the above test content
// NOTE: we cannot guarantee the order of Bundles in the Manifest, therefore this test has to simply check
// that each Bundle exists in the manifest with Canvases corresponding to each bitstream.
getClient().perform(get("/iiif/" + publicItem1.getID() + "/manifest"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.@context", is("http://iiif.io/api/presentation/2/context.json")))
.andExpect(jsonPath("$.sequences[0].canvases[0].@id",
Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c0")))
.andExpect(jsonPath("$.sequences[0].canvases[0].label", is("Global 1")))
.andExpect(jsonPath("$.sequences[0].canvases[0].width", is(2000)))
.andExpect(jsonPath("$.sequences[0].canvases[0].height", is(3000)))
.andExpect(jsonPath("$.sequences[0].canvases[1].label", is("Global 2")))
.andExpect(jsonPath("$.sequences[0].canvases[2].label", is("Global 3")))
.andExpect(jsonPath("$.structures[0].@id",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0")))
.andExpect(jsonPath("$.structures[0].label", is("Table of Contents")))
.andExpect(jsonPath("$.structures[0].viewingHint", is("top")))
.andExpect(jsonPath("$.structures[0].ranges[0]",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-0")))
.andExpect(jsonPath("$.structures[0].ranges[1]",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-1")))
.andExpect(jsonPath("$.structures[1].@id",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-0")))
.andExpect(jsonPath("$.structures[1].label", is("ORIGINAL")))
.andExpect(jsonPath("$.structures[1].canvases[0]",
Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c0")))
.andExpect(jsonPath("$.structures[2].@id",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-1")))
.andExpect(jsonPath("$.structures[2].label", is("IIIF")))
.andExpect(jsonPath("$.structures[2].canvases[0]",
Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c1")))
.andExpect(jsonPath("$.structures[2].canvases[1]",
Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c2")))
.andExpect(jsonPath("$.service").exists());
.andExpect(status().isOk())
.andExpect(jsonPath("$.@context", is("http://iiif.io/api/presentation/2/context.json")))
// should contain 3 canvases, corresponding to each bitstream
.andExpect(jsonPath("$.sequences[0].canvases[*].label",
Matchers.contains("Global 1", "Global 2", "Global 3")))
// First structure should be a Table of Contents
.andExpect(jsonPath("$.structures[0].@id",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0")))
.andExpect(jsonPath("$.structures[0].label", is("Table of Contents")))
.andExpect(jsonPath("$.structures[0].viewingHint", is("top")))
.andExpect(jsonPath("$.structures[0].ranges[0]",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-0")))
.andExpect(jsonPath("$.structures[0].ranges[1]",
Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-1")))
// Should contain a structure with label=IIIF, corresponding to IIIF bundle
// It should have exactly 2 canvases (corresponding to 2 bitstreams)
.andExpect(jsonPath("$.structures[?(@.label=='IIIF')].canvases[0]").exists())
.andExpect(jsonPath("$.structures[?(@.label=='IIIF')].canvases[1]").exists())
.andExpect(jsonPath("$.structures[?(@.label=='IIIF')].canvases[2]").doesNotExist())
// Should contain a structure with label=ORIGINAL, corresponding to ORIGINAL bundle
// It should have exactly 1 canvas (corresponding to 1 bitstream)
.andExpect(jsonPath("$.structures[?(@.label=='ORIGINAL')].canvases[0]").exists())
.andExpect(jsonPath("$.structures[?(@.label=='ORIGINAL')].canvases[1]").doesNotExist())
.andExpect(jsonPath("$.service").exists());
}
@Test