mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-17 18:51:10 +01:00
implemented integration tests for lfs
This commit is contained in:
343
scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java
Normal file
343
scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010, Sebastian Sdorra
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package sonia.scm.it;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.sun.jersey.api.client.Client;
|
||||||
|
import com.sun.jersey.api.client.UniformInterfaceException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import org.apache.shiro.crypto.hash.Sha256Hash;
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
|
import static sonia.scm.it.IntegrationTestUtil.*;
|
||||||
|
import static sonia.scm.it.RepositoryITUtil.*;
|
||||||
|
import sonia.scm.repository.Permission;
|
||||||
|
import sonia.scm.repository.PermissionType;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryTestData;
|
||||||
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.user.UserTestData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for git lfs.
|
||||||
|
*
|
||||||
|
* @author Sebastian Sdorra
|
||||||
|
*/
|
||||||
|
public class GitLfsITCase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException expectedException = ExpectedException.none();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
private final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private Client adminClient;
|
||||||
|
|
||||||
|
private Repository repository;
|
||||||
|
|
||||||
|
public GitLfsITCase() {
|
||||||
|
mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector());
|
||||||
|
}
|
||||||
|
|
||||||
|
// lifecycle methods
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUpTestDependencies() {
|
||||||
|
adminClient = createAdminClient();
|
||||||
|
repository = createRepository(adminClient, RepositoryTestData.createHeartOfGold("git"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDownTestDependencies() {
|
||||||
|
deleteRepository(adminClient, repository.getId());
|
||||||
|
adminClient.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLfsAPIWithAdminPermissions() throws IOException {
|
||||||
|
uploadAndDownload(adminClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLfsAPIWithOwnerPermissions() throws IOException {
|
||||||
|
uploadAndDownloadAsUser(PermissionType.OWNER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadAndDownloadAsUser(PermissionType permissionType) throws IOException {
|
||||||
|
User trillian = UserTestData.createTrillian();
|
||||||
|
trillian.setPassword("secret123");
|
||||||
|
createUser(trillian);
|
||||||
|
|
||||||
|
try {
|
||||||
|
repository.getPermissions().add(new Permission(trillian.getId(), permissionType));
|
||||||
|
modifyRepository(repository);
|
||||||
|
|
||||||
|
Client client = createClient();
|
||||||
|
authenticate(client, trillian.getId(), "secret123");
|
||||||
|
|
||||||
|
uploadAndDownload(client);
|
||||||
|
} finally {
|
||||||
|
removeUser(trillian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLfsAPIWithWritePermissions() throws IOException {
|
||||||
|
uploadAndDownloadAsUser(PermissionType.WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUser(User user) {
|
||||||
|
adminClient.resource(REST_BASE_URL + "users.json").post(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void modifyRepository(Repository repository) {
|
||||||
|
adminClient.resource(REST_BASE_URL + "repositories/" + repository.getId() + ".json").put(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeUser(User user) {
|
||||||
|
adminClient.resource(REST_BASE_URL + "users/" + user.getId() + ".json").delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLfsAPIWithoutWritePermissions() throws IOException {
|
||||||
|
User trillian = UserTestData.createTrillian();
|
||||||
|
trillian.setPassword("secret123");
|
||||||
|
createUser(trillian);
|
||||||
|
|
||||||
|
expectedException.expect(UniformInterfaceException.class);
|
||||||
|
expectedException.expectMessage(Matchers.containsString("403"));
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
repository.getPermissions().add(new Permission(trillian.getId(), PermissionType.READ));
|
||||||
|
modifyRepository(repository);
|
||||||
|
|
||||||
|
Client client = createClient();
|
||||||
|
authenticate(client, trillian.getId(), "secret123");
|
||||||
|
|
||||||
|
uploadAndDownload(client);
|
||||||
|
} finally {
|
||||||
|
removeUser(trillian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLfsDownloadWithReadPermissions() throws IOException {
|
||||||
|
User trillian = UserTestData.createTrillian();
|
||||||
|
trillian.setPassword("secret123");
|
||||||
|
createUser(trillian);
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
repository.getPermissions().add(new Permission(trillian.getId(), PermissionType.READ));
|
||||||
|
modifyRepository(repository);
|
||||||
|
|
||||||
|
// upload data as admin
|
||||||
|
String data = UUID.randomUUID().toString();
|
||||||
|
byte[] dataAsBytes = data.getBytes(Charsets.UTF_8);
|
||||||
|
LfsObject lfsObject = upload(adminClient, dataAsBytes);
|
||||||
|
|
||||||
|
Client client = createClient();
|
||||||
|
authenticate(client, trillian.getId(), "secret123");
|
||||||
|
|
||||||
|
// download as user
|
||||||
|
byte[] downloadedData = download(client, lfsObject);
|
||||||
|
|
||||||
|
// assert both are equal
|
||||||
|
assertArrayEquals(dataAsBytes, downloadedData);
|
||||||
|
} finally {
|
||||||
|
removeUser(trillian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lfs api
|
||||||
|
|
||||||
|
private void uploadAndDownload(Client client) throws IOException {
|
||||||
|
String data = UUID.randomUUID().toString();
|
||||||
|
byte[] dataAsBytes = data.getBytes(Charsets.UTF_8);
|
||||||
|
LfsObject lfsObject = upload(client, dataAsBytes);
|
||||||
|
byte[] downloadedData = download(client, lfsObject);
|
||||||
|
assertArrayEquals(dataAsBytes, downloadedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LfsObject upload(Client client, byte[] data) throws IOException {
|
||||||
|
LfsObject lfsObject = createLfsObject(data);
|
||||||
|
LfsRequestBody request = LfsRequestBody.createUploadRequest(lfsObject);
|
||||||
|
LfsResponseBody response = request(client, request);
|
||||||
|
|
||||||
|
String uploadURL = response.objects[0].actions.upload.href;
|
||||||
|
client.resource(uploadURL).put(data);
|
||||||
|
|
||||||
|
return lfsObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LfsResponseBody request(Client client, LfsRequestBody request) throws IOException {
|
||||||
|
String batchUrl = createBatchUrl();
|
||||||
|
String requestAsString = mapper.writeValueAsString(request);
|
||||||
|
|
||||||
|
return client
|
||||||
|
.resource(batchUrl)
|
||||||
|
.accept("application/vnd.git-lfs+json")
|
||||||
|
.header("Content-Type", "application/vnd.git-lfs+json")
|
||||||
|
.post(LfsResponseBody.class, requestAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createBatchUrl() {
|
||||||
|
String url = repository.createUrl(BASE_URL);
|
||||||
|
return url + "/info/lfs/objects/batch";
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] download(Client client, LfsObject lfsObject) throws IOException {
|
||||||
|
LfsRequestBody request = LfsRequestBody.createDownloadRequest(lfsObject);
|
||||||
|
LfsResponseBody response = request(client, request);
|
||||||
|
|
||||||
|
String downloadUrl = response.objects[0].actions.download.href;
|
||||||
|
return client.resource(downloadUrl).get(byte[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LfsObject createLfsObject(byte[] data) {
|
||||||
|
Sha256Hash hash = new Sha256Hash(data);
|
||||||
|
String oid = hash.toHex();
|
||||||
|
return new LfsObject(oid, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LFS DTO objects
|
||||||
|
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
private static class LfsRequestBody {
|
||||||
|
|
||||||
|
private String operation;
|
||||||
|
private String[] transfers = new String[]{ "basic" };
|
||||||
|
private LfsObject[] objects;
|
||||||
|
|
||||||
|
public LfsRequestBody() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private LfsRequestBody(String operation, LfsObject[] objects) {
|
||||||
|
this.operation = operation;
|
||||||
|
this.objects = objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LfsRequestBody createUploadRequest(LfsObject object) {
|
||||||
|
return new LfsRequestBody("upload", new LfsObject[]{object});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LfsRequestBody createDownloadRequest(LfsObject object) {
|
||||||
|
return new LfsRequestBody("download", new LfsObject[]{object});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
private static class LfsResponseBody {
|
||||||
|
|
||||||
|
private LfsObject[] objects;
|
||||||
|
|
||||||
|
public LfsResponseBody() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public LfsResponseBody(LfsObject[] objects) {
|
||||||
|
this.objects = objects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
private static class LfsObject {
|
||||||
|
|
||||||
|
private String oid;
|
||||||
|
private long size;
|
||||||
|
private LfsActions actions;
|
||||||
|
|
||||||
|
public LfsObject() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public LfsObject(String oid, long size) {
|
||||||
|
this.oid = oid;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LfsObject(String oid, long size, LfsActions actions) {
|
||||||
|
this.oid = oid;
|
||||||
|
this.size = size;
|
||||||
|
this.actions = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
private static class LfsActions {
|
||||||
|
|
||||||
|
private LfsAction upload;
|
||||||
|
private LfsAction download;
|
||||||
|
|
||||||
|
public LfsActions() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
private static class LfsAction {
|
||||||
|
|
||||||
|
private String href;
|
||||||
|
|
||||||
|
public LfsAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public LfsAction(String href) {
|
||||||
|
this.href = href;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user