org.eclipse.orion.server.tests.servlets.git.GitTest.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.orion.server.tests.servlets.git.GitTest.java

Source

/*******************************************************************************
 * Copyright (c) 2011 IBM Corporation and others 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 * IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.orion.server.tests.servlets.git;

import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import junit.framework.Assert;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.core.tests.harness.FileSystemHelper;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.RebaseCommand.Operation;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.orion.internal.server.servlets.Activator;
import org.eclipse.orion.internal.server.servlets.ProtocolConstants;
import org.eclipse.orion.internal.server.servlets.workspace.ServletTestingSupport;
import org.eclipse.orion.internal.server.servlets.workspace.WebProject;
import org.eclipse.orion.server.core.ServerConstants;
import org.eclipse.orion.server.core.ServerStatus;
import org.eclipse.orion.server.git.GitConstants;
import org.eclipse.orion.server.git.servlets.GitServlet;
import org.eclipse.orion.server.tests.servlets.files.FileSystemTest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.xml.sax.SAXException;

import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.PostMethodWebRequest;
import com.meterware.httpunit.PutMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;

public abstract class GitTest extends FileSystemTest {

    static final String GIT_SERVLET_LOCATION = GitServlet.GIT_URI + '/';

    static WebConversation webConversation;
    File gitDir;
    File testFile;
    protected FileRepository db;

    @BeforeClass
    public static void setupWorkspace() {
        initializeWorkspaceLocation();
    }

    protected static String sshRepo;
    protected static String sshRepo2;
    protected static char[] password;
    protected static String knownHosts;
    protected static String knownHosts2;
    protected static byte[] privateKey;
    protected static byte[] publicKey;
    protected static byte[] passphrase;

    protected static void readSshProperties() {
        String propertiesFile = System.getProperty("orion.tests.ssh");
        if (propertiesFile == null)
            return;
        Map<String, String> properties = new HashMap<String, String>();
        try {
            File file = new File(propertiesFile);
            if (file.isDirectory())
                file = new File(file, "sshtest.properties");
            BufferedReader reader = new BufferedReader(new FileReader(file));
            try {
                for (String line; (line = reader.readLine()) != null;) {
                    if (line.startsWith("#"))
                        continue;
                    int sep = line.indexOf("=");
                    String property = line.substring(0, sep).trim();
                    String value = line.substring(sep + 1).trim();
                    properties.put(property, value);
                }
            } finally {
                reader.close();
            }
            // initialize constants
            sshRepo = properties.get("host");
            sshRepo2 = properties.get("host2");
            password = properties.get("password").toCharArray();
            knownHosts = properties.get("knownHosts");
            knownHosts2 = properties.get("knownHosts2");
            privateKey = loadFileContents(properties.get("privateKeyPath")).getBytes();
            publicKey = loadFileContents(properties.get("publicKeyPath")).getBytes();
            passphrase = properties.get("passphrase").getBytes();
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Could not read ssh properties file: " + propertiesFile);
        }
    }

    @Before
    public void setUp() throws CoreException, IOException, GitAPIException {
        webConversation = new WebConversation();
        webConversation.setExceptionsThrownOnErrorStatus(false);
        setUpAuthorization();
        createRepository();
        ServletTestingSupport.allowedPrefixes = gitDir.toString();
    }

    @After
    public void tearDown() throws Exception {
        db.close();
        FileUtils.delete(gitDir, FileUtils.RECURSIVE);
    }

    protected JSONObject linkProject(String contentLocation, String projectName)
            throws JSONException, IOException, SAXException {
        // TODO: remove me
        URI workspaceLocation = createWorkspace(getMethodName());
        return createProjectOrLink(workspaceLocation, projectName, contentLocation);
    }

    protected JSONObject createProjectOrLink(URI workspaceLocation, String projectName, String contentLocation)
            throws JSONException, IOException, SAXException {
        JSONObject body = new JSONObject();
        if (contentLocation != null) {
            body.put(ProtocolConstants.KEY_CONTENT_LOCATION, contentLocation);
            ServletTestingSupport.allowedPrefixes = contentLocation;
        }
        WebRequest request = new PostMethodWebRequest(workspaceLocation.toString(),
                getJsonAsStream(body.toString()), "UTF-8");
        if (projectName != null)
            request.setHeaderField(ProtocolConstants.HEADER_SLUG, projectName);
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_CREATED, response.getResponseCode());
        JSONObject project = new JSONObject(response.getText());
        assertEquals(projectName, project.getString(ProtocolConstants.KEY_NAME));
        String projectId = project.optString(ProtocolConstants.KEY_ID, null);
        assertNotNull(projectId);
        return project;
    }

    protected URI createWorkspace(String suffix) throws IOException, SAXException {
        String workspaceName = getClass().getName() + "#" + suffix;
        WebRequest request = getCreateWorkspaceRequest(workspaceName);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return URI.create(response.getHeaderField(ProtocolConstants.HEADER_LOCATION));
    }

    protected void createRepository() throws IOException, GitAPIException, CoreException {
        IPath randomLocation = getRandomLocation();
        gitDir = randomLocation.toFile();
        randomLocation = randomLocation.addTrailingSeparator().append(Constants.DOT_GIT);
        File dotGitDir = randomLocation.toFile().getCanonicalFile();
        db = new FileRepository(dotGitDir);
        assertFalse(dotGitDir.exists());
        db.create(false /* non bare */);

        testFile = new File(gitDir, "test.txt");
        testFile.createNewFile();
        createFile(testFile.toURI(), "test");
        File folder = new File(gitDir, "folder");
        folder.mkdir();
        File folderFile = new File(folder, "folder.txt");
        folderFile.createNewFile();
        createFile(folderFile.toURI(), "folder");

        Git git = new Git(db);
        git.add().addFilepattern(".").call();
        git.commit().setMessage("Initial commit").call();
    }

    protected IPath getRandomLocation() {
        return FileSystemHelper.getRandomLocation(FileSystemHelper.getTempDir());
    }

    /**
     * A clone or fetch are long running operations. This method waits until the
     * given task has completed, and then returns the location from the status object.
     */
    protected String waitForTaskCompletion(String taskLocation) throws IOException, SAXException, JSONException {
        JSONObject status = null;
        long start = System.currentTimeMillis();
        while (true) {
            WebRequest request = new GetMethodWebRequest(taskLocation);
            WebResponse response = webConversation.getResponse(request);
            String text = response.getText();
            status = new JSONObject(text);
            if (status.isNull("Running"))
                Assert.fail("Unexpected task format: " + text);
            boolean running = status.getBoolean("Running");
            if (!running)
                break;
            //timeout after reasonable time to avoid hanging tests
            if (System.currentTimeMillis() - start > 10000)
                assertTrue("The operation took too long", false);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                //ignore
            }
        }
        assertNotNull(status);
        return status.getString(ProtocolConstants.KEY_LOCATION);
    }

    protected static String getMethodName() {
        final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
        return ste[3].getMethodName();
    }

    protected static JSONObject getChildByKey(List<JSONObject> children, String key, String value)
            throws JSONException {
        for (JSONObject child : children) {
            if (value.equals(child.getString(key)))
                return child;
        }
        return null;
    }

    protected static JSONObject getChildByName(List<JSONObject> children, String name) throws JSONException {
        return getChildByKey(children, ProtocolConstants.KEY_NAME, name);
    }

    protected static Repository getRepositoryForContentLocation(String fileLocation)
            throws CoreException, IOException {
        assertFileUri(fileLocation);

        URI uri = URI.create(fileLocation);
        IPath path = new Path(uri.getPath());

        WebProject.exists(path.segment(1));
        WebProject wp = WebProject.fromId(path.segment(1));
        IFileStore fsStore = getProjectStore(wp, "test");
        fsStore = fsStore.getFileStore(path.removeFirstSegments(2));

        File file = new File(fsStore.toURI());
        if (RepositoryCache.FileKey.isGitRepository(file, FS.DETECTED)) {
            // 'file' is already what we're looking for
        } else if (RepositoryCache.FileKey.isGitRepository(new File(file, Constants.DOT_GIT), FS.DETECTED)) {
            file = new File(file, Constants.DOT_GIT);
        } else {
            fail(fileLocation + " is not a repository");
        }
        return new FileRepository(file);
    }

    // see org.eclipse.orion.internal.server.servlets.workspace.WorkspaceResourceHandler.generateProjectLocation(WebProject, String)
    private static IFileStore getProjectStore(WebProject project, String user) throws CoreException {
        URI platformLocationURI = Activator.getDefault().getRootLocationURI();
        IFileStore root = EFS.getStore(platformLocationURI);

        //consult layout preference
        IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode("org.eclipse.orion.server.configurator"); //$NON-NLS-1$
        String layout = preferences.get(ServerConstants.CONFIG_FILE_LAYOUT, "flat").toLowerCase(); //$NON-NLS-1$

        IFileStore projectStore;
        if ("usertree".equals(layout) && user != null) { //$NON-NLS-1$
            //the user-tree layout organises projects by the user who created it
            String userPrefix = user.substring(0, Math.min(2, user.length()));
            projectStore = root.getChild(userPrefix).getChild(user).getChild(project.getId());
        } else {
            //default layout is a flat list of projects at the root
            projectStore = root.getChild(project.getId());
        }
        projectStore.mkdir(EFS.NONE, null);
        return projectStore;
    }

    // clone

    protected JSONObject clone(URIish uri, IPath workspacePath, IPath filePath, String name, String kh, char[] p)
            throws JSONException, IOException, SAXException, CoreException {
        // clone
        WebRequest request = getPostGitCloneRequest(uri, workspacePath, filePath, name, kh, p);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        String cloneLocation = waitForTaskCompletion(taskLocation);

        // validate the clone metadata
        response = webConversation.getResponse(getGetRequest(cloneLocation));
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        JSONObject clones = new JSONObject(response.getText());
        JSONArray clonesArray = clones.getJSONArray(ProtocolConstants.KEY_CHILDREN);
        assertEquals(1, clonesArray.length());
        JSONObject clone = clonesArray.getJSONObject(0);
        String n = clone.getString(ProtocolConstants.KEY_NAME);
        if (filePath != null)
            assertTrue(filePath.segmentCount() == 2 && n.equals(WebProject.fromId(filePath.segment(1)).getName())
                    || n.equals(filePath.lastSegment()));
        if (workspacePath != null)
            if (name != null)
                assertEquals(name, n);
            else
                assertEquals(uri.getHumanishName(), n);
        assertCloneUri(clone.getString(ProtocolConstants.KEY_LOCATION));
        assertFileUri(clone.getString(ProtocolConstants.KEY_CONTENT_LOCATION));
        assertRemoteUri(clone.getString(GitConstants.KEY_REMOTE));
        assertBranchUri(clone.getString(GitConstants.KEY_BRANCH));
        return clone;
    }

    // init

    protected JSONObject init(IPath workspacePath, IPath filePath, String name)
            throws JSONException, IOException, SAXException, CoreException {
        // no Git URL for init
        WebRequest request = getPostGitCloneRequest(null, workspacePath, filePath, name, null, null);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        String cloneLocation = waitForTaskCompletion(taskLocation);

        // validate the clone metadata
        response = webConversation.getResponse(getGetRequest(cloneLocation));
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        JSONObject clones = new JSONObject(response.getText());
        JSONArray clonesArray = clones.getJSONArray(ProtocolConstants.KEY_CHILDREN);
        assertEquals(1, clonesArray.length());
        JSONObject clone = clonesArray.getJSONObject(0);
        String n = clone.getString(ProtocolConstants.KEY_NAME);
        if (filePath != null)
            assertTrue(filePath.segmentCount() == 2 && n.equals(WebProject.fromId(filePath.segment(1)).getName())
                    || n.equals(filePath.lastSegment()));
        if (workspacePath != null && name != null)
            assertEquals(name, n);
        assertCloneUri(clone.getString(ProtocolConstants.KEY_LOCATION));
        assertFileUri(clone.getString(ProtocolConstants.KEY_CONTENT_LOCATION));
        assertRemoteUri(clone.getString(GitConstants.KEY_REMOTE));
        assertBranchUri(clone.getString(GitConstants.KEY_BRANCH));
        return clone;
    }

    protected JSONObject clone(IPath workspacePath, IPath filePath, String name)
            throws JSONException, IOException, SAXException, CoreException {
        URIish uri = new URIish(gitDir.toURL());
        return clone(uri, workspacePath, filePath, name, null, null);
    }

    protected JSONObject clone(IPath filePath) throws JSONException, IOException, SAXException, CoreException {
        URIish uri = new URIish(gitDir.toURL());
        return clone(uri, null, filePath, null, null, null);
    }

    // remotes
    protected JSONObject getRemote(String gitRemoteUri, int size, int i, String name)
            throws IOException, SAXException, JSONException {
        assertRemoteUri(gitRemoteUri);
        WebRequest request = GitRemoteTest.getGetGitRemoteRequest(gitRemoteUri);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        JSONObject remotes = new JSONObject(response.getText());
        JSONArray remotesArray = remotes.getJSONArray(ProtocolConstants.KEY_CHILDREN);
        assertEquals(size, remotesArray.length());
        JSONObject remote = remotesArray.getJSONObject(i);
        assertNotNull(remote);
        assertEquals(name, remote.getString(ProtocolConstants.KEY_NAME));
        String remoteLocation = remote.getString(ProtocolConstants.KEY_LOCATION);
        assertNotNull(remoteLocation);
        return remote;
    }

    protected WebResponse addRemote(String remotesLocation, String name, String uri)
            throws JSONException, IOException, SAXException {
        return addRemote(remotesLocation, name, uri, null, null, null);
    }

    protected WebResponse addRemote(String remotesLocation, String name, String uri, String fetchRefSpec,
            String pushUri, String pushRefSpec) throws JSONException, IOException, SAXException {
        assertRemoteUri(remotesLocation);
        WebRequest request = GitRemoteTest.getPostGitRemoteRequest(remotesLocation, name, uri, fetchRefSpec,
                pushUri, pushRefSpec);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_CREATED, response.getResponseCode());
        return response;
    }

    protected JSONObject getRemoteBranch(String gitRemoteUri, int size, int i, String name)
            throws IOException, SAXException, JSONException {
        assertRemoteUri(gitRemoteUri);
        JSONObject remote = getRemote(gitRemoteUri, 1, 0, Constants.DEFAULT_REMOTE_NAME);
        String remoteLocation = remote.getString(ProtocolConstants.KEY_LOCATION);

        WebRequest request = GitRemoteTest.getGetGitRemoteRequest(remoteLocation);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        remote = new JSONObject(response.getText());
        assertNotNull(remote);
        assertEquals(Constants.DEFAULT_REMOTE_NAME, remote.getString(ProtocolConstants.KEY_NAME));
        assertNotNull(remote.getString(ProtocolConstants.KEY_LOCATION));
        JSONArray refsArray = remote.getJSONArray(ProtocolConstants.KEY_CHILDREN);
        assertEquals(size, refsArray.length());
        JSONObject ref = refsArray.getJSONObject(i);
        assertEquals(Constants.R_REMOTES + Constants.DEFAULT_REMOTE_NAME + "/" + name,
                ref.getString(ProtocolConstants.KEY_FULL_NAME));
        String newRefId = ref.getString(ProtocolConstants.KEY_ID);
        assertNotNull(newRefId);
        assertTrue(ObjectId.isId(newRefId));
        String remoteBranchLocation = ref.getString(ProtocolConstants.KEY_LOCATION);
        ref.getString(GitConstants.KEY_COMMIT);

        request = GitRemoteTest.getGetGitRemoteRequest(remoteBranchLocation);
        response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        JSONObject remoteBranch = new JSONObject(response.getText());
        remoteBranch.getString(GitConstants.KEY_COMMIT);
        remoteBranch.getString(GitConstants.KEY_HEAD);

        return remoteBranch;
    }

    protected JSONObject merge(String gitHeadUri, String commit) throws JSONException, IOException, SAXException {
        assertCommitUri(gitHeadUri);
        WebRequest request = getPostGitMergeRequest(gitHeadUri, commit);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    // rebase 

    protected JSONObject rebase(String gitHeadUri, Operation operation)
            throws IOException, SAXException, JSONException {
        assertCommitUri(gitHeadUri);
        WebRequest request = GitRebaseTest.getPostGitRebaseRequest(gitHeadUri, "", operation);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    protected JSONObject rebase(String gitHeadUri, String commit) throws IOException, SAXException, JSONException {
        assertCommitUri(gitHeadUri);
        WebRequest request = GitRebaseTest.getPostGitRebaseRequest(gitHeadUri, commit, null);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    // push

    protected ServerStatus push(String gitRemoteUri, int size, int i, String name, String srcRef, boolean tags)
            throws IOException, SAXException, JSONException {
        return push(gitRemoteUri, size, i, name, srcRef, tags, false);
    }

    protected ServerStatus push(String gitRemoteUri, int size, int i, String name, String srcRef, boolean tags,
            boolean force) throws IOException, SAXException, JSONException {
        assertRemoteUri(gitRemoteUri);
        String remoteBranchLocation = getRemoteBranch(gitRemoteUri, size, i, name)
                .getString(ProtocolConstants.KEY_LOCATION);
        return push(remoteBranchLocation, srcRef, tags, force);
    }

    /**
     * Pushes the changes from the the source ref to the given remote branch.
     * 
     * @param gitRemoteBranchUri remote branch URI
     * @param srcRef the source ref to push
     * @param tags <code>true</code> to push tags
     * @return JSON object representing response
     * @throws IOException
     * @throws SAXException
     * @throws JSONException
     */
    protected ServerStatus push(String gitRemoteBranchUri, String srcRef, boolean tags)
            throws IOException, SAXException, JSONException {
        return push(gitRemoteBranchUri, srcRef, tags, false);
    }

    /**
     * Pushes the changes from the the source ref to the given remote branch.
     * 
     * @param gitRemoteBranchUri remote branch URI
     * @param srcRef the source ref to push
     * @param tags <code>true</code> to push tags
     * @param force <code>true</code> to force push
     * @return JSON object representing response
     * @throws IOException
     * @throws SAXException
     * @throws JSONException
     */
    protected ServerStatus push(String gitRemoteBranchUri, String srcRef, boolean tags, boolean force)
            throws IOException, SAXException, JSONException {
        assertRemoteOrRemoteBranchLocation(gitRemoteBranchUri);
        WebRequest request = GitPushTest.getPostGitRemoteRequest(gitRemoteBranchUri, srcRef, tags, force);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        waitForTaskCompletion(taskLocation);

        // get task details again
        request = new GetMethodWebRequest(taskLocation);
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());

        return ServerStatus.fromJSON(
                new JSONObject(response.getText()).optJSONObject("Result"/*TaskInfo.KEY_RESULT*/).toString());
    }

    protected ServerStatus push(String gitRemoteUri, int size, int i, String name, String srcRef, boolean tags,
            String userName, String kh, byte[] privk, byte[] pubk, byte[] p, boolean shouldBeOK)
            throws IOException, SAXException, JSONException {
        return push(gitRemoteUri, size, i, name, srcRef, tags, false, userName, kh, privk, pubk, p, shouldBeOK);
    }

    protected ServerStatus push(String gitRemoteUri, int size, int i, String name, String srcRef, boolean tags,
            boolean force, String userName, String kh, byte[] privk, byte[] pubk, byte[] p, boolean shouldBeOK)
            throws IOException, SAXException, JSONException {
        assertRemoteUri(gitRemoteUri);
        String remoteBranchLocation = getRemoteBranch(gitRemoteUri, size, i, name)
                .getString(ProtocolConstants.KEY_LOCATION);
        WebRequest request = GitPushTest.getPostGitRemoteRequest(remoteBranchLocation, srcRef, tags, force,
                userName, kh, privk, pubk, p);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        String location = waitForTaskCompletion(taskLocation);

        if (shouldBeOK) {
            request = new GetMethodWebRequest(location);
            response = webConversation.getResponse(request);
            JSONObject status = new JSONObject(response.getText());

            assertFalse(status.getBoolean("Running"));
            assertEquals(HttpURLConnection.HTTP_OK, status.getJSONObject("Result").getInt("HttpCode"));
        }

        // get task details again
        request = new GetMethodWebRequest(taskLocation);
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());

        return ServerStatus.fromJSON(
                new JSONObject(response.getText()).optJSONObject("Result"/*TaskInfo.KEY_RESULT*/).toString());
    }

    /**
     * Fetch objects and refs from the given remote branch. 
     * 
     * @param remoteBranchLocation remote branch URI
     * @return JSONObject representing remote branch after the fetch is done
     * @throws JSONException
     * @throws IOException
     * @throws SAXException
     */
    protected JSONObject fetch(String remoteBranchLocation, String userName, String kh, byte[] privk, byte[] pubk,
            byte[] p, boolean shouldBeOK) throws JSONException, IOException, SAXException {
        return fetch(remoteBranchLocation, false, userName, kh, privk, pubk, p, shouldBeOK);
    }

    /**
     * Fetch objects and refs from the given remote branch. 
     * 
     * @param remoteBranchLocation remote branch URI
     * @param force <code>true</code> to force fetch
     * @return JSONObject representing remote branch after the fetch is done
     * @throws JSONException
     * @throws IOException
     * @throws SAXException
     */
    protected JSONObject fetch(String remoteBranchLocation, boolean force, String userName, String kh, byte[] privk,
            byte[] pubk, byte[] p, boolean shouldBeOK) throws JSONException, IOException, SAXException {
        assertRemoteOrRemoteBranchLocation(remoteBranchLocation);

        // fetch
        WebRequest request = GitFetchTest.getPostGitRemoteRequest(remoteBranchLocation, true, force, userName, kh,
                privk, pubk, p);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        String location = waitForTaskCompletion(taskLocation);

        if (shouldBeOK) {
            request = new GetMethodWebRequest(location);
            response = webConversation.getResponse(request);
            JSONObject status = new JSONObject(response.getText());

            assertFalse(status.getBoolean("Running"));
            assertEquals(HttpURLConnection.HTTP_OK, status.getJSONObject("Result").getInt("HttpCode"));
        }

        // get remote branch details again
        request = GitRemoteTest.getGetGitRemoteRequest(remoteBranchLocation);
        response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    /**
     * Fetch objects and refs from the given remote or remote branch.
     * 
     * @param remoteLocation remote (branch) URI
     * @return JSONObject representing remote (branch) after the fetch is done
     * @throws JSONException
     * @throws IOException
     * @throws SAXException
     */
    protected JSONObject fetch(String remoteLocation) throws JSONException, IOException, SAXException {
        return fetch(remoteLocation, false);
    }

    /**
     * Fetch objects and refs from the given remote or remote branch.
     * 
     * @param remoteLocation remote (branch) URI
     * @param force <code>true</code> to force fetch
     * @return JSONObject representing remote (branch) after the fetch is done
     * @throws JSONException
     * @throws IOException
     * @throws SAXException
     */
    protected JSONObject fetch(String remoteLocation, boolean force)
            throws JSONException, IOException, SAXException {
        assertRemoteOrRemoteBranchLocation(remoteLocation);

        // fetch
        WebRequest request = GitFetchTest.getPostGitRemoteRequest(remoteLocation, true, force);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        String location = waitForTaskCompletion(taskLocation);

        // validate task completed successfully
        request = getGetRequest(location);
        response = webConversation.getResponse(request);
        JSONObject status = new JSONObject(response.getText());
        assertFalse(status.getBoolean("Running"));
        assertEquals(HttpURLConnection.HTTP_OK, status.getJSONObject("Result").getInt("HttpCode"));

        // get remote (branch) details again
        request = GitRemoteTest.getGetGitRemoteRequest(remoteLocation);
        response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    // tag
    /**
     * Tag the commit with given tag name 
     * 
     * @param gitTagUri tags URI
     * @param tagName tag name to use
     * @param commitId commit to tag
     * @return created tag object
     * @throws JSONException
     * @throws IOException
     * @throws SAXException
     * @throws URISyntaxException
     */
    protected JSONObject tag(String gitTagUri, String tagName, String commitId)
            throws JSONException, IOException, SAXException, URISyntaxException {
        assertTagUri(gitTagUri);
        WebRequest request = getPostGitTagRequest(gitTagUri, tagName, commitId);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    /**
     * Tag the selected commit with the given name.
     * 
     * @param gitCommitUri selected commit URI
     * @param tagName tag name to use
     * @return an updated commit object
     * @throws JSONException
     * @throws IOException
     * @throws SAXException
     */
    protected JSONObject tag(String gitCommitUri, String tagName) throws JSONException, IOException, SAXException {
        assertCommitUri(gitCommitUri);
        WebRequest request = getPutGitCommitRequest(gitCommitUri, tagName);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        return new JSONObject(response.getText());
    }

    protected JSONArray listTags(String gitTagUri) throws IOException, SAXException, JSONException {
        assertTagUri(gitTagUri);
        WebRequest request = getGetGitTagRequest(gitTagUri);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        JSONObject tags = new JSONObject(response.getText());
        return tags.getJSONArray(ProtocolConstants.KEY_CHILDREN);
    }

    /**
     * Return commits for the given URI.
     * 
     * @param gitCommitUri commit URI
     * @param remote <code>true</code> if remote location is expected
     * @return
     * @throws IOException
     * @throws SAXException
     * @throws JSONException
     */
    protected JSONArray log(String gitCommitUri, boolean remote) throws IOException, SAXException, JSONException {
        return log(gitCommitUri, remote, null, null);
    }

    /**
     * Return commits for the given URI.
     * 
     * @param gitCommitUri commit URI
     * @param remote <code>true</code> if remote location is expected
     * @param page number of page or <code>null</code> if all results
     * @param pageSize size of page or <code>null</code> if default
     * @return
     * @throws IOException
     * @throws SAXException
     * @throws JSONException
     */
    protected JSONArray log(String gitCommitUri, boolean remote, Integer page, Integer pageSize)
            throws IOException, SAXException, JSONException {
        assertCommitUri(gitCommitUri);
        WebRequest request = GitCommitTest.getGetGitCommitRequest(gitCommitUri, false, page, pageSize);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_OK, response.getResponseCode());
        JSONObject log = new JSONObject(response.getText());
        if (remote) // will fail if the key is not found
            log.getString(GitConstants.KEY_REMOTE);
        return log.getJSONArray(ProtocolConstants.KEY_CHILDREN);
    }

    protected WebResponse branch(String branchesLocation, String branchName)
            throws JGitInternalException, IOException, JSONException, SAXException {
        return branch(branchesLocation, branchName, null);
    }

    // branch
    protected WebResponse branch(String branchesLocation, String branchName, String startPoint)
            throws JGitInternalException, IOException, JSONException, SAXException {
        assertBranchUri(branchesLocation);
        WebRequest request = getPostGitBranchRequest(branchesLocation, branchName, startPoint);
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_CREATED, response.getResponseCode());
        return response;
    }

    static void assertBranchExist(Git git, String branch) {
        List<Ref> list = git.branchList().call();
        for (Ref ref : list) {
            if (ref.getName().equals(Constants.R_HEADS + branch)) {
                return; // found
            }
        }
        fail("branch '" + branch + "' doesn't exist locally");
    }

    protected WebResponse checkoutBranch(String cloneLocation, String branchName)
            throws JSONException, IOException, SAXException {
        String requestURI;
        if (cloneLocation.startsWith("http://")) {
            // assume the caller knows what he's doing
            // assertCloneUri(location);
            requestURI = cloneLocation;
        } else {
            requestURI = SERVER_LOCATION + GIT_SERVLET_LOCATION + GitConstants.CLONE_RESOURCE + cloneLocation;
        }
        JSONObject body = new JSONObject();
        body.put(GitConstants.KEY_BRANCH_NAME, branchName);
        WebRequest request = new PutMethodWebRequest(requestURI, getJsonAsStream(body.toString()), "UTF-8");
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return webConversation.getResponse(request);
    }

    protected String getWorkspaceId(URI uri) throws IOException, SAXException, JSONException {
        assertWorkspaceUri(uri);
        WebRequest request = new GetMethodWebRequest(uri.toString());
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        WebResponse response = webConversation.getResponse(request);
        JSONObject workspace = new JSONObject(response.getText());
        return workspace.getString(ProtocolConstants.KEY_ID);
    }

    // assertions for URIs

    private static void assertRemoteUri(String remoteUri) {
        URI uri = URI.create(remoteUri);
        IPath path = new Path(uri.getPath());
        // /git/remote/file/{path}
        assertTrue(path.segmentCount() > 3);
        assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
        assertEquals(GitConstants.REMOTE_RESOURCE, path.segment(1));
        assertEquals("file", path.segment(2));
    }

    protected static void assertCommitUri(String commitUri) {
        URI uri = URI.create(commitUri);
        IPath path = new Path(uri.getPath());
        AssertionError error = null;
        // /gitapi/commit/{ref}/file/{path}
        try {
            assertTrue(path.segmentCount() > 4);
            assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
            assertEquals(GitConstants.COMMIT_RESOURCE, path.segment(1));
            assertEquals("file", path.segment(3));
        } catch (AssertionError e) {
            error = e;
        }

        // /gitapi/commit/file/{path}
        try {
            assertTrue(path.segmentCount() > 3);
            assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
            assertEquals(GitConstants.COMMIT_RESOURCE, path.segment(1));
            assertEquals("file", path.segment(2));
        } catch (AssertionError e) {
            if (error != null) {
                throw error; // rethrow the first exception
            } // otherwise it's a commit location, ignore this exception
        }
    }

    static void assertStatusUri(String statusUri) {
        URI uri = URI.create(statusUri);
        IPath path = new Path(uri.getPath());
        // /git/status/file/{path}
        assertTrue(path.segmentCount() > 3);
        assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
        assertEquals(GitConstants.STATUS_RESOURCE, path.segment(1));
        assertEquals("file", path.segment(2));
    }

    private static void assertRemoteOrRemoteBranchLocation(String remoteLocation) {
        URI uri = URI.create(remoteLocation);
        IPath path = new Path(uri.getPath());
        AssertionError error = null;
        // /git/remote/{remote}/file/{path}
        try {
            assertTrue(path.segmentCount() > 4);
            assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
            assertEquals(GitConstants.REMOTE_RESOURCE, path.segment(1));
            assertEquals("file", path.segment(3));
        } catch (AssertionError e) {
            error = e;
        }

        // /git/remote/{remote}/{branch}/file/{path}
        try {
            assertTrue(path.segmentCount() > 5);
            assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
            assertEquals(GitConstants.REMOTE_RESOURCE, path.segment(1));
            assertEquals("file", path.segment(4));
        } catch (AssertionError e) {
            if (error != null) {
                throw error; // rethrow the first exception
            } // otherwise it's a remote location, ignore this exception
        }
    }

    private static void assertTagUri(String tagUri) {
        URI uri = URI.create(tagUri);
        IPath path = new Path(uri.getPath());
        // /git/tag/file/{path}
        assertTrue(path.segmentCount() > 3);
        assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
        assertEquals(GitConstants.TAG_RESOURCE, path.segment(1));
        assertEquals("file", path.segment(2));
    }

    static void assertCloneUri(String cloneUri) throws CoreException, IOException {
        URI uri = URI.create(cloneUri);
        IPath path = new Path(uri.getPath());
        // /git/clone/workspace/{id} or /git/clone/file/{id}[/{path}]
        assertTrue(path.segmentCount() > 3);
        assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
        assertEquals(GitConstants.CLONE_RESOURCE, path.segment(1));
        assertTrue("workspace".equals(path.segment(2)) || "file".equals(path.segment(2)));
        if ("workspace".equals(path.segment(2)))
            assertEquals(4, path.segmentCount());
        // TODO: check if clone
        if ("file".equals(path.segment(2)))
            getRepositoryForContentLocation(SERVER_LOCATION + path.removeFirstSegments(2).makeAbsolute());
    }

    private static void assertWorkspaceUri(URI uri) {
        IPath path = new Path(uri.getPath());
        // /workspace/{id}
        assertTrue(path.segmentCount() == 2);
        assertEquals("workspace", path.segment(0));
    }

    private static void assertFileUri(String fileUri) {
        URI uri = URI.create(fileUri);
        IPath path = new Path(uri.getPath());
        // /file/{id}[/{path}]
        assertTrue(path.segmentCount() > 1);
        assertEquals("file", path.segment(0));
    }

    protected static void assertBranchUri(String branchUri) {
        URI uri = URI.create(branchUri);
        IPath path = new Path(uri.getPath());
        // /git/branch/[{name}/]file/{path}
        assertTrue(path.segmentCount() > 3);
        assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
        assertEquals(GitConstants.BRANCH_RESOURCE, path.segment(1));
        assertTrue("file".equals(path.segment(2)) || "file".equals(path.segment(3)));
    }

    protected static void assertConfigUri(String configUri) {
        URI uri = URI.create(configUri);
        IPath path = new Path(uri.getPath());
        // /gitapi/config/[{key}/]clone/file/{id}
        assertTrue(path.segmentCount() > 4);
        assertEquals(GitServlet.GIT_URI.substring(1), path.segment(0));
        assertEquals(GitConstants.CONFIG_RESOURCE, path.segment(1));
        assertTrue(GitConstants.CLONE_RESOURCE.equals(path.segment(2))
                || GitConstants.CLONE_RESOURCE.equals(path.segment(3)));
        if (GitConstants.CLONE_RESOURCE.equals(path.segment(2)))
            assertTrue("file".equals(path.segment(3)));
        else
            assertTrue("file".equals(path.segment(4)));
    }

    protected static void assertGitSectionExists(JSONObject json) {
        JSONObject gitSection = json.optJSONObject(GitConstants.KEY_GIT);
        assertNotNull(gitSection);
        assertNotNull(gitSection.optString(GitConstants.KEY_STATUS, null));
        assertNotNull(gitSection.optString(GitConstants.KEY_DIFF, null));
        assertNotNull(gitSection.optString(GitConstants.KEY_INDEX, null));
        assertNotNull(gitSection.optString(GitConstants.KEY_HEAD, null));
        assertNotNull(gitSection.optString(GitConstants.KEY_REMOTE, null));
        assertNotNull(gitSection.optString(GitConstants.KEY_TAG, null));
        assertNotNull(gitSection.optString(GitConstants.KEY_CLONE, null));
    }

    // web requests

    /**
     * Creates a request to create a git clone for the given URI.
     * @param uri Git URL to clone
     * @param workspacePath workspace path (/workspace/{workspaceId}), required when no filePath is given
     * @param filePath project/folder path
     * @param name new project name
     * @param kh known hosts
     * @param p password
     * @return the request
     * @throws JSONException
     * @throws UnsupportedEncodingException 
     * @throws URISyntaxException 
     */
    protected static WebRequest getPostGitCloneRequest(URIish uri, IPath workspacePath, IPath filePath, String name,
            String kh, char[] p) throws JSONException, UnsupportedEncodingException {
        return new PostGitCloneRequest().setURIish(uri).setWorkspacePath(workspacePath).setFilePath(filePath)
                .setName(name).setKnownHosts(kh).setPassword(p).getWebRequest();
    }

    private static WebRequest getPostGitMergeRequest(String location, String commit)
            throws JSONException, UnsupportedEncodingException {
        String requestURI;
        if (location.startsWith("http://"))
            requestURI = location;
        else
            requestURI = SERVER_LOCATION + GIT_SERVLET_LOCATION + GitConstants.COMMIT_RESOURCE + location;

        JSONObject body = new JSONObject();
        body.put(GitConstants.KEY_MERGE, commit);
        WebRequest request = new PostMethodWebRequest(requestURI, getJsonAsStream(body.toString()), "UTF-8");
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return request;
    }

    private static WebRequest getPostGitTagRequest(String location, String tagName, String commitId)
            throws JSONException, UnsupportedEncodingException {
        assertTagUri(location);
        String requestURI;
        if (location.startsWith("http://"))
            requestURI = location;
        else
            requestURI = SERVER_LOCATION + GIT_SERVLET_LOCATION + GitConstants.TAG_RESOURCE + location;

        JSONObject body = new JSONObject();
        body.put(ProtocolConstants.KEY_NAME, tagName);
        body.put(GitConstants.KEY_TAG_COMMIT, commitId);
        WebRequest request = new PostMethodWebRequest(requestURI, getJsonAsStream(body.toString()),
                "application/json");
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return request;
    }

    private static WebRequest getPutGitCommitRequest(String location, String tagName)
            throws UnsupportedEncodingException, JSONException {
        String requestURI;
        if (location.startsWith("http://"))
            requestURI = location;
        else
            requestURI = SERVER_LOCATION + GIT_SERVLET_LOCATION + GitConstants.COMMIT_RESOURCE + location;
        JSONObject body = new JSONObject();
        body.put(ProtocolConstants.KEY_NAME, tagName);
        WebRequest request = new PutMethodWebRequest(requestURI, getJsonAsStream(body.toString()),
                "application/json");
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return request;
    }

    private static WebRequest getGetGitTagRequest(String location) {
        String requestURI;
        if (location.startsWith("http://"))
            requestURI = location;
        else
            requestURI = SERVER_LOCATION + GIT_SERVLET_LOCATION + GitConstants.TAG_RESOURCE + location;
        WebRequest request = new GetMethodWebRequest(requestURI);
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return request;
    }

    /**
     * Returns a GET request for the given location.
     */
    protected WebRequest getGetRequest(String location) {
        WebRequest request = new GetMethodWebRequest(location);
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return request;
    }

    private WebRequest getPostGitBranchRequest(String location, String branchName, String startPoint)
            throws IOException, JSONException {
        String requestURI;
        if (location.startsWith("http://")) {
            requestURI = location;
        } else {
            requestURI = SERVER_LOCATION + GIT_SERVLET_LOCATION + GitConstants.BRANCH_RESOURCE + location;
        }
        JSONObject body = new JSONObject();
        body.put(ProtocolConstants.KEY_NAME, branchName);
        body.put(GitConstants.KEY_BRANCH_NAME, startPoint);
        WebRequest request = new PostMethodWebRequest(requestURI, getJsonAsStream(body.toString()), "UTF-8");
        request.setHeaderField(ProtocolConstants.HEADER_ORION_VERSION, "1");
        setAuthentication(request);
        return request;
    }

    protected String clone(WebRequest request) throws JSONException, IOException, SAXException {
        WebResponse response = webConversation.getResponse(request);
        assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getResponseCode());
        String taskLocation = response.getHeaderField(ProtocolConstants.HEADER_LOCATION);
        assertNotNull(taskLocation);
        String cloneLocation = waitForTaskCompletion(taskLocation);

        // validate the clone metadata
        response = webConversation.getResponse(getGetRequest(cloneLocation));
        JSONObject clones = new JSONObject(response.getText());
        JSONArray clonesArray = clones.getJSONArray(ProtocolConstants.KEY_CHILDREN);
        assertEquals(1, clonesArray.length());
        JSONObject clone = clonesArray.getJSONObject(0);
        String contentLocation = clone.getString(ProtocolConstants.KEY_CONTENT_LOCATION);
        assertNotNull(contentLocation);
        return contentLocation;
    }

    // Utility methods

    protected static String loadFileContents(String path) throws IOException {
        File file = new File(path);
        InputStream is = new FileInputStream(file);
        return toString(is);
    }

    private static String toString(InputStream is) throws IOException {
        if (is != null) {
            Writer writer = new StringWriter();
            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, n);
                }
            } finally {
                is.close();
            }
            return writer.toString();
        }
        return "";
    }

    protected void assertRepositoryInfo(URIish uri, JSONObject result) throws JSONException {
        assertEquals(uri.toString(), result.getJSONObject("ErrorData").get("Url"));
        if (uri.getUser() != null)
            assertEquals(uri.getUser(), result.getJSONObject("ErrorData").get("User"));
        if (uri.getHost() != null)
            assertEquals(uri.getHost(), result.getJSONObject("ErrorData").get("Host"));
        if (uri.getHumanishName() != null)
            assertEquals(uri.getHumanishName(), result.getJSONObject("ErrorData").get("HumanishName"));
        if (uri.getPass() != null)
            assertEquals(uri.getPass(), result.getJSONObject("ErrorData").get("Password"));
        if (uri.getPort() > 0)
            assertEquals(uri.getPort(), result.getJSONObject("ErrorData").get("Port"));
        if (uri.getScheme() != null)
            assertEquals(uri.getScheme(), result.getJSONObject("ErrorData").get("Scheme"));
    }
}