package uk.ac.ic.wlgitbridge; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.Response; import org.eclipse.jgit.api.errors.GitAPIException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import uk.ac.ic.wlgitbridge.application.GitBridgeApp; import uk.ac.ic.wlgitbridge.snapshot.servermock.server.MockSnapshotServer; import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState; import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIStateBuilder; import uk.ac.ic.wlgitbridge.snapshot.servermock.util.FileUtil; import uk.ac.ic.wlgitbridge.util.Util; import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Created by Winston on 11/01/15. */ public class WLGitBridgeIntegrationTest { private Runtime runtime = Runtime.getRuntime(); private Map<String, Map<String, SnapshotAPIState>> states = new HashMap<String, Map<String, SnapshotAPIState>>() {{ put("canCloneARepository", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneARepository/state/state.json")).build()); }}); put("canCloneMultipleRepositories", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneMultipleRepositories/state/state.json")).build()); }}); put("cannotCloneAProtectedProject", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneAProtectedProject/state/state.json")).build()); }}); put("canPullAModifiedTexFile", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedTexFile/base/state.json")).build()); put("withModifiedTexFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedTexFile/withModifiedTexFile/state.json")).build()); }}); put("canPullADeletedTexFile", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedTexFile/base/state.json")).build()); put("withDeletedTexFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedTexFile/withDeletedTexFile/state.json")).build()); }}); put("canPullAModifiedBinaryFile", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedBinaryFile/base/state.json")).build()); put("withModifiedBinaryFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedBinaryFile/withModifiedBinaryFile/state.json")).build()); }}); put("canPullADeletedBinaryFile", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedBinaryFile/base/state.json")).build()); put("withDeletedBinaryFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedBinaryFile/withDeletedBinaryFile/state.json")).build()); }}); put("canPullADuplicateBinaryFile", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADuplicateBinaryFile/base/state.json")).build()); put("withDuplicateBinaryFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/state.json")).build()); }}); put("canCloneDuplicateBinaryFiles", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canCloneDuplicateBinaryFiles/state/state.json")).build()); }}); put("canPullUpdatedBinaryFiles", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullUpdatedBinaryFiles/base/state.json")).build()); put("withUpdatedBinaryFiles", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/state.json")).build()); }}); put("canPullAModifiedNestedFile", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedNestedFile/base/state.json")).build()); put("withModifiedNestedFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullAModifiedNestedFile/withModifiedNestedFile/state.json")).build()); }}); put("canPullDeletedNestedFiles", new HashMap<String, SnapshotAPIState>() {{ put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullDeletedNestedFiles/base/state.json")).build()); put("withDeletedNestedFiles", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullDeletedNestedFiles/withDeletedNestedFiles/state.json")).build()); }}); put("canPushFilesSuccessfully", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canPushFilesSuccessfully/state/state.json")).build()); }}); put("pushFailsOnFirstStageOutOfDate", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnFirstStageOutOfDate/state/state.json")).build()); }}); put("pushFailsOnSecondStageOutOfDate", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnSecondStageOutOfDate/state/state.json")).build()); }}); put("pushFailsOnInvalidFiles", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnInvalidFiles/state/state.json")).build()); }}); put("pushFailsOnInvalidProject", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnInvalidProject/state/state.json")).build()); }}); put("pushFailsOnUnexpectedError", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/pushFailsOnUnexpectedError/state/state.json")).build()); }}); put("pushSucceedsAfterRemovingInvalidFiles", new HashMap<String, SnapshotAPIState>() {{ put("invalidState", new SnapshotAPIStateBuilder(getResourceAsStream("/pushSucceedsAfterRemovingInvalidFiles/invalidState/state.json")).build()); put("validState", new SnapshotAPIStateBuilder(getResourceAsStream("/pushSucceedsAfterRemovingInvalidFiles/validState/state.json")).build()); }}); put("canServePushedFiles", new HashMap<String, SnapshotAPIState>() {{ put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/canServePushedFiles/state/state.json")).build()); }}); }}; @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void canCloneARepository() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3857, getResource("/canCloneARepository").toFile()); server.start(); server.setState(states.get("canCloneARepository").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33857, 3857) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33857, dir); wlgb.stop(); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneARepository/state/testproj"), testprojDir.toPath())); } @Test public void canCloneMultipleRepositories() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3858, getResource("/canCloneMultipleRepositories").toFile()); server.start(); server.setState(states.get("canCloneMultipleRepositories").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33858, 3858) }); wlgb.run(); File dir = folder.newFolder(); File testproj1Dir = cloneRepository("testproj1", 33858, dir); File testproj2Dir = cloneRepository("testproj2", 33858, dir); wlgb.stop(); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/testproj1"), testproj1Dir.toPath())); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/testproj2"), testproj2Dir.toPath())); } @Test public void canPullAModifiedTexFile() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3859, getResource("/canPullAModifiedTexFile").toFile()); server.start(); server.setState(states.get("canPullAModifiedTexFile").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33859, 3859) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33859, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullAModifiedTexFile").get("withModifiedTexFile")); Process gitWithModifiedTexFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithModifiedTexFile = gitWithModifiedTexFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithModifiedTexFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/withModifiedTexFile/testproj"), testprojDir.toPath())); } @Test public void canPullADeletedTexFile() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3860, getResource("/canPullADeletedTexFile").toFile()); server.start(); server.setState(states.get("canPullADeletedTexFile").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33860, 3860) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33860, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullADeletedTexFile").get("withDeletedTexFile")); Process gitWithDeletedTexFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithDeletedTexFile = gitWithDeletedTexFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithDeletedTexFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/withDeletedTexFile/testproj"), testprojDir.toPath())); } @Test public void canPullAModifiedBinaryFile() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3862, getResource("/canPullAModifiedBinaryFile").toFile()); server.start(); server.setState(states.get("canPullAModifiedBinaryFile").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33862, 3862) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33862, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullAModifiedBinaryFile").get("withModifiedBinaryFile")); Process gitWithModifiedBinaryFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithModifiedBinaryFile = gitWithModifiedBinaryFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithModifiedBinaryFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/withModifiedBinaryFile/testproj"), testprojDir.toPath())); } @Test public void canPullADeletedBinaryFile() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3863, getResource("/canPullADeletedBinaryFile").toFile()); server.start(); server.setState(states.get("canPullADeletedBinaryFile").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33863, 3863) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33863, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullADeletedBinaryFile").get("withDeletedBinaryFile")); Process gitWithDeletedBinaryFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithDeletedBinaryFile = gitWithDeletedBinaryFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithDeletedBinaryFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/withDeletedBinaryFile/testproj"), testprojDir.toPath())); } @Test public void canPullADuplicateBinaryFile() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(4001, getResource("/canPullADuplicateBinaryFile").toFile()); server.start(); server.setState(states.get("canPullADuplicateBinaryFile").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(44001, 4001) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 44001, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullADuplicateBinaryFile").get("withDuplicateBinaryFile")); Process gitWithDeletedBinaryFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithDeletedBinaryFile = gitWithDeletedBinaryFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithDeletedBinaryFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/testproj"), testprojDir.toPath())); } @Test public void canCloneDuplicateBinaryFiles() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(4002, getResource("/canCloneDuplicateBinaryFiles").toFile()); server.start(); server.setState(states.get("canCloneDuplicateBinaryFiles").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(44002, 4002) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 44002, dir); wlgb.stop(); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneDuplicateBinaryFiles/state/testproj"), testprojDir.toPath())); } @Test public void canPullUpdatedBinaryFiles() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(4003, getResource("/canPullUpdatedBinaryFiles").toFile()); server.start(); server.setState(states.get("canPullUpdatedBinaryFiles").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(44003, 4003) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 44003, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullUpdatedBinaryFiles").get("withUpdatedBinaryFiles")); Process gitWithDeletedBinaryFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithDeletedBinaryFile = gitWithDeletedBinaryFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithDeletedBinaryFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/testproj"), testprojDir.toPath())); } @Test public void canPullAModifiedNestedFile() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3864, getResource("/canPullAModifiedNestedFile").toFile()); server.start(); server.setState(states.get("canPullAModifiedNestedFile").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33864, 3864) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33864, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullAModifiedNestedFile").get("withModifiedNestedFile")); Process gitWithModifiedNestedFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithModifiedNestedFile = gitWithModifiedNestedFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithModifiedNestedFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/withModifiedNestedFile/testproj"), testprojDir.toPath())); } @Test public void canPullDeletedNestedFiles() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3865, getResource("/canPullDeletedNestedFiles").toFile()); server.start(); server.setState(states.get("canPullDeletedNestedFiles").get("base")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33865, 3865) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33865, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/base/testproj"), testprojDir.toPath())); server.setState(states.get("canPullDeletedNestedFiles").get("withDeletedNestedFiles")); Process gitWithDeletedBinaryFile = runtime.exec("git pull", null, testprojDir); int exitCodeWithDeletedBinaryFile = gitWithDeletedBinaryFile.waitFor(); wlgb.stop(); assertEquals(0, exitCodeWithDeletedBinaryFile); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/withDeletedNestedFiles/testproj"), testprojDir.toPath())); } @Test public void canPushFilesSuccessfully() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3866, getResource("/canPushFilesSuccessfully").toFile()); server.start(); server.setState(states.get("canPushFilesSuccessfully").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33866, 3866) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33866, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPushFilesSuccessfully/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(0, pushExitCode); } private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST = "To http://127.0.0.1:33867/testproj.git\n" + " ! [rejected] master -> master (non-fast-forward)\n" + "error: failed to push some refs to 'http://127.0.0.1:33867/testproj.git'\n" + "hint: Updates were rejected because the tip of your current branch is behind\n" + "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + "hint: 'git pull ...') before pushing again.\n" + "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n"; @Test public void pushFailsOnFirstStageOutOfDate() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3867, getResource("/pushFailsOnFirstStageOutOfDate").toFile()); server.start(); server.setState(states.get("pushFailsOnFirstStageOutOfDate").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33867, 3867) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33867, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnFirstStageOutOfDate/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(1, pushExitCode); assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST, Util.fromStream(gitPush.getErrorStream(), 2)); } private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND = "To http://127.0.0.1:33868/testproj.git\n" + " ! [rejected] master -> master (non-fast-forward)\n" + "error: failed to push some refs to 'http://127.0.0.1:33868/testproj.git'\n" + "hint: Updates were rejected because the tip of your current branch is behind\n" + "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + "hint: 'git pull ...') before pushing again.\n" + "hint: See the 'Note about fast-forwards' in 'git push --help' for details.\n"; @Test public void pushFailsOnSecondStageOutOfDate() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3868, getResource("/pushFailsOnSecondStageOutOfDate").toFile()); server.start(); server.setState(states.get("pushFailsOnSecondStageOutOfDate").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33868, 3868) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33868, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnSecondStageOutOfDate/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(1, pushExitCode); assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND, Util.fromStream(gitPush.getErrorStream(), 2)); } private static final List<String> EXPECTED_OUT_PUSH_INVALID_FILES = Arrays.asList( "remote: error: invalid files", "remote: hint: You have 4 invalid files in your Overleaf project:", "remote: hint: file1.invalid (error)", "remote: hint: file2.exe (invalid file extension)", "remote: hint: hello world.png (rename to: hello_world.png)", "remote: hint: an image.jpg (rename to: an_image.jpg)", "To http://127.0.0.1:33869/testproj.git", "! [remote rejected] master -> master (invalid files)", "error: failed to push some refs to 'http://127.0.0.1:33869/testproj.git'" ); @Test public void pushFailsOnInvalidFiles() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3869, getResource("/pushFailsOnInvalidFiles").toFile()); server.start(); server.setState(states.get("pushFailsOnInvalidFiles").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33869, 3869) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33869, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidFiles/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(1, pushExitCode); List<String> actual = Util.linesFromStream(gitPush.getErrorStream(), 2, "[K"); assertEquals(EXPECTED_OUT_PUSH_INVALID_FILES, actual); } private static final List<String> EXPECTED_OUT_PUSH_INVALID_PROJECT = Arrays.asList( "remote: error: invalid project", "remote: hint: project: no main file", "remote: hint: The project would have no (editable) main .tex file.", "To http://127.0.0.1:33870/testproj.git", "! [remote rejected] master -> master (invalid project)", "error: failed to push some refs to 'http://127.0.0.1:33870/testproj.git'" ); @Test public void pushFailsOnInvalidProject() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3870, getResource("/pushFailsOnInvalidProject").toFile()); server.start(); server.setState(states.get("pushFailsOnInvalidProject").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33870, 3870) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33870, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnInvalidProject/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(1, pushExitCode); List<String> actual = Util.linesFromStream(gitPush.getErrorStream(), 2, "[K"); assertEquals(EXPECTED_OUT_PUSH_INVALID_PROJECT, actual); } private static final List<String> EXPECTED_OUT_PUSH_UNEXPECTED_ERROR = Arrays.asList( "remote: error: Overleaf error", "remote: hint: There was an internal error with the Overleaf server.", "remote: hint: Please contact Overleaf.", "To http://127.0.0.1:33871/testproj.git", "! [remote rejected] master -> master (Overleaf error)", "error: failed to push some refs to 'http://127.0.0.1:33871/testproj.git'" ); /* this one prints a stack trace */ @Test public void pushFailsOnUnexpectedError() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3871, getResource("/pushFailsOnUnexpectedError").toFile()); server.start(); server.setState(states.get("pushFailsOnUnexpectedError").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33871, 3871) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33871, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushFailsOnUnexpectedError/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(1, pushExitCode); List<String> actual = Util.linesFromStream(gitPush.getErrorStream(), 2, "[K"); assertEquals(EXPECTED_OUT_PUSH_UNEXPECTED_ERROR, actual); } private static final List<String> EXPECTED_OUT_PUSH_INVALID_EXE_FILE = Arrays.asList( "remote: error: invalid files", "remote: hint: You have 1 invalid files in your Overleaf project:", "remote: hint: file1.exe (invalid file extension)", "To http://127.0.0.1:33872/testproj.git", "! [remote rejected] master -> master (invalid files)", "error: failed to push some refs to 'http://127.0.0.1:33872/testproj.git'" ); @Test public void pushSucceedsAfterRemovingInvalidFiles() throws IOException, GitAPIException, InterruptedException { MockSnapshotServer server = new MockSnapshotServer(3872, getResource("/pushSucceedsAfterRemovingInvalidFiles").toFile()); server.start(); server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("invalidState")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(33872, 3872) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", 33872, dir); // try to push invalid file; it should fail assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/invalidState/testproj"), testprojDir.toPath())); assertEquals(0, runtime.exec("touch file1.exe", null, testprojDir).waitFor()); assertEquals(0, runtime.exec("git add -A", null, testprojDir).waitFor()); assertEquals(0, runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor()); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); assertEquals(1, pushExitCode); List<String> actual = Util.linesFromStream(gitPush.getErrorStream(), 0, "[K"); assertEquals(EXPECTED_OUT_PUSH_INVALID_EXE_FILE, actual); // remove invalid file and push again; it should succeed this time assertEquals(0, runtime.exec("git rm file1.exe", null, testprojDir).waitFor()); assertEquals(0, runtime.exec("git commit -m remove_invalid_file", null, testprojDir).waitFor()); server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("validState")); gitPush = runtime.exec("git push", null, testprojDir); pushExitCode = gitPush.waitFor(); wlgb.stop(); assertEquals(0, pushExitCode); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/validState/testproj"), testprojDir.toPath())); } @Test public void canServePushedFiles() throws IOException, ExecutionException, InterruptedException { // // I don't think we can test this completely without some changes to the mock server, because we have no way // of pausing the test while the push is in progress. Once the push is over, the file isn't actually there for // us to fetch any more. We can however test the access and error conditions, which comprise most of the logic. // int gitBridgePort = 33873; int mockServerPort = 3873; MockSnapshotServer server = new MockSnapshotServer( mockServerPort, getResource("/canServePushedFiles").toFile()); server.start(); server.setState(states.get("canServePushedFiles").get("state")); GitBridgeApp wlgb = new GitBridgeApp(new String[] { makeConfigFile(gitBridgePort, mockServerPort) }); wlgb.run(); File dir = folder.newFolder(); File testprojDir = cloneRepository("testproj", gitBridgePort, dir); assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canServePushedFiles/state/testproj"), testprojDir.toPath())); runtime.exec("touch push.tex", null, testprojDir).waitFor(); runtime.exec("git add -A", null, testprojDir).waitFor(); runtime.exec("git commit -m \"push\"", null, testprojDir).waitFor(); Process gitPush = runtime.exec("git push", null, testprojDir); int pushExitCode = gitPush.waitFor(); assertEquals(0, pushExitCode); // With no key, we should get a 404. String url = "http://127.0.0.1:" + gitBridgePort + "/api/testproj/push.tex"; Response response = new AsyncHttpClient().prepareGet(url).execute().get(); assertEquals(404, response.getStatusCode()); // With an invalid project and no key, we should get a 404. url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/push.tex"; response = new AsyncHttpClient().prepareGet(url).execute().get(); assertEquals(404, response.getStatusCode()); // With a bad key for a valid project, we should get a 404. url = "http://127.0.0.1:" + gitBridgePort + "/api/testproj/push.tex?key=notavalidkey"; response = new AsyncHttpClient().prepareGet(url).execute().get(); assertEquals(404, response.getStatusCode()); // With a bad key for an invalid project, we should get a 404. url = "http://127.0.0.1:" + gitBridgePort + "/api/notavalidproject/push.tex?key=notavalidkey"; response = new AsyncHttpClient().prepareGet(url).execute().get(); assertEquals(404, response.getStatusCode()); wlgb.stop(); } private File cloneRepository(String repositoryName, int port, File dir) throws IOException, InterruptedException { String repo = "git clone http://127.0.0.1:" + port + "/" + repositoryName + ".git"; assertEquals(0, runtime.exec(repo, null, dir).waitFor()); File repositoryDir = new File(dir, repositoryName); assertEquals(0, runtime.exec("git config user.name TEST", null, repositoryDir).waitFor()); assertEquals(0, runtime.exec("git config user.email test@test.com", null, repositoryDir).waitFor()); assertEquals(0, runtime.exec("git config push.default matching", null, repositoryDir).waitFor()); return repositoryDir; } private String makeConfigFile(int port, int apiPort) throws IOException { File wlgb = folder.newFolder(); File config = folder.newFile(); PrintWriter writer = new PrintWriter(config); writer.println("{\n" + "\t\"port\": " + port + ",\n" + "\t\"rootGitDirectory\": \"" + wlgb.getAbsolutePath() + "\",\n" + "\t\"apiBaseUrl\": \"http://127.0.0.1:" + apiPort + "/api/v0\",\n" + "\t\"username\": \"\",\n" + "\t\"password\": \"\",\n" + "\t\"postbackBaseUrl\": \"http://127.0.0.1:" + port + "\",\n" + "\t\"serviceName\": \"Overleaf\"\n," + " \"oauth2\": {\n" + " \"oauth2ClientID\": \"clientID\",\n" + " \"oauth2ClientSecret\": \"oauth2 client secret\",\n" + " \"oauth2Server\": \"https://www.overleaf.com\"\n" + " }\n" + "}\n"); writer.close(); return config.getAbsolutePath(); } private Path getResource(String path) { return Paths.get("src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest" + path); } private InputStream getResourceAsStream(String path) { try { return new FileInputStream(getResource(path).toFile()); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } private static String withoutWhitespace(String s) { return s.replaceAll("\\s",""); } }