Merged in feature/file_history_endpoint_v2 (pull request #69)

Feature/file history endpoint v2
This commit is contained in:
Philipp Czora
2018-09-12 07:40:51 +00:00
19 changed files with 666 additions and 18 deletions

View File

@@ -37,6 +37,7 @@ public class RepositoryAccessITCase {
private final String repositoryType;
private File folder;
private RepositoryRequests.AppliedRepositoryGetRequest repositoryGetRequest;
public RepositoryAccessITCase(String repositoryType) {
this.repositoryType = repositoryType;
@@ -48,9 +49,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
@@ -281,5 +288,29 @@ public class RepositoryAccessITCase {
.contains("diff");
}
@Test
@SuppressWarnings("unchecked")
public void shouldFindFileHistory() throws IOException {
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
Changeset changeset = RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "folder/subfolder/a.txt", "a");
repositoryGetRequest
.usingRepositoryResponse()
.requestSources()
.usingSourcesResponse()
.requestSelf("folder")
.usingSourcesResponse()
.requestSelf("subfolder")
.usingSourcesResponse()
.requestFileHistory("a.txt")
.assertStatusCode(HttpStatus.SC_OK)
.usingChangesetsResponse()
.assertChangesets(changesets -> {
assertThat(changesets).hasSize(1);
assertThat(changesets.get(0)).containsEntry("id", changeset.getId());
assertThat(changesets.get(0)).containsEntry("description", changeset.getDescription());
}
);
}
}

View File

@@ -0,0 +1,249 @@
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
* <p>
* 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 <code>url</code> and return the response.
*
* @param url the url of the GET request
* @return the response of the GET request using the given <code>url</code>
*/
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<SELF extends 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<Response> 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> {
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> {
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<List<Map>> changesetsConsumer) {
List<Map> 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<AppliedGetSourcesRequest> {
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<String> assertRevision) {
String revision = sourcesResponse.then().extract().path("revision");
assertRevision.accept(revision);
return this;
}
SourcesResponse assertFiles(Consumer<List> assertFiles) {
List files = sourcesResponse.then().extract().path("files");
assertFiles.accept(files);
return this;
}
AppliedGetChangesetsRequest requestFileHistory(String fileName) {
return new AppliedGetChangesetsRequest(getResponseFromLink(sourcesResponse, "_embedded.files.find{it.name=='" + fileName + "'}._links.history.href"));
}
AppliedGetSourcesRequest requestSelf(String fileName) {
return new AppliedGetSourcesRequest(getResponseFromLink(sourcesResponse, "_embedded.files.find{it.name=='" + fileName + "'}._links.self.href"));
}
}
class AppliedGetDiffRequest extends AppliedGetRequest<AppliedGetDiffRequest> {
AppliedGetDiffRequest(Response response) {
super(response);
}
}
class GivenUrl {
GivenWithUrlAndAuth usernameAndPassword(String username, String password) {
setUsername(username);
setPassword(password);
return new GivenWithUrlAndAuth();
}
}
}

View File

@@ -43,6 +43,7 @@ public class RepositoryUtil {
static Changeset createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
File file = new File(repositoryClient.getWorkingCopy(), fileName);
Files.createParentDirs(file);
Files.write(content, file, Charsets.UTF_8);
addWithParentDirectories(repositoryClient, file);
return commit(repositoryClient, username, "added " + fileName);
@@ -53,7 +54,6 @@ public class RepositoryUtil {
String thisName = file.getName();
String path;
if (!repositoryClient.getWorkingCopy().equals(parent)) {
addWithParentDirectories(repositoryClient, parent);
path = addWithParentDirectories(repositoryClient, parent) + File.separator + thisName;
} else {
path = thisName;

View File

@@ -6,11 +6,12 @@ import sonia.scm.repository.Repository;
import javax.inject.Inject;
import java.util.Optional;
import java.util.function.Supplier;
public class ChangesetCollectionToDtoMapper extends BasicCollectionToDtoMapper<Changeset, ChangesetDto, ChangesetToChangesetDtoMapper> {
private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper;
private final ResourceLinks resourceLinks;
protected final ResourceLinks resourceLinks;
@Inject
public ChangesetCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) {
@@ -20,10 +21,14 @@ public class ChangesetCollectionToDtoMapper extends BasicCollectionToDtoMapper<C
}
public CollectionDto map(int pageNumber, int pageSize, PageResult<Changeset> pageResult, Repository repository) {
return super.map(pageNumber, pageSize, pageResult, createSelfLink(repository), Optional.empty(), changeset -> changesetToChangesetDtoMapper.map(changeset, repository));
return this.map(pageNumber, pageSize, pageResult, repository, () -> createSelfLink(repository));
}
private String createSelfLink(Repository repository) {
public CollectionDto map(int pageNumber, int pageSize, PageResult<Changeset> pageResult, Repository repository, Supplier<String> selfLinkSupplier) {
return super.map(pageNumber, pageSize, pageResult, selfLinkSupplier.get(), Optional.empty(), changeset -> changesetToChangesetDtoMapper.map(changeset, repository));
}
protected String createSelfLink(Repository repository) {
return resourceLinks.changeset().all(repository.getNamespace(), repository.getName());
}
}

View File

@@ -0,0 +1,24 @@
package sonia.scm.api.v2.resources;
import sonia.scm.PageResult;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.Repository;
import javax.inject.Inject;
public class FileHistoryCollectionToDtoMapper extends ChangesetCollectionToDtoMapper {
@Inject
public FileHistoryCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) {
super(changesetToChangesetDtoMapper, resourceLinks);
}
public CollectionDto map(int pageNumber, int pageSize, PageResult<Changeset> pageResult, Repository repository, String revision, String path) {
return super.map(pageNumber, pageSize, pageResult, repository, () -> createSelfLink(repository, revision, path));
}
protected String createSelfLink(Repository repository, String revision, String path) {
return super.resourceLinks.fileHistory().self(repository.getNamespace(), repository.getName(), revision, path);
}
}

View File

@@ -0,0 +1,92 @@
package sonia.scm.api.v2.resources;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import lombok.extern.slf4j.Slf4j;
import sonia.scm.PageResult;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.io.IOException;
@Slf4j
public class FileHistoryRootResource {
private final RepositoryServiceFactory serviceFactory;
private final FileHistoryCollectionToDtoMapper fileHistoryCollectionToDtoMapper;
@Inject
public FileHistoryRootResource(RepositoryServiceFactory serviceFactory, FileHistoryCollectionToDtoMapper fileHistoryCollectionToDtoMapper) {
this.serviceFactory = serviceFactory;
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({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"),
@ResponseCode(code = 404, condition = "not found, no changesets available in the repository"),
@ResponseCode(code = 500, condition = "internal server error")
})
@Produces(VndMediaType.CHANGESET_COLLECTION)
@TypeHint(CollectionDto.class)
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name,
@PathParam("revision") String revision,
@PathParam("path") String path,
@DefaultValue("0") @QueryParam("page") int page,
@DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException, RevisionNotFoundException, RepositoryNotFoundException {
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
log.info("Get changesets of the file {} and revision {}", path, revision);
Repository repository = repositoryService.getRepository();
ChangesetPagingResult changesets = repositoryService.getLogCommand()
.setPagingStart(page)
.setPagingLimit(pageSize)
.setPath(path)
.setStartChangeset(revision)
.getChangesets();
if (changesets != null && changesets.getChangesets() != null) {
PageResult<Changeset> pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal());
return Response.ok(fileHistoryCollectionToDtoMapper.map(page, pageSize, pageResult, repository, revision, path)).build();
} else {
String message = String.format("for the revision %s and the file %s there is no changesets", revision, path);
log.error(message);
throw new InternalRepositoryException(message);
}
}
}
}

View File

@@ -11,6 +11,8 @@ import sonia.scm.repository.SubRepository;
import javax.inject.Inject;
import static de.otto.edison.hal.Link.link;
@Mapper
public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper<FileObject, FileObjectDto> {
@@ -29,6 +31,7 @@ public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper<FileObj
links.self(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path));
} else {
links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path));
links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path)));
}
dto.add(links.build());

View File

@@ -0,0 +1,15 @@
package sonia.scm.api.v2.resources;
import sonia.scm.api.rest.StatusExceptionMapper;
import sonia.scm.repository.InternalRepositoryException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
@Provider
public class InternalRepositoryExceptionMapper extends StatusExceptionMapper<InternalRepositoryException> {
public InternalRepositoryExceptionMapper() {
super(InternalRepositoryException.class, Response.Status.INTERNAL_SERVER_ERROR);
}
}

View File

@@ -40,6 +40,7 @@ public class RepositoryResource {
private final Provider<ContentResource> contentResource;
private final Provider<PermissionRootResource> permissionRootResource;
private final Provider<DiffRootResource> diffRootResource;
private final Provider<FileHistoryRootResource> fileHistoryRootResource;
@Inject
public RepositoryResource(
@@ -50,7 +51,8 @@ public class RepositoryResource {
Provider<ChangesetRootResource> changesetRootResource,
Provider<SourceRootResource> sourceRootResource, Provider<ContentResource> contentResource,
Provider<PermissionRootResource> permissionRootResource,
Provider<DiffRootResource> diffRootResource) {
Provider<DiffRootResource> diffRootResource,
Provider<FileHistoryRootResource> fileHistoryRootResource) {
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
this.manager = manager;
this.repositoryToDtoMapper = repositoryToDtoMapper;
@@ -62,6 +64,7 @@ public class RepositoryResource {
this.contentResource = contentResource;
this.permissionRootResource = permissionRootResource;
this.diffRootResource = diffRootResource;
this.fileHistoryRootResource = fileHistoryRootResource;
}
/**
@@ -165,6 +168,11 @@ public class RepositoryResource {
return changesetRootResource.get();
}
@Path("history/")
public FileHistoryRootResource history() {
return fileHistoryRootResource.get();
}
@Path("sources/")
public SourceRootResource sources() {
return sourceRootResource.get();

View File

@@ -15,6 +15,10 @@ class ResourceLinks {
this.uriInfoStore = uriInfoStore;
}
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
private static String addPath(String sourceWithPath, String path) {
return URI.create(sourceWithPath).resolve(path).toASCIIString();
}
GroupLinks group() {
return new GroupLinks(uriInfoStore.get());
@@ -307,6 +311,23 @@ class ResourceLinks {
}
}
public FileHistoryLinks fileHistory() {
return new FileHistoryLinks(uriInfoStore.get());
}
static class FileHistoryLinks {
private final LinkBuilder fileHistoryLinkBuilder;
FileHistoryLinks(UriInfo uriInfo) {
fileHistoryLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, FileHistoryRootResource.class);
}
String self(String namespace, String name, String changesetId, String path) {
return addPath(fileHistoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("history").parameters().method("getAll").parameters(changesetId, "").href(), path);
}
}
public SourceLinks source() {
return new SourceLinks(uriInfoStore.get());
}
@@ -338,10 +359,7 @@ class ResourceLinks {
return addPath(sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("content").parameters().method("get").parameters(revision, "").href(), path);
}
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
private String addPath(String sourceWithPath, String path) {
return URI.create(sourceWithPath).resolve(path).toASCIIString();
}
}
public PermissionLinks permission() {
return new PermissionLinks(uriInfoStore.get());

View File

@@ -92,7 +92,7 @@ public class BranchRootResourceTest {
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper, changesetCollectionToDtoMapper);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null, null)), null);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null, null, null)), null);
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);

View File

@@ -81,7 +81,7 @@ public class ChangesetRootResourceTest {
changesetRootResource = new ChangesetRootResource(serviceFactory, changesetCollectionToDtoMapper, changesetToChangesetDtoMapper);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
.of(new RepositoryResource(null, null, null, null, null,
MockProvider.of(changesetRootResource), null, null, null, null)), null);
MockProvider.of(changesetRootResource), null, null, null, null, null)), null);
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
@@ -125,7 +125,6 @@ public class ChangesetRootResourceTest {
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
}
@Test
@@ -155,7 +154,6 @@ public class ChangesetRootResourceTest {
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
}
}

View File

@@ -65,7 +65,7 @@ public class DiffResourceTest {
diffRootResource = new DiffRootResource(serviceFactory);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
.of(new RepositoryResource(null, null, null, null, null,
null, null, null, null, MockProvider.of(diffRootResource))), null);
null, null, null, null, MockProvider.of(diffRootResource),null)), null);
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);

View File

@@ -0,0 +1,203 @@
package sonia.scm.api.v2.resources;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Person;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.LogCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.web.VndMediaType;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class)
@Slf4j
public class FileHistoryResourceTest {
public static final String FILE_HISTORY_PATH = "space/repo/history/";
public static final String FILE_HISTORY_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + FILE_HISTORY_PATH;
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private final URI baseUri = URI.create("/");
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
@Mock
private RepositoryServiceFactory serviceFactory;
@Mock
private RepositoryService service;
@Mock
private LogCommandBuilder logCommandBuilder;
private FileHistoryCollectionToDtoMapper fileHistoryCollectionToDtoMapper;
@InjectMocks
private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper;
private FileHistoryRootResource fileHistoryRootResource;
private final Subject subject = mock(Subject.class);
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
@Before
public void prepareEnvironment() throws Exception {
fileHistoryCollectionToDtoMapper = new FileHistoryCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
fileHistoryRootResource = new FileHistoryRootResource(serviceFactory, fileHistoryCollectionToDtoMapper);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
.of(new RepositoryResource(null, null, null, null, null,
null, null, null, null, null, MockProvider.of(fileHistoryRootResource))), null);
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
when(service.getLogCommand()).thenReturn(logCommandBuilder);
subjectThreadState.bind();
ThreadContext.bind(subject);
when(subject.isPermitted(any(String.class))).thenReturn(true);
}
@After
public void cleanupContext() {
ThreadContext.unbindSubject();
}
@Test
public void shouldGetFileHistory() throws Exception {
String id = "revision_123";
String path = "root_dir/sub_dir/file-to-inspect.txt";
Instant creationDate = Instant.now();
String authorName = "name";
String authorEmail = "em@i.l";
String commit = "my branch commit";
ChangesetPagingResult changesetPagingResult = mock(ChangesetPagingResult.class);
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
when(changesetPagingResult.getTotal()).thenReturn(1);
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(200, response.getStatus());
log.info("Response :{}", response.getContentAsString());
assertTrue(response.getContentAsString().contains(String.format("\"id\":\"%s\"", id)));
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
}
@Test
public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException {
when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class);
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + "revision/a.txt")
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(404, response.getStatus());
}
@Test
public void shouldGet404OnMissingRevision() throws Exception {
String id = "revision_123";
String path = "root_dir/sub_dir/file-to-inspect.txt";
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenThrow(RevisionNotFoundException.class);
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(404, response.getStatus());
}
@Test
public void shouldGet500OnInternalRepositoryException() throws Exception {
String id = "revision_123";
String path = "root_dir/sub_dir/file-to-inspect.txt";
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenThrow(InternalRepositoryException.class);
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(500, response.getStatus());
}
@Test
public void shouldGet500OnNullChangesets() throws Exception {
String id = "revision_123";
String path = "root_dir/sub_dir/file-to-inspect.txt";
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenReturn(null);
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(500, response.getStatus());
}
}

View File

@@ -138,7 +138,7 @@ public class PermissionRootResourceTest {
permissionCollectionToDtoMapper = new PermissionCollectionToDtoMapper(permissionToPermissionDtoMapper, resourceLinks);
permissionRootResource = new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, permissionCollectionToDtoMapper, resourceLinks, repositoryManager);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
.of(new RepositoryResource(null, null, null, null, null, null, null, null, MockProvider.of(permissionRootResource), null)), null);
.of(new RepositoryResource(null, null, null, null, null, null, null, null, MockProvider.of(permissionRootResource), null, null)), null);
dispatcher = createDispatcher(repositoryRootResource);
subjectThreadState.bind();
ThreadContext.bind(subject);

View File

@@ -79,7 +79,7 @@ public class RepositoryRootResourceTest {
@Before
public void prepareEnvironment() {
initMocks(this);
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null, null);
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null, null, null);
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));

View File

@@ -22,6 +22,7 @@ public class ResourceLinksMock {
when(resourceLinks.tag()).thenReturn(new ResourceLinks.TagCollectionLinks(uriInfo));
when(resourceLinks.branchCollection()).thenReturn(new ResourceLinks.BranchCollectionLinks(uriInfo));
when(resourceLinks.changeset()).thenReturn(new ResourceLinks.ChangesetLinks(uriInfo));
when(resourceLinks.fileHistory()).thenReturn(new ResourceLinks.FileHistoryLinks(uriInfo));
when(resourceLinks.source()).thenReturn(new ResourceLinks.SourceLinks(uriInfo));
when(resourceLinks.permission()).thenReturn(new ResourceLinks.PermissionLinks(uriInfo));
when(resourceLinks.config()).thenReturn(new ResourceLinks.ConfigLinks(uriInfo));

View File

@@ -73,6 +73,7 @@ public class SourceRootResourceTest {
MockProvider.of(sourceRootResource),
null,
null,
null,
null)),
null);
dispatcher = createDispatcher(repositoryRootResource);

View File

@@ -74,7 +74,7 @@ public class TagRootResourceTest {
tagRootResource = new TagRootResource(serviceFactory, tagCollectionToDtoMapper, tagToTagDtoMapper);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
.of(new RepositoryResource(null, null, null, MockProvider.of(tagRootResource), null,
null, null, null, null, null)), null);
null, null, null, null, null, null)), null);
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);