From 8ec7677b754945d036713b5c41753193d0c6b2f0 Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Mon, 10 Sep 2018 09:51:59 +0200 Subject: [PATCH] integration tests for the file history endpoint --- .../sonia/scm/it/RepositoryAccessITCase.java | 37 ++- .../java/sonia/scm/it/RepositoryRequests.java | 245 ++++++++++++++++++ .../java/sonia/scm/it/RepositoryUtil.java | 4 +- .../v2/resources/FileHistoryRootResource.java | 14 + 4 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 scm-it/src/test/java/sonia/scm/it/RepositoryRequests.java diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index a9139f18c8..e3603d258b 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import sonia.scm.repository.Changeset; import sonia.scm.repository.client.api.ClientCommand; import sonia.scm.repository.client.api.RepositoryClient; @@ -16,10 +17,12 @@ import java.io.IOException; import java.util.Collection; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; import static java.lang.Thread.sleep; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertNotNull; +import static sonia.scm.it.RestUtil.ADMIN_PASSWORD; +import static sonia.scm.it.RestUtil.ADMIN_USERNAME; import static sonia.scm.it.RestUtil.given; import static sonia.scm.it.ScmTypes.availableScmTypes; @@ -31,6 +34,7 @@ public class RepositoryAccessITCase { private final String repositoryType; private File folder; + private RepositoryRequests.AppliedRepositoryGetRequest repositoryGetRequest; public RepositoryAccessITCase(String repositoryType) { this.repositoryType = repositoryType; @@ -42,9 +46,15 @@ public class RepositoryAccessITCase { } @Before - public void initClient() { + public void init() { TestData.createDefault(); folder = tempFolder.getRoot(); + repositoryGetRequest = RepositoryRequests.start() + .given() + .url(TestData.getDefaultRepositoryUrl(repositoryType)) + .usernameAndPassword(ADMIN_USERNAME, ADMIN_PASSWORD) + .get() + .assertStatusCode(HttpStatus.SC_OK); } @Test @@ -152,5 +162,28 @@ public class RepositoryAccessITCase { assertThat(changesets).size().isBetween(2, 3); // svn has an implicit root revision '0' that is extra to the two commits } + + @Test + @SuppressWarnings("unchecked") + public void shouldFindFileHistory() throws IOException { + RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder); + + String fileName_1 = "a.txt"; + Changeset changeset_1 = RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, fileName_1, "a"); + + repositoryGetRequest + .usingRepositoryResponse() + .requestSources() + .usingSourcesResponse() + .requestFileHistory(fileName_1) + .assertStatusCode(HttpStatus.SC_OK) + .usingChangesetsResponse() + .assertChangesets(changesets -> { + assertThat(changesets).hasSize(1); + assertThat(changesets.get(0)).containsEntry("id", changeset_1.getId()); + assertThat(changesets.get(0)).containsEntry("description", changeset_1.getDescription()); + } + ); + } } diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryRequests.java b/scm-it/src/test/java/sonia/scm/it/RepositoryRequests.java new file mode 100644 index 0000000000..62ce82aded --- /dev/null +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryRequests.java @@ -0,0 +1,245 @@ +package sonia.scm.it; + +import io.restassured.RestAssured; +import io.restassured.response.Response; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + + +/** + * Encapsulate rest requests of a repository in builder pattern + *

+ * A Get Request can be applied with the methods request*() + * These methods return a AppliedGet*Request object + * This object can be used to apply general assertions over the rest Assured response + * In the AppliedGet*Request classes there is a using*Response() method + * that return the *Response class containing specific operations related to the specific response + * the *Response class contains also the request*() method to apply the next GET request from a link in the response. + */ +public class RepositoryRequests { + + private String url; + private String username; + private String password; + + static RepositoryRequests start() { + return new RepositoryRequests(); + } + + Given given() { + return new Given(); + } + + + /** + * Apply a GET Request to the extracted url from the given link + * + * @param linkPropertyName the property name of link + * @param response the response containing the link + * @return the response of the GET request using the given link + */ + private Response getResponseFromLink(Response response, String linkPropertyName) { + return getResponse(response + .then() + .extract() + .path(linkPropertyName)); + } + + + /** + * Apply a GET Request to the given url and return the response. + * + * @param url the url of the GET request + * @return the response of the GET request using the given url + */ + private Response getResponse(String url) { + return RestAssured.given() + .auth().preemptive().basic(username, password) + .when() + .get(url); + } + + private void setUrl(String url) { + this.url = url; + } + + private void setUsername(String username) { + this.username = username; + } + + private void setPassword(String password) { + this.password = password; + } + + private String getUrl() { + return url; + } + + private String getUsername() { + return username; + } + + private String getPassword() { + return password; + } + + class Given { + + GivenUrl url(String url) { + setUrl(url); + return new GivenUrl(); + } + + } + + class GivenWithUrlAndAuth { + AppliedRepositoryGetRequest get() { + return new AppliedRepositoryGetRequest( + getResponse(url) + ); + } + } + + class AppliedGetRequest { + private Response response; + + public AppliedGetRequest(Response response) { + this.response = response; + } + + /** + * apply custom assertions to the actual response + * + * @param consumer consume the response in order to assert the content. the header, the payload etc.. + * @return the self object + */ + SELF assertResponse(Consumer consumer) { + consumer.accept(response); + return (SELF) this; + } + + /** + * special assertion of the status code + * + * @param expectedStatusCode the expected status code + * @return the self object + */ + SELF assertStatusCode(int expectedStatusCode) { + this.response.then().assertThat().statusCode(expectedStatusCode); + return (SELF) this; + } + + } + + class AppliedRepositoryGetRequest extends AppliedGetRequest { + + AppliedRepositoryGetRequest(Response response) { + super(response); + } + + RepositoryResponse usingRepositoryResponse() { + return new RepositoryResponse(super.response); + } + } + + class RepositoryResponse { + + private Response repositoryResponse; + + public RepositoryResponse(Response repositoryResponse) { + this.repositoryResponse = repositoryResponse; + } + + AppliedGetSourcesRequest requestSources() { + return new AppliedGetSourcesRequest(getResponseFromLink(repositoryResponse, "_links.sources.href")); + } + + AppliedGetChangesetsRequest requestChangesets(String fileName) { + return new AppliedGetChangesetsRequest(getResponseFromLink(repositoryResponse, "_links.changesets.href")); + } + } + + class AppliedGetChangesetsRequest extends AppliedGetRequest { + + AppliedGetChangesetsRequest(Response response) { + super(response); + } + + ChangesetsResponse usingChangesetsResponse() { + return new ChangesetsResponse(super.response); + } + } + + class ChangesetsResponse { + private Response changesetsResponse; + + public ChangesetsResponse(Response changesetsResponse) { + this.changesetsResponse = changesetsResponse; + } + + ChangesetsResponse assertChangesets(Consumer> changesetsConsumer) { + List changesets = changesetsResponse.then().extract().path("_embedded.changesets"); + changesetsConsumer.accept(changesets); + return this; + } + + AppliedGetDiffRequest requestDiff(String revision) { + return new AppliedGetDiffRequest(getResponseFromLink(changesetsResponse, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href")); + } + + } + + class AppliedGetSourcesRequest extends AppliedGetRequest { + + public AppliedGetSourcesRequest(Response sourcesResponse) { + super(sourcesResponse); + } + + SourcesResponse usingSourcesResponse() { + return new SourcesResponse(super.response); + } + } + + class SourcesResponse { + + private Response sourcesResponse; + + SourcesResponse(Response sourcesResponse) { + this.sourcesResponse = sourcesResponse; + } + + SourcesResponse assertRevision(Consumer assertRevision) { + String revision = sourcesResponse.then().extract().path("revision"); + assertRevision.accept(revision); + return this; + } + + SourcesResponse assertFiles(Consumer assertFiles) { + List files = sourcesResponse.then().extract().path("files"); + assertFiles.accept(files); + return this; + } + + AppliedGetChangesetsRequest requestFileHistory(String fileName) { + return new AppliedGetChangesetsRequest(getResponseFromLink(sourcesResponse, "files.find{it.name=='" + fileName + "'}._links.history.href")); + } + } + + class AppliedGetDiffRequest extends AppliedGetRequest { + + AppliedGetDiffRequest(Response response) { + super(response); + } + } + + class GivenUrl { + + GivenWithUrlAndAuth usernameAndPassword(String username, String password) { + setUsername(username); + setPassword(password); + return new GivenWithUrlAndAuth(); + } + } +} diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java index e49927b1b9..b2d0f44578 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java @@ -40,11 +40,11 @@ public class RepositoryUtil { return name; } - static void createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException { + static Changeset createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException { File file = new File(repositoryClient.getWorkingCopy(), fileName); Files.write(content, file, Charsets.UTF_8); addWithParentDirectories(repositoryClient, file); - commit(repositoryClient, username, "added " + fileName); + return commit(repositoryClient, username, "added " + fileName); } private static String addWithParentDirectories(RepositoryClient repositoryClient, File file) throws IOException { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java index d56590b384..118cc4167a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java @@ -40,6 +40,20 @@ public class FileHistoryRootResource { this.fileHistoryCollectionToDtoMapper = fileHistoryCollectionToDtoMapper; } + /** + * Get all changesets related to the given file starting with the given revision + * + * @param namespace the repository namespace + * @param name the repository name + * @param revision the revision + * @param path the path of the file + * @param page pagination + * @param pageSize pagination + * @return all changesets related to the given file starting with the given revision + * @throws IOException on io error + * @throws RevisionNotFoundException on missing revision + * @throws RepositoryNotFoundException on missing repository + */ @GET @Path("{revision}/{path: .*}") @StatusCodes({