diff --git a/src/Stack.Tests/Commands/Stack/StackStatusCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/StackStatusCommandHandlerTests.cs index 6e88029..d55a43d 100644 --- a/src/Stack.Tests/Commands/Stack/StackStatusCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/StackStatusCommandHandlerTests.cs @@ -15,20 +15,25 @@ public class StackStatusCommandHandlerTests public async Task WhenMultipleBranchesExistInAStack_AndOneHasAPullRequests_ReturnsStatus() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, []); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -37,26 +42,10 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneHasAPullRequests_Retur .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-3", "branch-1") - .Returns((10, 5)); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-3") - .Returns((1, 0)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-3") + .GetPullRequest(aBranch) .Returns(pr); // Act @@ -65,8 +54,8 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneHasAPullRequests_Retur // Assert var expectedBranchDetails = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } + { aBranch, new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } }; response.Statuses.Should().BeEquivalentTo(new Dictionary { @@ -80,47 +69,37 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneHasAPullRequests_Retur public async Task WhenStackNameIsProvided_DoesNotAskForStack_ReturnsStatus() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, []); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); + inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); outputProvider .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-3", "branch-1") - .Returns((10, 5)); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-3") - .Returns((1, 0)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-3") + .GetPullRequest(aBranch) .Returns(pr); // Act @@ -129,8 +108,8 @@ public async Task WhenStackNameIsProvided_DoesNotAskForStack_ReturnsStatus() // Assert var expectedBranchDetails = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } + { aBranch, new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } }; response.Statuses.Should().BeEquivalentTo(new Dictionary { @@ -146,20 +125,27 @@ public async Task WhenStackNameIsProvided_DoesNotAskForStack_ReturnsStatus() public async Task WhenAllStacksAreRequested_ReturnsStatusOfEachStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + var aThirdBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithBranch(builder => builder.WithName(aThirdBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(3).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, [aThirdBranch]); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -167,30 +153,10 @@ public async Task WhenAllStacksAreRequested_ReturnsStatusOfEachStack() .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-2", "branch-3", "branch-4", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-2", "branch-3", "branch-4", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-3", "branch-1") - .Returns((10, 5)); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-3") - .Returns((1, 0)); - - gitOperations - .GetStatusOfRemoteBranch("branch-4", "branch-2") - .Returns((3, 1)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-3") + .GetPullRequest(aBranch) .Returns(pr); // Act @@ -199,12 +165,12 @@ public async Task WhenAllStacksAreRequested_ReturnsStatusOfEachStack() // Assert var expectedBranchDetailsForStack1 = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } + { aBranch, new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } }; var expectedBranchDetailsForStack2 = new Dictionary { - { "branch-4", new BranchDetail { Status = new BranchStatus(true, true, 3, 1) } } + { aThirdBranch, new BranchDetail { Status = new BranchStatus(true, true, 3, 5) } } }; response.Statuses.Should().BeEquivalentTo(new Dictionary { @@ -221,21 +187,28 @@ public async Task WhenAllStacksAreRequested_ReturnsStatusOfEachStack() public async Task WhenAllStacksAreRequested_WithStacksInMultipleRepositories_ReturnsStatusOfEachStackInTheCorrectRepository() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + var aThirdBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithBranch(builder => builder.WithName(aThirdBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(3).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); - var stack3 = new Config.Stack("Stack2", Some.HttpsUri().ToString(), "branch-6", ["branch-7"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, [aThirdBranch]); + var stack3 = new Config.Stack("Stack2", Some.HttpsUri().ToString(), Some.BranchName(), [Some.BranchName()]); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -243,30 +216,10 @@ public async Task WhenAllStacksAreRequested_WithStacksInMultipleRepositories_Ret .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-2", "branch-3", "branch-4", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-2", "branch-3", "branch-4", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-3", "branch-1") - .Returns((10, 5)); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-3") - .Returns((1, 0)); - - gitOperations - .GetStatusOfRemoteBranch("branch-4", "branch-2") - .Returns((3, 1)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-3") + .GetPullRequest(aBranch) .Returns(pr); // Act @@ -275,12 +228,12 @@ public async Task WhenAllStacksAreRequested_WithStacksInMultipleRepositories_Ret // Assert var expectedBranchDetailsForStack1 = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } + { aBranch, new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } }; var expectedBranchDetailsForStack2 = new Dictionary { - { "branch-4", new BranchDetail { Status = new BranchStatus(true, true, 3, 1) } } + { aThirdBranch, new BranchDetail { Status = new BranchStatus(true, true, 3, 5) } } }; response.Statuses.Should().BeEquivalentTo(new Dictionary { @@ -297,20 +250,27 @@ public async Task WhenAllStacksAreRequested_WithStacksInMultipleRepositories_Ret public async Task WhenStackNameIsProvided_ButStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + var aThirdBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithBranch(builder => builder.WithName(aThirdBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(3).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, [aThirdBranch]); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -326,20 +286,25 @@ await handler public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRemote_ReturnsCorrectStatus() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + var aThirdBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).WithNumberOfEmptyCommits(5).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10)) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, [aThirdBranch]); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -348,22 +313,10 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-1") - .Returns((1, 0)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-5") + .GetPullRequest(aSecondBranch) .Returns(pr); // Act @@ -372,8 +325,8 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem // Assert var expectedBranchDetails = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(true, false, 0, 0) } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0), PullRequest = pr } } + { aBranch, new BranchDetail { Status = new BranchStatus(true, false, 0, 0) } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 11, 0), PullRequest = pr } } // The 11 commits are the 10 commits from the parent branch and one from this branch }; response.Statuses.Should().BeEquivalentTo(new Dictionary { @@ -387,20 +340,24 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRemoteAndLocally_ReturnsCorrectStatus() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + var aThirdBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).WithNumberOfEmptyCommits(5).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, [aThirdBranch]); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -409,22 +366,10 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-1") - .Returns((1, 0)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-5") + .GetPullRequest(aSecondBranch) .Returns(pr); // Act @@ -433,8 +378,8 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem // Assert var expectedBranchDetails = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(false, false, 0, 0) } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0), PullRequest = pr } } + { aBranch, new BranchDetail { Status = new BranchStatus(false, false, 0, 0) } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 1, 0), PullRequest = pr } } }; response.Statuses.Should().BeEquivalentTo(new Dictionary { @@ -448,19 +393,24 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_ReturnsStatus() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var aBranch = Some.BranchName(); + var aSecondBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(aBranch).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(aSecondBranch).FromSourceBranch(aBranch).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); + var gitHubOperations = Substitute.For(); var handler = new StackStatusCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [aBranch, aSecondBranch]); var stacks = new List([stack1]); stackConfig.Load().Returns(stacks); @@ -468,26 +418,10 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_ReturnsStatus() .WhenForAnyArgs(o => o.Status(Arg.Any(), Arg.Any())) .Do(ci => ci.ArgAt(1)()); - gitOperations - .GetBranchesThatExistInRemote(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetBranchesThatExistLocally(Arg.Any()) - .Returns(["branch-1", "branch-3", "branch-5"]); - - gitOperations - .GetStatusOfRemoteBranch("branch-3", "branch-1") - .Returns((10, 5)); - - gitOperations - .GetStatusOfRemoteBranch("branch-5", "branch-3") - .Returns((1, 0)); - var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false); gitHubOperations - .GetPullRequest("branch-3") + .GetPullRequest(aBranch) .Returns(pr); // Act @@ -496,8 +430,8 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_ReturnsStatus() // Assert var expectedBranchDetails = new Dictionary { - { "branch-3", new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, - { "branch-5", new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } + { aBranch, new BranchDetail { Status = new BranchStatus(true, true, 10, 5), PullRequest = pr } }, + { aSecondBranch, new BranchDetail { Status = new BranchStatus(true, true, 1, 0) } } }; response.Statuses.Should().BeEquivalentTo(new Dictionary { diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs index cf5e083..e048dd4 100644 --- a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -1,23 +1,203 @@ -using System; using LibGit2Sharp; -using Microsoft.VisualBasic; using Stack.Git; namespace Stack.Tests.Helpers; + +public class BranchBuilder +{ + string? name; + bool pushToRemote; + string? sourceBranch; + int numberOfEmptyCommits; + + public BranchBuilder WithName(string name) + { + this.name = name; + return this; + } + + public BranchBuilder FromSourceBranch(string sourceBranch) + { + this.sourceBranch = sourceBranch; + return this; + } + + public BranchBuilder PushToRemote() + { + this.pushToRemote = true; + return this; + } + + public BranchBuilder WithNumberOfEmptyCommits(int numberOfCommits) + { + this.numberOfEmptyCommits = numberOfCommits; + return this; + } + + public void Build(Repository repository, string defaultBranchName) + { + var branchName = name ?? Some.BranchName(); + var defaultBranch = repository.Branches[defaultBranchName]; + var target = sourceBranch is not null ? repository.Branches[sourceBranch].Tip : defaultBranch.Tip; + var branch = repository.CreateBranch(branchName, target); + + for (var i = 0; i < numberOfEmptyCommits; i++) + { + CreateEmptyCommit(repository, branch!, $"Empty commit {i + 1}"); + } + + if (pushToRemote) + { + repository.Branches.Update(branch, + b => b.Remote = repository.Network.Remotes["origin"].Name, + b => b.UpstreamBranch = branch!.CanonicalName); + repository.Network.Push(branch); + } + } + + private static Commit CreateEmptyCommit(Repository repository, Branch branch, string message) + { + repository.Refs.UpdateTarget("HEAD", branch.CanonicalName); + var signature = new Signature(Some.Name(), Some.Name(), DateTimeOffset.Now); + + return repository.Commit(message, signature, signature, new CommitOptions() { AllowEmptyCommit = true }); + } +} + +public class CommitBuilder +{ + string? branchName; + string? message; + string? authorName; + string? authorEmail; + string? committerName; + string? committerEmail; + bool allowEmptyCommit; + bool pushToRemote; + + public CommitBuilder OnBranch(string branch) + { + this.branchName = branch; + return this; + } + + public CommitBuilder WithMessage(string message) + { + this.message = message; + return this; + } + + public CommitBuilder WithAuthor(string name, string email) + { + authorName = name; + authorEmail = email; + return this; + } + + public CommitBuilder WithCommitter(string name, string email) + { + committerName = name; + committerEmail = email; + return this; + } + + public CommitBuilder AllowEmptyCommit() + { + allowEmptyCommit = true; + return this; + } + + public CommitBuilder PushToRemote() + { + pushToRemote = true; + return this; + } + + public void Build(Repository repository) + { + Branch? branch = null; + + if (branchName is not null) + { + branch = repository.Branches[branchName]; + } + + if (branch is not null) + { + repository.Refs.UpdateTarget("HEAD", branch.CanonicalName); + } + + var signature = new Signature(authorName ?? Some.Name(), authorEmail ?? Some.Name(), DateTimeOffset.Now); + var committer = new Signature(committerName ?? Some.Name(), committerEmail ?? Some.Name(), DateTimeOffset.Now); + + repository.Commit(message ?? Some.Name(), signature, committer, new CommitOptions() { AllowEmptyCommit = allowEmptyCommit }); + + if (branch is not null && pushToRemote) + { + repository.Network.Push(branch); + } + } +} + public class TestGitRepositoryBuilder { - List<(string Name, bool PushToRemote)> branches = []; + List> branchBuilders = []; + List> commitBuilders = []; public TestGitRepositoryBuilder WithBranch(string branch) { - branches.Add((branch, false)); + branchBuilders.Add(b => b.WithName(branch)); return this; } public TestGitRepositoryBuilder WithBranch(string branch, bool pushToRemote) { - branches.Add((branch, pushToRemote)); + branchBuilders.Add(b => + { + b.WithName(branch); + + if (pushToRemote) + { + b.PushToRemote(); + } + }); + return this; + } + + public TestGitRepositoryBuilder WithBranch(Action branchBuilder) + { + branchBuilders.Add(branchBuilder); + return this; + } + + public TestGitRepositoryBuilder WithNumberOfEmptyCommits(string branchName, int number, bool pushToRemote) + { + for (var i = 0; i < number; i++) + { + commitBuilders.Add(b => + { + b.OnBranch(branchName).WithMessage($"Empty commit {i + 1}").AllowEmptyCommit(); + + if (pushToRemote) + { + b.PushToRemote(); + } + }); + } + return this; + } + + public TestGitRepositoryBuilder WithNumberOfEmptyCommits(Action commitBuilder, int number) + { + for (var i = 0; i < number; i++) + { + commitBuilders.Add(b => + { + commitBuilder(b); + b.AllowEmptyCommit(); + }); + } return this; } @@ -28,48 +208,48 @@ public TestGitRepository Build() var localDirectory = TemporaryDirectory.Create(); var remoteRepo = new Repository(Repository.Init(remoteDirectory.DirectoryPath, true)); - var repo = new Repository(Repository.Clone(remote, localDirectory.DirectoryPath)); - var defaultBranch = Some.BranchName(); - repo.Refs.UpdateTarget("HEAD", "refs/heads/" + defaultBranch); + var localRepo = new Repository(Repository.Clone(remote, localDirectory.DirectoryPath)); + var defaultBranchName = Some.BranchName(); + + localRepo.Refs.UpdateTarget("HEAD", "refs/heads/" + defaultBranchName); - Commit(repo, ("README.md", "Hello, World!")); + CreateInitialCommit(localRepo); - foreach (var branch in branches) + foreach (var branchBuilder in branchBuilders) { - var newBranch = repo.CreateBranch(branch.Name); + var builder = new BranchBuilder(); + branchBuilder(builder); + builder.Build(localRepo, defaultBranchName); + } - if (branch.PushToRemote) - { - repo.Branches.Update(newBranch, - b => b.Remote = repo.Network.Remotes["origin"].Name, - b => b.UpstreamBranch = newBranch.CanonicalName); - repo.Network.Push(newBranch); - } + foreach (var commitBuilder in commitBuilders) + { + var builder = new CommitBuilder(); + commitBuilder(builder); + builder.Build(localRepo); } - return new TestGitRepository(localDirectory, remoteDirectory, repo); + return new TestGitRepository(localDirectory, remoteDirectory, localRepo); } - private Commit Commit(Repository repository, params (string Name, string? Content)[] files) + private static Commit CreateInitialCommit(Repository repository) { - var message = $"Commit: {Some.Name()}"; + var message = $"Initial commit"; var signature = new Signature(Some.Name(), Some.Name(), DateTimeOffset.Now); return repository.Commit(message, signature, signature); } } -public class TestGitRepository(TemporaryDirectory LocalDirectory, TemporaryDirectory RemoteDirectory, Repository Repository) : IDisposable +public class TestGitRepository(TemporaryDirectory LocalDirectory, TemporaryDirectory RemoteDirectory, Repository LocalRepository) : IDisposable { - public TemporaryDirectory LocalDirectory { get; } = LocalDirectory; - public TemporaryDirectory RemoteDirectory { get; } = RemoteDirectory; public string RemoteUri => RemoteDirectory.DirectoryPath; public GitOperationSettings GitOperationSettings => new GitOperationSettings(false, false, LocalDirectory.DirectoryPath); public void Dispose() { GC.SuppressFinalize(this); - Repository.Dispose(); + LocalRepository.Dispose(); LocalDirectory.Dispose(); RemoteDirectory.Dispose(); }