package helpers; import static com.google.common.collect.Collections2.orderedPermutations; import static java.util.Arrays.asList; import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Fail.fail; import static play.test.Helpers.running; import static test.AorraTestUtils.fakeAorraApp; import static test.AorraTestUtils.fileStore; import static test.AorraTestUtils.jcrom; import static test.AorraTestUtils.sessionFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Enumeration; import java.util.List; import java.util.SortedSet; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.jcr.ItemExistsException; import javax.jcr.Session; import models.GroupManager; import models.User; import models.UserDAO; import org.apache.commons.compress.utils.IOUtils; import org.apache.jackrabbit.api.security.user.Group; import org.eclipse.jetty.util.IO; import org.junit.Test; import play.libs.F; import service.filestore.FileStore; import service.filestore.roles.Admin; import com.google.common.base.Joiner; import com.google.common.collect.Sets; public class FileStoreHelperTest { @Test public void mvFolder() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { fsh.mkdir("/foo", false); String barId = fsh.mkdir("/bar", false).getIdentifier(); assertThat(fm.getFileOrFolder("/foo/bar")).isNull(); fsh.mv("bar", "foo"); assertThat(fm.getFileOrFolder("/foo/bar")).isNotNull(); assertThat(barId).isEqualTo(fm.getFileOrFolder("/foo/bar").getIdentifier()); return null; } }); } @Test public void mvFile() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { fsh.mkdir("/foo", false); FileStore.File f = fm.getRoot().createFile("test.xml", "application/xml", new ByteArrayInputStream("<root></root>".getBytes())); assertThat(fm.getFileOrFolder("/test.xml")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/test.xml")).isNull(); fsh.mv("/test.xml", "/foo"); assertThat(fm.getFileOrFolder("/test.xml")).isNull(); assertThat(fm.getFileOrFolder("/foo/test.xml")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/test.xml").getIdentifier()).isEqualTo(f.getIdentifier()); fsh.mv("/foo/test.xml", "/"); assertThat(fm.getFileOrFolder("/test.xml")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/test.xml")).isNull(); return null; } }); } @Test public void mvWhenFolderExists() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { fsh.mkdir("/foo", false); fsh.mkdir("/bar", false); fsh.mkdir("/foo/bar", false); assertThat(fm.getFileOrFolder("/bar")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/bar")).isNotNull(); assertThat(fm.getFileOrFolder("/bar").getIdentifier()).isNotEqualTo( fm.getFileOrFolder("/foo/bar").getIdentifier()); try { fsh.mv("bar", "foo"); fail("Should have thrown ItemExistsException"); } catch (ItemExistsException e) { assertThat(e.getMessage()).contains("bar"); } assertThat(fm.getFileOrFolder("/bar")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/bar")).isNotNull(); assertThat(fm.getFileOrFolder("/bar").getIdentifier()).isNotEqualTo( fm.getFileOrFolder("/foo/bar").getIdentifier()); return null; } }); } @Test public void mvRenameWhenFileExists() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { FileStore.Folder foo = fsh.mkdir("/foo", false); fm.getRoot().createFile("bar.xml", "application/xml", new ByteArrayInputStream("<root>bar</root>".getBytes())); foo.createFile("test.xml", "application/xml", new ByteArrayInputStream("<root>test</root>".getBytes())); assertThat(fm.getFileOrFolder("/bar.xml")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/test.xml")).isNotNull(); fsh.mv("bar.xml", "foo/test.xml"); assertThat(fm.getFileOrFolder("/bar.xml")).isNotNull(); assertThat(fm.getFileOrFolder("/foo/test.xml")).isNotNull(); return null; } }); } @Test public void mkdirSingle() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath = "/foobar"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath); assertThat(fof).isNull(); } fsh.mkdir(absPath, false); { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath); assertThat(fof).isNotNull(); assertThat(fof).isInstanceOf(FileStore.Folder.class); } return null; } }); } @Test public void mkdirSingleWhenFolderExists() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath = "/foobar"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath); assertThat(fof).isNull(); fm.getRoot().createFolder("foobar"); } try { fsh.mkdir(absPath, false); fail("Should have thrown FolderExistsException"); } catch (FileStoreHelper.FolderExistsException e) { assertThat(e.getMessage()).contains("foobar"); } return null; } }); } @Test public void mkdirSingleWhenFileExists() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath = "/foobar"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath); assertThat(fof).isNull(); fm.getRoot().createFile("foobar", "text/plain", new ByteArrayInputStream("Some content.".getBytes())); } try { fsh.mkdir(absPath, false); fail("Should have thrown FileExistsException"); } catch (FileStoreHelper.FileExistsException e) { assertThat(e.getMessage()).contains("foobar"); } return null; } }); } @Test public void mkdirMultiple() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath = "/foo/bar"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath); assertThat(fof).isNull(); } try { fsh.mkdir(absPath, false); fail("Should have thrown FolderNotFoundException"); } catch (FileStoreHelper.FolderNotFoundException e) { assertThat(e.getMessage()).contains("foo"); } return null; } }); } @Test public void mkdirMultipleWithParentCreation() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath1 = "/foo/bar"; final String absPath2 = "/foo/baz"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath1); assertThat(fof).isNull(); } fsh.mkdir(absPath1, true); { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath1); assertThat(fof).isNotNull(); assertThat(fof).isInstanceOf(FileStore.Folder.class); } { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath2); assertThat(fof).isNull(); } fsh.mkdir(absPath2, true); { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath2); assertThat(fof).isNotNull(); assertThat(fof).isInstanceOf(FileStore.Folder.class); } return null; } }); } @Test public void createZipFile() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fh, final FileStore.Manager fm) throws Throwable { fh.mkdir("/a (a-f)/1", true); fh.mkdir("/a (a-f)/2", true); fh.mkdir("/b (a-f)/1", true); final byte[] testBytes = "Some test content.".getBytes(); ((FileStore.Folder) fm.getFileOrFolder("/a (a-f)/2")) .createFile("test.txt", "text/plain", new ByteArrayInputStream(testBytes)); // Test root folder { final java.io.File tf = asTempFile(fh.createZipFile(fm.getRoot())); assertThat(tf).isNotNull(); final ZipFile zf = new ZipFile(tf); final Enumeration<? extends ZipEntry> entries = zf.entries(); final ZipEntry ze = entries.nextElement(); assertThat(ze).isNotNull(); assertThat(ze.getName()).isEqualTo("a (a-f)/2/test.txt"); assertThat(ze.getSize()).isEqualTo(testBytes.length); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); IOUtils.copy(zf.getInputStream(ze), bos); assertThat(bos.toByteArray()).isEqualTo(testBytes); assertThat(entries.hasMoreElements()).isFalse(); zf.close(); } // Test subfolder { final java.io.File tf = asTempFile(fh.createZipFile( (FileStore.Folder) fm.getFileOrFolder("/a (a-f)/2"))); assertThat(tf).isNotNull(); final ZipFile zf = new ZipFile(tf); final Enumeration<? extends ZipEntry> entries = zf.entries(); final ZipEntry ze = entries.nextElement(); assertThat(ze).isNotNull(); assertThat(ze.getName()).isEqualTo("2/test.txt"); assertThat(ze.getSize()).isEqualTo(testBytes.length); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); IOUtils.copy(zf.getInputStream(ze), bos); assertThat(bos.toByteArray()).isEqualTo(testBytes); assertThat(entries.hasMoreElements()).isFalse(); zf.close(); } return session; } }); } @Test public void rmFile() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String filename = "test.txt"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder("/"+filename); assertThat(fof).isNull(); } { fm.getRoot().createFile(filename, "text/plain", new ByteArrayInputStream("Hello World!".getBytes())); final FileStore.FileOrFolder fof = fm.getFileOrFolder("/"+filename); assertThat(fof).isNotNull(); } fsh.rm("/"+filename, true); { final FileStore.FileOrFolder fof = fm.getFileOrFolder("/"+filename); assertThat(fof).isNull(); } return null; } }); } @Test public void rmFolder() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath1 = "/foo/bar"; { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath1); assertThat(fof).isNull(); } fsh.mkdir(absPath1, true); { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath1); assertThat(fof).isNotNull(); assertThat(fof).isInstanceOf(FileStore.Folder.class); } fsh.rm(absPath1, true); { final FileStore.FileOrFolder fof = fm.getFileOrFolder(absPath1); assertThat(fof).isNull(); } return null; } }); } @Test public void rmBoundaryCases() { helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { // Should be able to handle null path fsh.rm(null, false); // Should handle non-existent paths fsh.rm("/doesnotexist", true); // Should handle non-recursive delete on folder fsh.rm("/", false); return session; } }); } @Test public void tree() { final PrintWriterTester pwt = new PrintWriterTester(); helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final GroupManager gm = new GroupManager(session); final Group group = gm.create("foo"); final String absPath1 = "/foo/bar"; final FileStore.Folder folder = fsh.mkdir(absPath1, true); folder.createFile("test.txt", "text/plain", new ByteArrayInputStream("Hello World!".getBytes())); // Assign permissions fm.getRoot().grantAccess(group.getPrincipal().getName(), FileStore.Permission.NONE); folder.grantAccess(group.getPrincipal().getName(), FileStore.Permission.RO); // Truncate string pwt.dump(); // Check output without permissions (null & false both work) for (final Boolean flag : new Boolean[] { null, false }) { // Check tree output from root fsh.tree("", flag, false); assertThat(pwt.dump()).isEqualTo( "/ (root)\n"+ "|-+ foo\n"+ "| |-+ bar\n"+ "| | |-- test.txt\n"); } // Check tree output with permissons fsh.tree("", true, false); assertThat(pwt.dump()).isEqualTo( "/ (root)\n"+ "|-+ foo {foo=NONE}\n"+ "| |-+ bar {foo=RO}\n"+ "| | |-- test.txt\n"); // Check some things we can't do fsh.tree("/foo/bar/test.txt", false, false); assertThat(pwt.dump()).isEqualTo("/foo/bar/test.txt is a file\n"); fsh.tree("/doesnotexist", false, false); assertThat(pwt.dump()).isEqualTo("no such folder /doesnotexist\n"); return session; } }, pwt.getPrintWriter()); } @Test public void listadmin() { final PrintWriterTester pwt = new PrintWriterTester(); helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final GroupManager gm = new GroupManager(session); final Group first = gm.create("foo"); final Group second = gm.create("bar"); Admin.getInstance(session).getGroup().addMember(first); final UserDAO userDao = new UserDAO(session, jcrom()); final User user = new User(); user.setEmail("test@example.com"); user.setName("Test Example User"); userDao.create(user); first.addMember(second); first.addMember(userDao.jackrabbitUser(user)); // Get tree // TODO: Find out why we pass a null parameter! fsh.listadmin(null); // TODO: Pretty sure this output is wrong - check with Andre assertThat(pwt.dump()).isEqualTo( "|-+ filestoreAdmin\n"+ "|-+ foo\n"+ "| |-+ bar\n"+ "| |-- test@example.com\n"); return session; } }, pwt.getPrintWriter()); } @Test public void importArchive() { final PrintWriterTester pwt = new PrintWriterTester(); helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final String absPath1 = "/foo/bar"; final FileStore.Folder folder = fsh.mkdir(absPath1, true); folder.createFile("test.txt", "text/plain", new ByteArrayInputStream("Hello World!".getBytes())); assertThat(fm.getFileOrFolder("/foo/bar/test.txt")).isNotNull(); final java.io.File zipFile = asTempFile(fsh.createZipFile(fm.getRoot())); // Delete all files assertThat(fm.getFileOrFolder("/foo")).isNotNull(); fm.getFileOrFolder("/foo").delete(); assertThat(fm.getFileOrFolder("/foo/bar/test.txt")).isNull(); // Load again from zip fsh.importArchive(zipFile.getAbsolutePath(), ""); // Get file & check contents assertThat(fm.getFileOrFolder("/foo/bar/test.txt")).isNotNull(); assertThat(IO.toString(((FileStore.File) fm.getFileOrFolder("/foo/bar/test.txt")).getData())) .isEqualTo("Hello World!"); // Second load should not cause errors fsh.importArchive(zipFile.getAbsolutePath(), "/"); assertThat(IO.toString(((FileStore.File) fm.getFileOrFolder("/foo/bar/test.txt")).getData())) .isEqualTo("Hello World!"); // Load to non-root location fsh.importArchive(zipFile.getAbsolutePath(), "/subfolder"); assertThat( fm.getFileOrFolder("/subfolder/foo/bar/test.txt")).isNotNull(); assertThat(IO.toString(((FileStore.File) fm.getFileOrFolder("/subfolder/foo/bar/test.txt")).getData())) .isEqualTo("Hello World!"); return session; } }, pwt.getPrintWriter()); } @Test public void listFilesInFolder() { final PrintWriterTester pwt = new PrintWriterTester(); helperTest( new F.Function3<Session,FileStoreHelper,FileStore.Manager,Session>() { @Override public Session apply(final Session session, final FileStoreHelper fsh, final FileStore.Manager fm) throws Throwable { final Iterable<List<String>> folderParts = orderedPermutations(asList( new String[] { "a", "b", "c", "d", "e" })); final SortedSet<String> folderPaths = Sets.newTreeSet(); for (final List<String> parts : folderParts) { folderPaths.add("/"+Joiner.on('/').join(parts)); } int fileNo = 1; for (final String absPath : folderPaths) { final FileStore.Folder folder = fsh.mkdir(absPath, true); for (int i = 0; i < 2; i++) { folder.createFile(String.format("%03d.txt", fileNo), "text/plain", new ByteArrayInputStream( String.format("This is file %d", fileNo).getBytes())); fileNo++; } } final List<FileStore.File> files = fsh.listFilesInFolder(fm.getRoot()); for (int i = 1; i < fileNo; i++) { assertThat(files.get(i-1).getName()).isEqualTo( String.format("%03d.txt", i)); } return session; } }, pwt.getPrintWriter()); } private void helperTest(final F.Function3<Session, FileStoreHelper, FileStore.Manager, Session> op) { helperTest(op, null); } private void helperTest(final F.Function3<Session, FileStoreHelper, FileStore.Manager, Session> op, final PrintWriter writer) { running(fakeAorraApp(), new Runnable() { @Override public void run() { sessionFactory().inSession(new F.Function<Session,Session>() { @Override public Session apply(final Session session) throws Throwable { final FileStoreHelper fsh = writer == null ? new FileStoreHelper(session) : new FileStoreHelper(session, writer); op.apply(session, fsh, fileStore().getManager(session)); return session; } }); } }); } private static java.io.File asTempFile(InputStream is) throws IOException { final java.io.File tf = File.createTempFile("zipfile", ""); tf.deleteOnExit(); IOUtils.copy(is, new FileOutputStream(tf)); return tf; } private class PrintWriterTester { private final StringWriter sw; private final PrintWriter pw; public PrintWriterTester() { sw = new StringWriter(); pw = new PrintWriter(sw); } public PrintWriter getPrintWriter() { return pw; } public String dump() { final String content = toString(); sw.getBuffer().setLength(0); return content; } @Override public String toString() { pw.flush(); return sw.getBuffer().toString(); } } }