Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CloneDevOpsTemplate/Controllers/RepositoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ public async Task<IActionResult> ProjectRepositories(Guid projectId)
return View("Repositories", repositories.Value);
}

public async Task<IActionResult> PullRequests(Guid projectId)
{
GitPullRequests pullRequests = new();

if (!ModelState.IsValid)
{
return View(pullRequests.Value);
}

pullRequests = await _repositoryService.GetGitPullRequest(projectId) ?? new();
return View(pullRequests.Value);
}

[HttpGet]
public async Task<IActionResult> CloneRepository()
{
Expand Down
1 change: 1 addition & 0 deletions CloneDevOpsTemplate/IServices/IRepositoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public interface IRepositoryService
Task<HttpResponseMessage> DeleteRepositoryAsync(Guid projectId, Guid repositoryId);
Task<GitImportRequest?> CreateImportRequestAsync(Guid projectId, Guid repositoryId, string sourceRepositoryRemoteUrl, Guid serviceEndpointId);
Task<GitImportRequest?> GetImportRequestAsync(Guid projectId, Guid repositoryId, int importRequestId);
Task<GitPullRequests?> GetGitPullRequest(Guid projectId);
}
3 changes: 2 additions & 1 deletion CloneDevOpsTemplate/Models/Projects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public class ProcessTemplate
public enum Visibility
{
Private,
Public
Public,
Unchanged
}

[JsonConverter(typeof(JsonStringEnumConverter<ProjectState>))]
Expand Down
57 changes: 57 additions & 0 deletions CloneDevOpsTemplate/Models/PullRequests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Text.Json.Serialization;

namespace CloneDevOpsTemplate.Models;

public class GitPullRequests
{
public int Count { get; set; }
public GitPullRequest[] Value { get; set; } = [];
}

public class GitPullRequest
{
public Repository Repository { get; set; } = new();
public int PullRequestId { get; set; }
public int CodeReviewId { get; set; }
public PullRequestStatus Status { get; set; }
public User CreatedBy { get; set; } = new();
public DateTime CreationDate { get; set; }
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string SourceRefName { get; set; } = string.Empty;
public string TargetRefName { get; set; } = string.Empty;
public PullRequestAsyncStatus MergeStatus { get; set; }
public bool IsDraft { get; set; }
public Guid MergeId { get; set; }
public GitCommitRef LastMergeSourceCommit { get; set; } = new();
public GitCommitRef LastMergeTargetCommit { get; set; } = new();
public GitCommitRef LastMergeCommit { get; set; } = new();
public User[] Reviewers { get; set; } = [];
public bool SupportsIterations { get; set; }
}

[JsonConverter(typeof(JsonStringEnumConverter<PullRequestStatus>))]
public enum PullRequestStatus
{
Abandoned,
Active,
All,
Completed,
NotSet
}

[JsonConverter(typeof(JsonStringEnumConverter<PullRequestAsyncStatus>))]
public enum PullRequestAsyncStatus
{
Conflicts,
Failure,
NotSet,
Queued,
RejectedByPolicy,
Succeeded
}

public class GitCommitRef
{
public string CommitId { get; set; } = string.Empty;
}
5 changes: 5 additions & 0 deletions CloneDevOpsTemplate/Services/RepositoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ public Task<HttpResponseMessage> DeleteRepositoryAsync(Guid projectId, Guid repo
{
return _client.GetFromJsonAsync<GitImportRequest>($"{projectId}/_apis/git/repositories/{repositoryId}/importRequests/{importRequestId}");
}

public Task<GitPullRequests?> GetGitPullRequest(Guid projectId)
{
return _client.GetFromJsonAsync<GitPullRequests>($"{projectId}/_apis/git/pullrequests");
}
}
3 changes: 3 additions & 0 deletions CloneDevOpsTemplate/Views/Project/Project.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,7 @@
<tr>
<td colspan="2">@Html.ActionLink("WorkItems", "WorkItems", "WorkItems", new { projectId = Model.Id, projectName = Model.Name })</td>
</tr>
<tr>
<td colspan="2">@Html.ActionLink("Pull requests", "PullRequests", "Repository", new { projectId = Model.Id })</td>
</tr>
</table>
34 changes: 34 additions & 0 deletions CloneDevOpsTemplate/Views/Repository/PullRequests.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@model GitPullRequest[]
@{
ViewData["Title"] = "Pull Requests";
}

<h1>Pull Requests</h1>

@Html.ValidationSummary(false, "", new { @class = "text-danger" })

<table class="table">
<thead>
<tr>
<th>Title</th>
<th>CreatedBy</th>
<th>Repository</th>
<th>Source</th>
<th>Target</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach (var pullRequest in Model)
{
<tr>
<td>@pullRequest.Title</td>
<td>@pullRequest.CreatedBy.DisplayName</td>
<td>@pullRequest.Repository.Name</td>
<td>@pullRequest.SourceRefName</td>
<td>@pullRequest.TargetRefName</td>
<td>@pullRequest.Status</td>
</tr>
}
</tbody>
</table>
52 changes: 52 additions & 0 deletions CloneDevOpsTemplateTest/Controllers/RepositoryControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,56 @@ public async Task CloneRepository_ValidModelState_ClonesRepositoriesAndReturnsVi
Assert.Equal(mockProjects.Value, viewResult.Model);
Assert.Equal("Success", _controller.ViewBag.SuccessMessage);
}

[Fact]
public async Task PullRequests_InvalidModelState_ReturnsDefaultView()
{
// Arrange
_controller.ModelState.AddModelError("Error", "Invalid model state");
var mockPullRequests = new GitPullRequests { Value = Array.Empty<GitPullRequest>() };

// Act
var result = await _controller.PullRequests(Guid.NewGuid());

// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var viewModel = Assert.IsType<GitPullRequest[]>(viewResult.Model);
Assert.Empty(viewModel);
}

[Fact]
public async Task PullRequests_ReturnsViewWithPullRequests()
{
// Arrange
var projectId = Guid.NewGuid();
var mockPullRequests = new GitPullRequests { Value = Array.Empty<GitPullRequest>() };
_mockRepositoryService
.Setup(service => service.GetGitPullRequest(projectId))
.ReturnsAsync(mockPullRequests);

// Act
var result = await _controller.PullRequests(projectId);

// Assert
var viewResult = Assert.IsType<ViewResult>(result);
Assert.Equal(mockPullRequests.Value, viewResult.Model);
}

[Fact]
public async Task PullRequests_ReturnsViewWithEmptyPullRequests_WhenServiceReturnsNull()
{
// Arrange
var projectId = Guid.NewGuid();
_mockRepositoryService
.Setup(service => service.GetGitPullRequest(projectId))
.ReturnsAsync((GitPullRequests?)null);

// Act
var result = await _controller.PullRequests(projectId);

// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var viewModel = Assert.IsType<GitPullRequest[]>(viewResult.Model);
Assert.Empty(viewModel);
}
}
58 changes: 58 additions & 0 deletions CloneDevOpsTemplateTest/Services/RepositoryServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,62 @@ public async Task CreateImportRequestAsync_ReturnsNullOnFailure()
// Act & Assert
await Assert.ThrowsAsync<JsonException>(async () => await _repositoryService.CreateImportRequestAsync(projectId, repositoryId, sourceRepositoryRemoteUrl, serviceEndpointId));
}

[Fact]
public async Task GetGitPullRequest_ReturnsGitPullRequests()
{
// Arrange
var projectId = Guid.NewGuid();
var gitPullRequests = new GitPullRequests
{
Count = 2,
Value =
[
new GitPullRequest { PullRequestId = 1, Title = "PR 1" },
new GitPullRequest { PullRequestId = 2, Title = "PR 2" }
]
};

_httpMessageHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = JsonContent.Create(gitPullRequests)
});

// Act
var result = await _repositoryService.GetGitPullRequest(projectId);

// Assert
Assert.NotNull(result);
Assert.Equal(gitPullRequests.Count, result.Count);
Assert.Equal(gitPullRequests.Value[0].PullRequestId, result.Value[0].PullRequestId);
Assert.Equal(gitPullRequests.Value[1].PullRequestId, result.Value[1].PullRequestId);
}

[Fact]
public async Task GetGitPullRequest_ReturnsNullOnFailure()
{
// Arrange
var projectId = Guid.NewGuid();

_httpMessageHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest
});

// Act & Assert
await Assert.ThrowsAsync<HttpRequestException>(() => _repositoryService.GetGitPullRequest(projectId));
}
}