/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.content; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.UUID; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.test.util.ReflectionTestUtils; /** * Unit Tests for class Bitstream * * @author pvillega */ public class BitstreamTest extends AbstractDSpaceObjectTest { /** * log4j category */ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(BitstreamTest.class); protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() .getBitstreamFormatService(); /** * BitStream instance for the tests */ private Bitstream bs; /** * Spy of AuthorizeService to use for tests * (initialized / setup in @Before method) */ private AuthorizeService authorizeServiceSpy; /** * This method will be run before every test as per @Before. It will * initialize resources required for the tests. * * Other methods can be annotated with @Before here or in subclasses * but no execution order is guaranteed */ @Before @Override public void init() { super.init(); try { //we have to create a new bitstream in the database context.turnOffAuthorisationSystem(); File f = new File(testProps.get("test.bitstream").toString()); this.bs = bitstreamService.create(context, new FileInputStream(f)); this.dspaceObject = bs; context.restoreAuthSystemState(); // Initialize our spy of the autowired (global) authorizeService bean. // This allows us to customize the bean's method return values in tests below authorizeServiceSpy = spy(authorizeService); // "Wire" our spy to be used by the current loaded bitstreamService // (To ensure it uses the spy instead of the real service) ReflectionTestUtils.setField(bitstreamService, "authorizeService", authorizeServiceSpy); } catch (IOException ex) { log.error("IO Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); } catch (SQLException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); } } /** * This method will be run after every test as per @After. It will * clean resources initialized by the @Before methods. * * Other methods can be annotated with @After here or in subclasses * but no execution order is guaranteed */ @After @Override public void destroy() { bs = null; super.destroy(); } /** * Test of find method, of class Bitstream. */ @Test public void testBSFind() throws SQLException { UUID id = this.bs.getID(); Bitstream found = bitstreamService.find(context, id); assertThat("testBSFind 0", found, notNullValue()); //the item created by default has no name nor type set assertThat("testBSFind 1", found.getFormat(context).getMIMEType(), equalTo("application/octet-stream")); assertThat("testBSFind 2", found.getName(), nullValue()); assertThat("testBSFind 3", found.getID(), equalTo(id)); } /** * Test of findAll method, of class Bitstream. */ @Test public void testFindAll() throws SQLException { List found = bitstreamService.findAll(context); assertThat("testFindAll 0", found, notNullValue()); //we have many bs, one created per test run, so at least we have 1 if //this test is run first assertTrue("testFindAll 1", found.size() >= 1); boolean added = false; for (Bitstream b : found) { if (b.equals(bs)) { added = true; } } assertTrue("testFindAll 2", added); } @Test public void testFindAllBatches() throws Exception { //Adding some data for processing and cleaning this up at the end context.turnOffAuthorisationSystem(); File f = new File(testProps.get("test.bitstream").toString()); List inserted = new ArrayList<>(); for (int i = 0; i < 5; i++) { Bitstream bs = bitstreamService.create(context, new FileInputStream(f)); inserted.add(bs); } context.restoreAuthSystemState(); // sorted list of all bitstreams List all = bitstreamService.findAll(context); List expected = new ArrayList<>(all); expected.sort(Comparator.comparing(bs -> bs.getID().toString())); int total = bitstreamService.countTotal(context); int batchSize = 2; int numberOfBatches = (int) Math.ceil((double) total / batchSize); //collect in batches List collected = new ArrayList<>(); for (int i = 0; i < numberOfBatches; i++) { Iterator it = bitstreamService.findAll(context, batchSize, i * batchSize); it.forEachRemaining(collected::add); } assertEquals("Batched results should match sorted findAll", expected, collected); // Cleanup context.turnOffAuthorisationSystem(); for (Bitstream b : inserted) { bitstreamService.delete(context, b); } context.restoreAuthSystemState(); } /** * Test of create method, of class Bitstream. */ @Test public void testCreate() throws IOException, SQLException { File f = new File(testProps.get("test.bitstream").toString()); Bitstream created = bitstreamService.create(context, new FileInputStream(f)); //the item created by default has no name nor type set assertThat("testCreate 0", created.getFormat(context).getMIMEType(), equalTo("application/octet-stream")); assertThat("testCreate 1", created.getName(), nullValue()); } /** * Test of register method, of class Bitstream. */ @Test public void testRegister() throws IOException, SQLException, AuthorizeException { // Allow general Bitstream WRITE permissions doNothing().when(authorizeServiceSpy).authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); int assetstore = 0; File f = new File(testProps.get("test.bitstream").toString()); Bitstream registered = bitstreamService.register(context, assetstore, f.getName()); //the item created by default has no name nor type set assertThat("testRegister 0", registered.getFormat(context).getMIMEType(), equalTo("application/octet-stream")); assertThat("testRegister 1", registered.getName(), nullValue()); } /** * Test of getID method, of class Bitstream. */ @Override @Test public void testGetID() { assertTrue("testGetID 0", bs.getID() != null); } @Test public void testLegacyID() { assertTrue("testGetLegacyID 0", bs.getLegacyId() == null); } /** * Test of getHandle method, of class Bitstream. */ @Override @Test public void testGetHandle() { //no handle for bitstreams assertThat("testGetHandle 0", bs.getHandle(), nullValue()); } /** * Test of getSequenceID method, of class Bitstream. */ @Test public void testGetSequenceID() { //sequence id is -1 when not set assertThat("testGetSequenceID 0", bs.getSequenceID(), equalTo(-1)); } /** * Test of setSequenceID method, of class Bitstream. */ @Test public void testSetSequenceID() { int val = 2; bs.setSequenceID(val); assertThat("testSetSequenceID 0", bs.getSequenceID(), equalTo(val)); } /** * Test of getName method, of class Bitstream. */ @Override @Test public void testGetName() { //name is null when not set assertThat("testGetName 0", bs.getName(), nullValue()); } /** * Test of setName method, of class Bitstream. */ @Test public void testSetName() throws SQLException { String name = "new name"; bs.setName(context, name); assertThat("testGetName 0", bs.getName(), notNullValue()); assertThat("testGetName 1", bs.getName(), not(equalTo(""))); assertThat("testGetName 2", bs.getName(), equalTo(name)); } /** * Test of getSource method, of class Bitstream. */ @Test public void testGetSource() { //source is null if not set assertThat("testGetSource 0", bs.getSource(), nullValue()); } /** * Test of setSource method, of class Bitstream. */ @Test public void testSetSource() throws SQLException { String source = "new source"; bs.setSource(context, source); assertThat("testSetSource 0", bs.getSource(), notNullValue()); assertThat("testSetSource 1", bs.getSource(), not(equalTo(""))); assertThat("testSetSource 2", bs.getSource(), equalTo(source)); } /** * Test of getDescription method, of class Bitstream. */ @Test public void testGetDescription() { //default description is null if not set assertThat("testGetDescription 0", bs.getDescription(), nullValue()); } /** * Test of setDescription method, of class Bitstream. */ @Test public void testSetDescription() throws SQLException { String description = "new description"; bs.setDescription(context, description); assertThat("testSetDescription 0", bs.getDescription(), notNullValue()); assertThat("testSetDescription 1", bs.getDescription(), not(equalTo(""))); assertThat("testSetDescription 2", bs.getDescription(), equalTo(description)); } /** * Test of getChecksum method, of class Bitstream. */ @Test public void testGetChecksum() { String checksum = "75a060bf6eb63fd0aad88b7d757728d3"; assertThat("testGetChecksum 0", bs.getChecksum(), notNullValue()); assertThat("testGetChecksum 1", bs.getChecksum(), not(equalTo(""))); assertThat("testGetChecksum 2", bs.getChecksum(), equalTo(checksum)); } /** * Test of getChecksumAlgorithm method, of class Bitstream. */ @Test public void testGetChecksumAlgorithm() { String alg = "MD5"; assertThat("testGetChecksumAlgorithm 0", bs.getChecksumAlgorithm(), notNullValue()); assertThat("testGetChecksumAlgorithm 1", bs.getChecksumAlgorithm(), not(equalTo(""))); assertThat("testGetChecksumAlgorithm 2", bs.getChecksumAlgorithm(), equalTo(alg)); } /** * Test of getSizeBytes method, of class Bitstream. */ @Test public void testGetSize() { long size = 238413; // yuck, hardcoded! assertThat("testGetSize 0", bs.getSizeBytes(), equalTo(size)); } /** * Test of setUserFormatDescription method, of class Bitstream. */ @Test public void testSetUserFormatDescription() throws SQLException { String userdescription = "user format description"; bs.setUserFormatDescription(context, userdescription); assertThat("testSetUserFormatDescription 0", bs.getUserFormatDescription() , notNullValue()); assertThat("testSetUserFormatDescription 1", bs.getUserFormatDescription() , not(equalTo(""))); assertThat("testSetUserFormatDescription 2", bs.getUserFormatDescription() , equalTo(userdescription)); } /** * Test of getUserFormatDescription method, of class Bitstream. */ @Test public void testGetUserFormatDescription() { //null by default if not set assertThat("testGetUserFormatDescription 0", bs.getUserFormatDescription() , nullValue()); } /** * Test of getFormatDescription method, of class Bitstream. */ @Test public void testGetFormatDescription() throws SQLException { //format is unknown by default String format = "Unknown"; assertThat("testGetFormatDescription 0", bs.getFormatDescription(context), notNullValue()); assertThat("testGetFormatDescription 1", bs.getFormatDescription(context), not(equalTo(""))); assertThat("testGetFormatDescription 2", bs.getFormatDescription(context), equalTo(format)); } /** * Test of getFormat method, of class Bitstream. */ @Test public void testGetFormat() throws SQLException { assertThat("testGetFormat 0", bs.getFormat(context), notNullValue()); assertThat("testGetFormat 1", bs.getFormat(context), equalTo(bitstreamFormatService.findUnknown(context))); } /** * Test of setFormat method, of class Bitstream. */ @Test public void testSetFormat() throws SQLException { int id = 3; BitstreamFormat format = bitstreamFormatService.find(context, id); bs.setFormat(format); assertThat("testSetFormat 0", bs.getFormat(context), notNullValue()); assertThat("testSetFormat 1", bs.getFormat(context), equalTo(bitstreamFormatService.find(context, id))); } /** * Test of update method, of class Bitstream. */ @Test(expected = AuthorizeException.class) public void testUpdateNotAdmin() throws SQLException, AuthorizeException { // Disallow Bitstream WRITE permissions doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, bs, Constants.WRITE); //TODO: we need to verify the update, how? bitstreamService.update(context, bs); } /** * Test of update method, of class Bitstream. */ @Test public void testUpdateAdmin() throws SQLException, AuthorizeException { // Allow Bitstream WRITE permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, bs, Constants.WRITE); //TODO: we need to verify the update, how? bitstreamService.update(context, bs); } /** * Test of delete method, of class Bitstream. */ @Test public void testDeleteAndExpunge() throws IOException, SQLException, AuthorizeException { // Create a new bitstream, which we can delete. As ordering of these // tests is unpredictable we don't want to delete the global bitstream context.ignoreAuthorization(); File f = new File(testProps.get("test.bitstream").toString()); Bitstream delBS = bitstreamService.create(context, new FileInputStream(f)); UUID bitstreamId = delBS.getID(); context.restoreAuthSystemState(); // Allow Bitstream WRITE permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, delBS, Constants.WRITE); // Allow Bitstream DELETE permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, delBS, Constants.DELETE); // Test that delete will flag the bitstream as deleted assertFalse("testIsDeleted 0", delBS.isDeleted()); bitstreamService.delete(context, delBS); assertTrue("testDelete 0", delBS.isDeleted()); // Now test expunge actually removes the bitstream bitstreamService.expunge(context, delBS); assertThat("testExpunge 0", bitstreamService.find(context, bitstreamId), nullValue()); } /** * Test of delete method, of class Bitstream. */ @Test public void testDeleteBitstreamAndUnsetPrimaryBitstreamID() throws IOException, SQLException, AuthorizeException { context.turnOffAuthorisationSystem(); Community owningCommunity = communityService.create(null, context); Collection collection = collectionService.create(context, owningCommunity); WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false); Item item = installItemService.installItem(context, workspaceItem); Bundle b = bundleService.create(context, item, "TESTBUNDLE"); // Allow Bundle REMOVE permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, b, Constants.REMOVE); // Allow Bitstream WRITE permissions doNothing().when(authorizeServiceSpy) .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.WRITE)); // Allow Bitstream DELETE permissions doNothing().when(authorizeServiceSpy) .authorizeAction(any(Context.class), any(Bitstream.class), eq(Constants.DELETE)); //set a value different than default File f = new File(testProps.get("test.bitstream").toString()); // Create a new bitstream, which we can delete. Bitstream delBS = bitstreamService.create(context, new FileInputStream(f)); bundleService.addBitstream(context, b, delBS); // set primary bitstream b.setPrimaryBitstreamID(delBS); context.restoreAuthSystemState(); // Test that delete will flag the bitstream as deleted assertFalse("testDeleteBitstreamAndUnsetPrimaryBitstreamID 0", delBS.isDeleted()); assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 1", b.getPrimaryBitstream(), equalTo(delBS)); // Delete bitstream bitstreamService.delete(context, delBS); assertTrue("testDeleteBitstreamAndUnsetPrimaryBitstreamID 2", delBS.isDeleted()); // Now test if the primary bitstream was unset from bundle assertThat("testDeleteBitstreamAndUnsetPrimaryBitstreamID 3", b.getPrimaryBitstream(), equalTo(null)); } /** * Test of retrieve method, of class Bitstream. */ @Test public void testRetrieveCanRead() throws IOException, SQLException, AuthorizeException { // Allow Bitstream READ permissions doNothing().when(authorizeServiceSpy).authorizeAction(context, bs, Constants.READ); assertThat("testRetrieveCanRead 0", bitstreamService.retrieve(context, bs), notNullValue()); } /** * Test of retrieve method, of class Bitstream. */ @Test(expected = AuthorizeException.class) public void testRetrieveNoRead() throws IOException, SQLException, AuthorizeException { // Disallow Bitstream READ permissions doThrow(new AuthorizeException()).when(authorizeServiceSpy).authorizeAction(context, bs, Constants.READ); assertThat("testRetrieveNoRead 0", bitstreamService.retrieve(context, bs), notNullValue()); } /** * Test of getBundles method, of class Bitstream. */ @Test public void testGetBundles() throws SQLException { assertThat("testGetBundles 0", bs.getBundles(), notNullValue()); //by default no bundles assertTrue("testGetBundles 1", bs.getBundles().size() == 0); } /** * Test of getType method, of class Bitstream. */ @Override @Test public void testGetType() { assertThat("testGetType 0", bs.getType(), equalTo(Constants.BITSTREAM)); } /** * Test of isRegisteredBitstream method, of class Bitstream. */ @Test public void testIsRegisteredBitstream() { //false by default assertThat("testIsRegisteredBitstream 0", bitstreamService.isRegisteredBitstream(bs), equalTo(false)); } /** * Test of getStoreNumber method, of class Bitstream. */ @Test public void testGetStoreNumber() { //stored in store 0 by default assertTrue("testGetStoreNumber 0", bs.getStoreNumber() == 0); } /** * Test of getParentObject method, of class Bitstream. */ @Test @Override public void testGetParentObject() throws SQLException { //by default this bitstream is not linked to any object assertThat("testGetParentObject 0", bitstreamService.getParentObject(context, bs), nullValue()); } }