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);
}
}