package com.constellio.data.dao.services.contents; import static com.constellio.data.conf.HashingEncoding.BASE64_URL_ENCODED; import static com.constellio.sdk.tests.TestUtils.frenchPangram; import static java.io.File.separator; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import com.constellio.data.conf.DigitSeparatorMode; import com.constellio.data.dao.services.contents.ContentDaoException.ContentDaoException_NoSuchContent; import com.constellio.data.dao.services.idGenerator.UUIDV1Generator; import com.constellio.data.io.services.facades.FileService; import com.constellio.data.io.services.facades.IOServices; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.annotations.SlowTest; @RunWith(Parameterized.class) public class ContentDaoRealTest extends ConstellioTest { static String givenContentDaoIsTheConfiguredOne = "givenContentDaoIsTheConfiguredOne"; static String givenContentDaoIsTheFileSystemImpl = "givenContentDaoIsTheFileSystemImpl"; String theContent = aString(); String theId = aString() + aString() + aString() + aString(); String theParsedContent = frenchPangram(); ContentDao vaultDao; String testCase; public ContentDaoRealTest(String testCase) { this.testCase = testCase; } @Parameterized.Parameters(name = "{0}") public static Collection<Object[]> testCases() { return Arrays.asList(new Object[][] { { givenContentDaoIsTheConfiguredOne }, { givenContentDaoIsTheFileSystemImpl } }); } @Before public void setup() { if (testCase.equals(givenContentDaoIsTheConfiguredOne)) { vaultDao = getDataLayerFactory().getContentsDao(); } else if (testCase.equals(givenContentDaoIsTheFileSystemImpl)) { vaultDao = new FileSystemContentDao(newTempFolder(), getIOLayerFactory().newIOServices(), getDataLayerFactory().getDataLayerConfiguration()); } } @Test public void givenContentDaoConfiguredWithTwoDigitSeperatorThenOK() throws Exception { getDataLayerFactory().getDataLayerConfiguration() .setContentDaoFileSystemDigitsSeparatorMode(DigitSeparatorMode.TWO_DIGITS); assertThat(getDataLayerFactory().getDataLayerConfiguration().getContentDaoFileSystemDigitsSeparatorMode()) .isEqualTo(DigitSeparatorMode.TWO_DIGITS); vaultDao.add("anIdWithoutSlash", newInputStreamOfTextContent("test1")); vaultDao.add("anIdWithA/Slash", newInputStreamOfTextContent("test2")); vaultDao.add("z+", newInputStreamOfTextContent("test3")); vaultDao.add("z+q=1", newInputStreamOfTextContent("test4")); vaultDao.add("z+q", newInputStreamOfTextContent("test5")); vaultDao.add("z+/z+q=1k", newInputStreamOfTextContent("test6")); assertThat(vaultDao.getContentInputStream("anIdWithoutSlash", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test1")); assertThat(vaultDao.getContentInputStream("anIdWithA/Slash", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test2")); assertThat(vaultDao.getContentInputStream("z+", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test3")); assertThat(vaultDao.getContentInputStream("z+q=1", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test4")); assertThat(vaultDao.getContentInputStream("z+q", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test5")); assertThat(vaultDao.getContentInputStream("z+/z+q=1k", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test6")); if (vaultDao instanceof FileSystemContentDao) { File root = ((FileSystemContentDao) vaultDao).rootFolder; assertThat(root.listFiles()).extracting("name", "file").containsOnly( tuple("an", false), tuple("anIdWithA", false), tuple("z+", false) ); assertThat(new File(root, "an").listFiles()).extracting("name", "file").containsOnly( tuple("anIdWithoutSlash", true) ); assertThat(new File(root, "anIdWithA").listFiles()).extracting("name", "file").containsOnly( tuple("Slash", true) ); assertThat(new File(root, "z+").listFiles()).extracting("name", "file").containsOnly( tuple("z+q=1", true), tuple("z+q=1k", true), tuple("z+q", true), tuple("z+", true) ); } } @Test public void givenContentDaoConfiguredWithThreeOneDigitSeperatorThenOK() throws Exception { getDataLayerFactory().getDataLayerConfiguration() .setHashingEncoding(BASE64_URL_ENCODED); getDataLayerFactory().getDataLayerConfiguration() .setContentDaoFileSystemDigitsSeparatorMode(DigitSeparatorMode.THREE_LEVELS_OF_ONE_DIGITS); vaultDao.add("anIdWithoutSlash", newInputStreamOfTextContent("test1")); vaultDao.add("anotherId", newInputStreamOfTextContent("test2")); vaultDao.add("z", newInputStreamOfTextContent("test4")); vaultDao.add("zz", newInputStreamOfTextContent("test5")); vaultDao.add("zzz", newInputStreamOfTextContent("test6")); vaultDao.add("zzzz", newInputStreamOfTextContent("test7")); assertThat(vaultDao.getContentInputStream("anIdWithoutSlash", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test1")); assertThat(vaultDao.getContentInputStream("anotherId", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test2")); assertThat(vaultDao.getContentInputStream("z", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test4")); assertThat(vaultDao.getContentInputStream("zz", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test5")); assertThat(vaultDao.getContentInputStream("zzz", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test6")); assertThat(vaultDao.getContentInputStream("zzzz", SDK_STREAM)).hasContentEqualTo( newInputStreamOfTextContent("test7")); if (vaultDao instanceof FileSystemContentDao) { File root = ((FileSystemContentDao) vaultDao).rootFolder; assertThat(root.listFiles()).extracting("name", "file").containsOnly( tuple("a", false), tuple("z", false) ); assertThat(new File(root, "a").listFiles()).extracting("name", "file").containsOnly( tuple("an", false) ); assertThat(new File(root, "a" + separator + "an").listFiles()).extracting("name", "file").containsOnly( tuple("anI", false), tuple("ano", false) ); assertThat(new File(root, "a" + separator + "an" + separator + "anI").listFiles()).extracting("name", "file") .containsOnly(tuple("anIdWithoutSlash", true)); assertThat(new File(root, "a" + separator + "an" + separator + "ano").listFiles()).extracting("name", "file") .containsOnly(tuple("anotherId", true)); assertThat(new File(root, "z").listFiles()).extracting("name", "file").containsOnly( tuple("z", true), tuple("zz", false) ); assertThat(new File(root, "z" + separator + "zz").listFiles()).extracting("name", "file").containsOnly( tuple("zz", true), tuple("zzz", false) ); assertThat(new File(root, "z" + separator + "zz" + separator + "zzz").listFiles()).extracting("name", "file") .containsOnly(tuple("zzz", true), tuple("zzzz", true)); } } @Test public void whenAddingRegularRecordContentThenCanBeRetreivedWithHisNewId() throws IOException, ContentDaoException { InputStream inputStreamFromTestFile = newFileInputStream(getTestResourceFile("image.png")); vaultDao.add(theId, inputStreamFromTestFile); inputStreamFromTestFile.close(); IOServices ioServices = getIOLayerFactory().newIOServices(); byte[] bytesFromVault = ioServices.readBytes(vaultDao.getContentInputStream(theId, SDK_STREAM)); byte[] bytesFromTestFile = ioServices.readBytes(newFileInputStream(getTestResourceFile("image.png"))); assertThat(bytesFromVault).isEqualTo(bytesFromTestFile); } @Test(expected = ContentDaoException_NoSuchContent.class) public void givenContentDoesNotExistWhenGetInputStreamThenThrowNoSuchContent() throws IOException, ContentDaoException { vaultDao.getContentInputStream("anInvalidId", SDK_STREAM); } @Test public void whenAddingSmallRecordContentThenCanBeRetreivedWithHisId() throws IOException, ContentDaoException { theId = "a/b/c.txt"; InputStream theContentInputStream = newInputStreamOfTextContent(theContent); vaultDao.add(theId, theContentInputStream); InputStream inputStream = vaultDao.getContentInputStream(theId, SDK_STREAM); String theOutputContent = new FileService(null).readStreamToStringWithoutExpectableIOException(inputStream); assertEquals(theContent, theOutputContent); if (vaultDao instanceof FileSystemContentDao) { FileSystemContentDao fileSystemContentDao = (FileSystemContentDao) vaultDao; File folderA = new File(fileSystemContentDao.rootFolder, "a"); File folderB = new File(folderA, "b"); File fileC = new File(folderB, "c.txt"); assertThat(folderA).exists(); assertThat(folderB).exists(); assertThat(fileC).exists(); } } @Test public void givenContentsInFolderWhenGetFolderContentsThenReturnValidContent() throws IOException, ContentDaoException { vaultDao.add("a/b/c.txt", newInputStreamOfTextContent(theContent)); vaultDao.add("a/b/e.txt", newInputStreamOfTextContent(theContent)); vaultDao.add("a/d.txt", newInputStreamOfTextContent(theContent)); vaultDao.add("a/f.txt", newInputStreamOfTextContent(theContent)); assertThat(vaultDao.getFolderContents("a")).containsOnly("a/b", "a/d.txt", "a/f.txt"); assertThat(vaultDao.getFolderContents("a/b")).containsOnly("a/b/c.txt", "a/b/e.txt"); assertThat(vaultDao.getFolderContents("t")).isEmpty(); } @Test public void whenAddingRecordContentUsingAnIdWithASlashThenCanBeRetreivedWithHisId() throws IOException, ContentDaoException { InputStream theContentInputStream = newInputStreamOfTextContent(theContent); vaultDao.add(theId, theContentInputStream); InputStream inputStream = vaultDao.getContentInputStream(theId, SDK_STREAM); String theOutputContent = new FileService(null).readStreamToStringWithoutExpectableIOException(inputStream); assertEquals(theContent, theOutputContent); } @SlowTest @Test public void whenAddingContentsWithMultipleThreadsThenSurvive() throws Exception { vaultDao = getDataLayerFactory().getContentsDao(); File tempFolder = createRandomTextFilesInTempFolder(100, 30 * 1024); final LinkedBlockingQueue<File> queue = new LinkedBlockingQueue<File>(tempFolder.list().length); queue.addAll(Arrays.asList(tempFolder.listFiles())); final Map<File, String> synchronizedMap = java.util.Collections.synchronizedMap(new HashMap<File, String>()); List<Thread> threads = new ArrayList<Thread>(); for (int i = 0; i < 10; i++) { threads.add(new Thread() { @Override public void run() { File nextFile; while ((nextFile = queue.poll()) != null) { InputStream stream = null; try { String contentId = UUIDV1Generator.newRandomId(); stream = newFileInputStream(nextFile); vaultDao.add(contentId, stream); synchronizedMap.put(nextFile, contentId); } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(stream); } } } }); } for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } for (Map.Entry<File, String> entry : synchronizedMap.entrySet()) { InputStream expectedInputStream = null; InputStream savedInputStream = null; try { expectedInputStream = newFileInputStream(entry.getKey()); savedInputStream = vaultDao.getContentInputStream(entry.getValue(), SDK_STREAM); assertInputStreamEquals(expectedInputStream, savedInputStream); } finally { IOUtils.closeQuietly(expectedInputStream); IOUtils.closeQuietly(savedInputStream); } } } private InputStream newInputStreamOfTextContent(String aContent) throws IOException { File file = newTempFileWithContent("tempFile.txt", aContent); return newFileInputStream(file); } }