Skip to content

Commit

Permalink
feat: create pull request api (#9)
Browse files Browse the repository at this point in the history
* chore: rename CreateBranchResponseDto.cs to BranchResponseDto.cs

* chore: move BranchResponseDto.cs to Common directory

* chore: implement get branch list api

* chore: write unit tests

* chore: implement create pull request api; except its response dto

* chore: write unit tests

* chore: add pull request facade to gitea client

* chore: write integration test for get branch list api

* chore: add response dto

* chore: add integration test

* chore: add auto-init field to create repo

* chore: remove class fixtures

* chore: resolve warning

* fix: integration tests

* chore: add cancellation token

* fix: use unique names for each integration tests
  • Loading branch information
HamedSY authored Jul 25, 2024
1 parent 3925652 commit f0d0d41
Show file tree
Hide file tree
Showing 21 changed files with 538 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ public CreateBranchTests(GiteaCollectionFixture giteaCollectionFixture)
public async Task CreateBranch_ShouldCreateBranchWithCreatedStatusCode_WhenInputsAreProvidedProperly()
{
// Arrange
const string repositoryName = GiteaTestConstants.RepositoryName;
const string repositoryName = "create_branch_repo";
await _repositoryCreator.CreateRepositoryAsync(repositoryName, _giteaCollectionFixture.CancellationToken);

const string newBranchName = "feature/test_new_branch";
const string newBranchName = "create_branch_branch";
var createBranchCommandDto = new CreateBranchCommandDto
{
RepositoryName = repositoryName,
NewBranchName = newBranchName,
OldReferenceName = "main"
OldReferenceName = GiteaTestConstants.DefaultBranch
};

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public GetBranchListTests(GiteaCollectionFixture giteaCollectionFixture)
public async Task GetBranchList_ShouldGetBranchListOfRepo_WhenInputsAreProvidedProperly()
{
// Arrange
const string repositoryName = GiteaTestConstants.RepositoryName;
const string branch1 = "branch1";
const string branch2 = "branch2";
const string branch3 = "branch3";
const string repositoryName = "get_branch_list_repo";
const string branch1 = "get_branch_list_repo_branch_1";
const string branch2 = "get_branch_list_repo_branch_2";
const string branch3 = "get_branch_list_repo_branch_3";
var cancellationToken = _giteaCollectionFixture.CancellationToken;

await _repositoryCreator.CreateRepositoryAsync(repositoryName, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Net;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Mohaymen.GiteaClient.Gitea.Client.Abstractions;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Dtos;
using Mohaymen.GiteaClient.IntegrationTests.Common.Collections.Gitea;
using Mohaymen.GiteaClient.IntegrationTests.Common.Initializers.TestData.Abstractions;
using Mohaymen.GiteaClient.IntegrationTests.Common.Models;

namespace Mohaymen.GiteaClient.IntegrationTests.Gitea.PullRequest.CreatePullRequest;

[Collection("GiteaIntegrationTests")]
public class CreatePullRequestTests
{
private readonly IGiteaClient _sut;
private readonly ITestRepositoryCreator _repositoryCreator;
private readonly ITestBranchCreator _branchCreator;
private readonly GiteaCollectionFixture _giteaCollectionFixture;

public CreatePullRequestTests(GiteaCollectionFixture giteaCollectionFixture)
{
_giteaCollectionFixture = giteaCollectionFixture ?? throw new ArgumentNullException(nameof(giteaCollectionFixture));
_repositoryCreator = _giteaCollectionFixture.ServiceProvider.GetRequiredService<ITestRepositoryCreator>();
_branchCreator = _giteaCollectionFixture.ServiceProvider.GetRequiredService<ITestBranchCreator>();
_sut = giteaCollectionFixture.ServiceProvider.GetRequiredService<IGiteaClient>();
}

[Fact]
public async Task CreatePullRequest_ShouldGetBranchListOfRepo_WhenInputsAreProvidedProperly()
{
// Arrange
const string repositoryName = "create_pull_request_repo";
const string branchName = "create_pull_request_branch";
var cancellationToken = _giteaCollectionFixture.CancellationToken;

await _repositoryCreator.CreateRepositoryAsync(repositoryName, cancellationToken);
await _branchCreator.CreateBranchAsync(repositoryName, branchName, cancellationToken);

const string title = "title";
var createPullRequestCommandDto = new CreatePullRequestCommandDto
{
RepositoryName = repositoryName,
Title = title,
HeadBranch = branchName,
BaseBranch = GiteaTestConstants.DefaultBranch
};

// Act
var actual = await _sut.PullRequestClient.CreatePullRequestAsync(createPullRequestCommandDto, cancellationToken);

// Assert
actual.StatusCode.Should().Be(HttpStatusCode.Created);
actual.Content!.Title.Should().Be(title);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Mohaymen.GiteaClient.Gitea.Repository.CreateRepository.Dtos;
using Mohaymen.GiteaClient.IntegrationTests.Common.Assertions.Abstractions;
using Mohaymen.GiteaClient.IntegrationTests.Common.Collections.Gitea;
using Mohaymen.GiteaClient.IntegrationTests.Common.Models;

namespace Mohaymen.GiteaClient.IntegrationTests.Gitea.Repository.CreateRepository;

Expand All @@ -31,7 +32,7 @@ public async Task CreateRepository_ShouldCreateRepositoryWithCreatedStatusCode_W
var createRepositoryCommandDto = new CreateRepositoryCommandDto
{
Name = repositoryName,
DefaultBranch = "main",
DefaultBranch = GiteaTestConstants.DefaultBranch,
IsPrivateBranch = true
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using MediatR;
using Mohaymen.GiteaClient.Gitea.PullRequest.Common.Facade;
using Mohaymen.GiteaClient.Gitea.PullRequest.Common.Facade.Abstractions;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Commands;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Dtos;
using NSubstitute;
using Xunit;

namespace Mohaymen.GiteaClient.Tests.Gitea.PullRequest.Common.Facade;

public class PullRequestFacadeTests
{
private readonly IMediator _mediator;
private readonly IPullRequestFacade _sut;

public PullRequestFacadeTests()
{
_mediator = Substitute.For<IMediator>();
_sut = new PullRequestFacade(_mediator);
}

[Fact]
public async Task CreatePullRequestAsync_ShouldCallSend_WhenAllFieldsAreProvided()
{
// Arrange
const string repositoryName = "repo";
const string headBranch = "head";
const string baseBranch = "base";
const string body = "body";
const string title = "title";
const string assignee = "assignee";
var assignees = new List<string>
{
"assignee1",
"assignee2"
};
var commandDto = new CreatePullRequestCommandDto
{
RepositoryName = repositoryName,
HeadBranch = headBranch,
BaseBranch = baseBranch,
Body = body,
Title = title,
Assignee = assignee,
Assignees = assignees
};

// Act
await _sut.CreatePullRequestAsync(commandDto, default);

// Assert
await _mediator.Received(1).Send(Arg.Is<CreatePullRequestCommand>(x => x.RepositoryName == repositoryName
&& x.HeadBranch == headBranch
&& x.BaseBranch == baseBranch
&& x.Body == body
&& x.Title == title
&& x.Assignee == assignee
&& x.Assignees!.SequenceEqual(assignees)));
}

[Fact]
public async Task CreatePullRequestAsync_ShouldCallSend_WhenOptionalFieldsAreNotProvided()
{
// Arrange
const string repositoryName = "repo";
var commandDto = new CreatePullRequestCommandDto
{
RepositoryName = repositoryName
};

// Act
await _sut.CreatePullRequestAsync(commandDto, default);

// Assert
await _mediator.Received(1).Send(Arg.Is<CreatePullRequestCommand>(x => x.RepositoryName == repositoryName));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using FluentAssertions;
using FluentValidation;
using MediatR;
using Microsoft.Extensions.Options;
using Mohaymen.GiteaClient.Core.Configs;
using Mohaymen.GiteaClient.Gitea.PullRequest.Common.ApiCall.Abstractions;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Commands;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Context;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Dtos;
using NSubstitute;
using Refit;
using Xunit;

namespace Mohaymen.GiteaClient.Tests.Gitea.PullRequest.CreatePullRequest.Commands;

public class CreatePullRequestCommandHandlerTests
{
private readonly IPullRequestRestClient _pullRequestRestClient;
private readonly IOptions<GiteaApiConfiguration> _options;
private readonly InlineValidator<CreatePullRequestCommand> _validator;
private readonly IRequestHandler<CreatePullRequestCommand, ApiResponse<CreatePullRequestResponseDto>> _sut;

public CreatePullRequestCommandHandlerTests()
{
_pullRequestRestClient = Substitute.For<IPullRequestRestClient>();
_options = Substitute.For<IOptions<GiteaApiConfiguration>>();
_validator = new InlineValidator<CreatePullRequestCommand>();
_sut = new CreatePullRequestCommandHandler(_pullRequestRestClient, _options, _validator);
}

[Fact]
public async Task Handle_ShouldThrowsValidationException_WhenInputIsInvalid()
{
// Arrange
_validator.RuleFor(x => x).Must(_ => false);
var command = new CreatePullRequestCommand
{
RepositoryName = "repo"
};

// Act
var actual = async () => await _sut.Handle(command, default);

// Assert
await actual.Should().ThrowAsync<ValidationException>();
}

[Fact]
public async Task Handle_ShouldCallCreatePullRequestAsync_AndInputsAreValid()
{
// Arrange
_validator.RuleFor(x => x).Must(_ => true);
const string owner = "owner";
const string repositoryName = "repo";
const string headBranch = "head";
const string baseBranch = "base";
const string body = "body";
const string title = "title";
const string assignee = "assignee";
var assignees = new List<string>
{
"assignee1",
"assignee2"
};
var command = new CreatePullRequestCommand
{
RepositoryName = repositoryName,
HeadBranch = headBranch,
BaseBranch = baseBranch,
Body = body,
Title = title,
Assignee = assignee,
Assignees = assignees
};
_options.Value.Returns(new GiteaApiConfiguration
{
BaseUrl = "url",
PersonalAccessToken = "token",
RepositoriesOwner = owner
});

// Act
await _sut.Handle(command, default);

// Assert
await _pullRequestRestClient.Received(1).CreatePullRequestAsync(owner,
repositoryName,
Arg.Is<CreatePullRequestRequest>(x => x.HeadBranch == headBranch
&& x.BaseBranch == baseBranch
&& x.Body == body
&& x.Title == title
&& x.Assignee == assignee
&& x.Assignees!.SequenceEqual(assignees)),
default);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using FluentAssertions;
using FluentValidation;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Commands;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Validators;
using Xunit;

namespace Mohaymen.GiteaClient.Tests.Gitea.PullRequest.CreatePullRequest.Validators;

public class CreatePullRequestCommandValidatorTests
{
private readonly IValidator<CreatePullRequestCommand> _sut;

public CreatePullRequestCommandValidatorTests()
{
_sut = new CreatePullRequestCommandValidator();
}

[Theory]
[InlineData(null)]
[InlineData("")]
public void Validate_ShouldReturnEmptyRepositoryNameErrorCode_WhenRepositoryNameIsNullOrEmpty(string repositoryName)
{
// Arrange
var command = new CreatePullRequestCommand
{
RepositoryName = repositoryName
};

// Act
var actual = _sut.Validate(command);

// Assert
actual.Errors.Select(x => x.ErrorCode)
.Should().Contain(CreatePullRequestErrorCodes.EmptyRepositoryNameErrorCode);
}

[Fact]
public void Validate_ShouldReturnValidResult_WhenEveryThingIsProvidedProperly()
{
// Arrange
var command = new CreatePullRequestCommand
{
RepositoryName = "repo"
};

// Act
var actual = _sut.Validate(command);

// Assert
actual.IsValid.Should().BeTrue();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Mohaymen.GiteaClient.Gitea.Branch.Common.Facade.Abstractions;
using Mohaymen.GiteaClient.Gitea.Commit.Common.Facades.Abstractions;
using Mohaymen.GiteaClient.Gitea.PullRequest.Common.Facade.Abstractions;
using Mohaymen.GiteaClient.Gitea.Repository.Common.Facade.Abstractions;

namespace Mohaymen.GiteaClient.Gitea.Client.Abstractions;
Expand All @@ -9,4 +10,5 @@ public interface IGiteaClient
ICommitFacade CommitClient { get; }
IRepositoryFacade RepositoryClient { get; }
IBranchFacade BranchClient { get; }
IPullRequestFacade PullRequestClient { get; }
}
6 changes: 5 additions & 1 deletion Mohaymen.GiteaClient/Gitea/Client/GiteaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Mohaymen.GiteaClient.Gitea.Branch.Common.Facade.Abstractions;
using Mohaymen.GiteaClient.Gitea.Client.Abstractions;
using Mohaymen.GiteaClient.Gitea.Commit.Common.Facades.Abstractions;
using Mohaymen.GiteaClient.Gitea.PullRequest.Common.Facade.Abstractions;
using Mohaymen.GiteaClient.Gitea.Repository.Common.Facade.Abstractions;

namespace Mohaymen.GiteaClient.Gitea.Client;
Expand All @@ -11,13 +12,16 @@ internal class GiteaClient : IGiteaClient
public ICommitFacade CommitClient { get; }
public IRepositoryFacade RepositoryClient { get; }
public IBranchFacade BranchClient { get; }
public IPullRequestFacade PullRequestClient { get; }

public GiteaClient(IRepositoryFacade repositoryFacade,
IBranchFacade branchClient,
ICommitFacade commitClient)
ICommitFacade commitClient,
IPullRequestFacade pullRequestClient)
{
RepositoryClient = repositoryFacade ?? throw new ArgumentNullException(nameof(repositoryFacade));
BranchClient = branchClient ?? throw new ArgumentNullException(nameof(branchClient));
PullRequestClient = pullRequestClient ?? throw new ArgumentNullException(nameof(pullRequestClient));
CommitClient = commitClient ?? throw new ArgumentNullException(nameof(commitClient));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Threading;
using System.Threading.Tasks;
using Mohaymen.GiteaClient.Core.ApiCall.Abstractions;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Context;
using Mohaymen.GiteaClient.Gitea.PullRequest.CreatePullRequest.Dtos;
using Refit;

namespace Mohaymen.GiteaClient.Gitea.PullRequest.Common.ApiCall.Abstractions;

internal interface IPullRequestRestClient : IRefitClientInterface
{
[Post("/repos/{owner}/{repo}/pulls")]
Task<ApiResponse<CreatePullRequestResponseDto>> CreatePullRequestAsync(
[AliasAs("owner")] string owner,
[AliasAs("repo")] string repositoryName,
[Body] CreatePullRequestRequest createPullRequestRequest,
CancellationToken cancellationToken);
}
Loading

0 comments on commit f0d0d41

Please sign in to comment.