From 81d5c5a78c255ab979170941075dc946c7e1f327 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Mon, 16 Dec 2024 09:36:14 +1100 Subject: [PATCH 01/24] Wip adding proper Git tests --- .../Branch/AddBranchCommandHandlerTests.cs | 28 +++--- src/Stack.Tests/Helpers/Some.cs | 4 +- .../Helpers/TestGitRepositoryBuilder.cs | 86 +++++++++++++++++++ src/Stack.Tests/Stack.Tests.csproj | 1 + src/Stack/Commands/Branch/AddBranchCommand.cs | 2 +- 5 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs diff --git a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs index 8e4544d..96afdaf 100644 --- a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using NSubstitute; +using Spectre.Console; using Stack.Commands; using Stack.Commands.Helpers; using Stack.Config; @@ -15,22 +16,25 @@ public class AddBranchCommandHandlerTests public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_AddsBranchToStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + + var gitOperations = new GitOperations(Substitute.For(), new GitOperationSettings(false, false, repo.LocalDirectory.DirectoryPath)); var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -38,7 +42,7 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_AddsBran .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-5"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); // Act await handler.Handle(AddBranchCommandInputs.Empty); @@ -46,8 +50,8 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_AddsBran // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, branchToAdd]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } diff --git a/src/Stack.Tests/Helpers/Some.cs b/src/Stack.Tests/Helpers/Some.cs index 12b1537..1991841 100644 --- a/src/Stack.Tests/Helpers/Some.cs +++ b/src/Stack.Tests/Helpers/Some.cs @@ -5,5 +5,7 @@ namespace Stack.Tests.Helpers; public static class Some { public static string Name() => Guid.NewGuid().ToString("N"); - public static Uri HttpsUri() => new($"https://{Name()}.com"); + public static string ShortName() => Guid.NewGuid().ToString("N").Substring(0, 8); + public static string BranchName() => $"branch-{ShortName()}"; + public static Uri HttpsUri() => new($"https://{ShortName()}.com"); } diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs new file mode 100644 index 0000000..19c0213 --- /dev/null +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -0,0 +1,86 @@ +using System; +using LibGit2Sharp; +using Microsoft.VisualBasic; + +namespace Stack.Tests.Helpers; + +public class TestGitRepositoryBuilder +{ + List branches = []; + + public TestGitRepositoryBuilder WithBranch(string branch) + { + branches.Add(branch); + return this; + } + + public TestGitRepository Build() + { + var remote = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString("N"), ".git"); + var remoteDirectory = new TemporaryDirectory(remote); + var localDirectory = TemporaryDirectory.Create(); + + 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); + + Commit(repo, ("README.md", "Hello, World!")); + + foreach (var branch in branches) + { + repo.CreateBranch(branch); + } + + return new TestGitRepository(localDirectory, remoteDirectory, repo); + } + + private Commit Commit(Repository repository, params (string Name, string? Content)[] files) + { + var message = $"Commit: {Some.Name()}"; + 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 TemporaryDirectory LocalDirectory { get; } = LocalDirectory; + public TemporaryDirectory RemoteDirectory { get; } = RemoteDirectory; + public string RemoteUri => RemoteDirectory.DirectoryPath; + + public void Dispose() + { + GC.SuppressFinalize(this); + Repository.Dispose(); + LocalDirectory.Dispose(); + RemoteDirectory.Dispose(); + } +} + +public class TemporaryDirectory(string DirectoryPath) : IDisposable +{ + public string DirectoryPath { get; } = DirectoryPath; + + public void Dispose() + { + GC.SuppressFinalize(this); + + try + { + Directory.Delete(DirectoryPath, true); + } + catch (Exception) + { + // Ignore + } + } + + public static TemporaryDirectory Create() + { + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(path); + return new TemporaryDirectory(path); + } +} diff --git a/src/Stack.Tests/Stack.Tests.csproj b/src/Stack.Tests/Stack.Tests.csproj index 71e0471..7c449f5 100644 --- a/src/Stack.Tests/Stack.Tests.csproj +++ b/src/Stack.Tests/Stack.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Stack/Commands/Branch/AddBranchCommand.cs b/src/Stack/Commands/Branch/AddBranchCommand.cs index 098453d..c5de92e 100644 --- a/src/Stack/Commands/Branch/AddBranchCommand.cs +++ b/src/Stack/Commands/Branch/AddBranchCommand.cs @@ -56,7 +56,7 @@ public async Task Handle(AddBranchCommandInputs inputs { await Task.CompletedTask; - var defaultBranch = gitOperations.GetDefaultBranch(); + // var defaultBranch = gitOperations.GetDefaultBranch(); var remoteUri = gitOperations.GetRemoteUri(); var currentBranch = gitOperations.GetCurrentBranch(); var branches = gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate(); From 5789694f75383e1b16c0a6e167700d78e021eaee Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Mon, 16 Dec 2024 17:23:56 +1100 Subject: [PATCH 02/24] Use output provider for Git operations --- src/Stack/Commands/Branch/AddBranchCommand.cs | 5 +++-- src/Stack/Commands/Branch/NewBranchCommand.cs | 5 +++-- .../Commands/Branch/RemoveBranchCommand.cs | 5 +++-- .../PullRequests/CreatePullRequestsCommand.cs | 5 +++-- .../PullRequests/OpenPullRequestsCommand.cs | 5 +++-- .../Commands/Stack/CleanupStackCommand.cs | 6 ++++-- src/Stack/Commands/Stack/DeleteStackCommand.cs | 6 ++++-- src/Stack/Commands/Stack/ListStacksCommand.cs | 4 +++- src/Stack/Commands/Stack/NewStackCommand.cs | 6 ++++-- src/Stack/Commands/Stack/StackStatusCommand.cs | 5 +++-- src/Stack/Commands/Stack/StackSwitchCommand.cs | 3 ++- src/Stack/Commands/Stack/UpdateStackCommand.cs | 6 ++++-- src/Stack/Git/GitOperations.cs | 18 +++++++++--------- 13 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/Stack/Commands/Branch/AddBranchCommand.cs b/src/Stack/Commands/Branch/AddBranchCommand.cs index 098453d..d397038 100644 --- a/src/Stack/Commands/Branch/AddBranchCommand.cs +++ b/src/Stack/Commands/Branch/AddBranchCommand.cs @@ -26,11 +26,12 @@ public override async Task ExecuteAsync(CommandContext context, AddBranchCo await Task.CompletedTask; var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new AddBranchCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new StackConfig()); await handler.Handle(new AddBranchCommandInputs(settings.Stack, settings.Name)); diff --git a/src/Stack/Commands/Branch/NewBranchCommand.cs b/src/Stack/Commands/Branch/NewBranchCommand.cs index 5b241aa..00f9c96 100644 --- a/src/Stack/Commands/Branch/NewBranchCommand.cs +++ b/src/Stack/Commands/Branch/NewBranchCommand.cs @@ -30,11 +30,12 @@ public override async Task ExecuteAsync(CommandContext context, NewBranchCo await Task.CompletedTask; var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new NewBranchCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new StackConfig()); await handler.Handle(new NewBranchCommandInputs(settings.Stack, settings.Name, settings.Force)); diff --git a/src/Stack/Commands/Branch/RemoveBranchCommand.cs b/src/Stack/Commands/Branch/RemoveBranchCommand.cs index 1e1d494..2bbd8da 100644 --- a/src/Stack/Commands/Branch/RemoveBranchCommand.cs +++ b/src/Stack/Commands/Branch/RemoveBranchCommand.cs @@ -30,11 +30,12 @@ public override async Task ExecuteAsync(CommandContext context, RemoveBranc await Task.CompletedTask; var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new RemoveBranchCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new StackConfig()); await handler.Handle(new RemoveBranchCommandInputs(settings.Stack, settings.Name, settings.Force)); diff --git a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs index 5b5b132..f278434 100644 --- a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs @@ -20,11 +20,12 @@ public class CreatePullRequestsCommand : AsyncCommand ExecuteAsync(CommandContext context, CreatePullRequestsCommandSettings settings) { var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new CreatePullRequestsCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new GitHubOperations(console, settings.GetGitHubOperationSettings()), new FileOperations(), new StackConfig()); diff --git a/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs b/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs index fddc5a6..a25e686 100644 --- a/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs @@ -20,11 +20,12 @@ public class OpenPullRequestsCommand : AsyncCommand ExecuteAsync(CommandContext context, OpenPullRequestsCommandSettings settings) { var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new OpenPullRequestsCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new GitHubOperations(console, settings.GetGitHubOperationSettings()), new StackConfig()); diff --git a/src/Stack/Commands/Stack/CleanupStackCommand.cs b/src/Stack/Commands/Stack/CleanupStackCommand.cs index 8d3b715..29331dc 100644 --- a/src/Stack/Commands/Stack/CleanupStackCommand.cs +++ b/src/Stack/Commands/Stack/CleanupStackCommand.cs @@ -27,10 +27,12 @@ public override async Task ExecuteAsync(CommandContext context, CleanupStac await Task.CompletedTask; var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); + var handler = new CleanupStackCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new GitHubOperations(console, settings.GetGitHubOperationSettings()), new StackConfig()); diff --git a/src/Stack/Commands/Stack/DeleteStackCommand.cs b/src/Stack/Commands/Stack/DeleteStackCommand.cs index 7f4133e..6142127 100644 --- a/src/Stack/Commands/Stack/DeleteStackCommand.cs +++ b/src/Stack/Commands/Stack/DeleteStackCommand.cs @@ -27,10 +27,12 @@ public override async Task ExecuteAsync(CommandContext context, DeleteStack await Task.CompletedTask; var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); + var handler = new DeleteStackCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new GitHubOperations(console, settings.GetGitHubOperationSettings()), new StackConfig()); diff --git a/src/Stack/Commands/Stack/ListStacksCommand.cs b/src/Stack/Commands/Stack/ListStacksCommand.cs index 57cd795..d259a48 100644 --- a/src/Stack/Commands/Stack/ListStacksCommand.cs +++ b/src/Stack/Commands/Stack/ListStacksCommand.cs @@ -3,6 +3,7 @@ using Spectre.Console.Cli; using Stack.Config; using Stack.Git; +using Stack.Infrastructure; namespace Stack.Commands; @@ -14,7 +15,8 @@ public override async Task ExecuteAsync(CommandContext context, ListStacksC { await Task.CompletedTask; var console = AnsiConsole.Console; - var gitOperations = new GitOperations(console, settings.GetGitOperationSettings()); + var outputProvider = new ConsoleOutputProvider(console); + var gitOperations = new GitOperations(outputProvider, settings.GetGitOperationSettings()); var stackConfig = new StackConfig(); var stacks = stackConfig.Load(); diff --git a/src/Stack/Commands/Stack/NewStackCommand.cs b/src/Stack/Commands/Stack/NewStackCommand.cs index c5fd5a5..619b1d8 100644 --- a/src/Stack/Commands/Stack/NewStackCommand.cs +++ b/src/Stack/Commands/Stack/NewStackCommand.cs @@ -40,10 +40,12 @@ public class NewStackCommand : AsyncCommand public override async Task ExecuteAsync(CommandContext context, NewStackCommandSettings settings) { var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); + var handler = new NewStackCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new StackConfig()); var response = await handler.Handle( diff --git a/src/Stack/Commands/Stack/StackStatusCommand.cs b/src/Stack/Commands/Stack/StackStatusCommand.cs index e5e1e88..ce2500c 100644 --- a/src/Stack/Commands/Stack/StackStatusCommand.cs +++ b/src/Stack/Commands/Stack/StackStatusCommand.cs @@ -24,11 +24,12 @@ public class StackStatusCommand : AsyncCommand public override async Task ExecuteAsync(CommandContext context, StackStatusCommandSettings settings) { var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new StackStatusCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new GitHubOperations(console, settings.GetGitHubOperationSettings()), new StackConfig()); diff --git a/src/Stack/Commands/Stack/StackSwitchCommand.cs b/src/Stack/Commands/Stack/StackSwitchCommand.cs index ed206a9..082f808 100644 --- a/src/Stack/Commands/Stack/StackSwitchCommand.cs +++ b/src/Stack/Commands/Stack/StackSwitchCommand.cs @@ -20,10 +20,11 @@ public class StackSwitchCommand : AsyncCommand public override async Task ExecuteAsync(CommandContext context, StackSwitchCommandSettings settings) { var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); var handler = new StackSwitchCommandHandler( new ConsoleInputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new StackConfig()); await handler.Handle(new StackSwitchCommandInputs(settings.Branch)); diff --git a/src/Stack/Commands/Stack/UpdateStackCommand.cs b/src/Stack/Commands/Stack/UpdateStackCommand.cs index 1a0c8c8..abde171 100644 --- a/src/Stack/Commands/Stack/UpdateStackCommand.cs +++ b/src/Stack/Commands/Stack/UpdateStackCommand.cs @@ -25,10 +25,12 @@ public class UpdateStackCommand : AsyncCommand public override async Task ExecuteAsync(CommandContext context, UpdateStackCommandSettings settings) { var console = AnsiConsole.Console; + var outputProvider = new ConsoleOutputProvider(console); + var handler = new UpdateStackCommandHandler( new ConsoleInputProvider(console), - new ConsoleOutputProvider(console), - new GitOperations(console, settings.GetGitOperationSettings()), + outputProvider, + new GitOperations(outputProvider, settings.GetGitOperationSettings()), new GitHubOperations(console, settings.GetGitHubOperationSettings()), new StackConfig()); diff --git a/src/Stack/Git/GitOperations.cs b/src/Stack/Git/GitOperations.cs index e9a2aff..27be14a 100644 --- a/src/Stack/Git/GitOperations.cs +++ b/src/Stack/Git/GitOperations.cs @@ -1,7 +1,7 @@ using System.Diagnostics; using System.Text; using Octopus.Shellfish; -using Spectre.Console; +using Stack.Infrastructure; namespace Stack.Git; @@ -38,7 +38,7 @@ public interface IGitOperations void OpenFileInEditorAndWaitForClose(string path); } -public class GitOperations(IAnsiConsole console, GitOperationSettings settings) : IGitOperations +public class GitOperations(IOutputProvider outputProvider, GitOperationSettings settings) : IGitOperations { public void CreateNewBranch(string branchName, string sourceBranch) { @@ -165,7 +165,7 @@ public void OpenFileInEditorAndWaitForClose(string path) var editor = GetConfiguredEditor(); if (string.IsNullOrWhiteSpace(editor)) { - console.MarkupLine("[red]No editor is configured in git. Please configure an editor using 'git config --global core.editor '.[/]"); + outputProvider.Error("No editor is configured in git. Please configure an editor using 'git config --global core.editor '."); return; } @@ -185,7 +185,7 @@ public void OpenFileInEditorAndWaitForClose(string path) if (process == null) { - console.MarkupLine("[red]Failed to start editor process.[/]"); + outputProvider.Error("Failed to start editor process."); return; } @@ -195,7 +195,7 @@ public void OpenFileInEditorAndWaitForClose(string path) private string ExecuteGitCommandAndReturnOutput(string command) { if (settings.Verbose) - console.MarkupLine($"[grey]git {command}[/]"); + outputProvider.Debug($"git {command}"); var infoBuilder = new StringBuilder(); var errorBuilder = new StringBuilder(); @@ -210,13 +210,13 @@ private string ExecuteGitCommandAndReturnOutput(string command) if (result != 0) { - console.MarkupLine($"[red]{errorBuilder}[/]"); + outputProvider.Error($"{errorBuilder}"); throw new Exception("Failed to execute git command."); } if (settings.Verbose && infoBuilder.Length > 0) { - console.MarkupLine($"[grey]{infoBuilder}[/]"); + outputProvider.Debug($"{infoBuilder}"); } return infoBuilder.ToString(); @@ -227,7 +227,7 @@ private void ExecuteGitCommand(string command) if (settings.DryRun) { if (settings.Verbose) - console.MarkupLine($"[grey]git {command}[/]"); + outputProvider.Debug($"git {command}"); } else { @@ -239,7 +239,7 @@ private void ExecuteGitCommand(string command) // changes to the Git repository as the output might be important. // In verbose mode we would have already written the output // of the command so don't write it again. - console.MarkupLine($"[grey]{output}[/]"); + outputProvider.Debug($"{output}"); } } } From 19c5dbeb1db5a4c6c9ba203a121ea5dce4b0d8b1 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Mon, 16 Dec 2024 17:26:43 +1100 Subject: [PATCH 03/24] Use output provider in GitHub operations --- .../PullRequests/CreatePullRequestsCommand.cs | 2 +- .../PullRequests/OpenPullRequestsCommand.cs | 2 +- src/Stack/Commands/Stack/CleanupStackCommand.cs | 2 +- src/Stack/Commands/Stack/DeleteStackCommand.cs | 2 +- src/Stack/Commands/Stack/StackStatusCommand.cs | 2 +- src/Stack/Commands/Stack/UpdateStackCommand.cs | 2 +- src/Stack/Git/GitHubOperations.cs | 15 ++++++++------- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs index f278434..9f28f50 100644 --- a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs @@ -26,7 +26,7 @@ public override async Task ExecuteAsync(CommandContext context, CreatePullR new ConsoleInputProvider(console), outputProvider, new GitOperations(outputProvider, settings.GetGitOperationSettings()), - new GitHubOperations(console, settings.GetGitHubOperationSettings()), + new GitHubOperations(outputProvider, settings.GetGitHubOperationSettings()), new FileOperations(), new StackConfig()); diff --git a/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs b/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs index a25e686..f1bf100 100644 --- a/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs @@ -26,7 +26,7 @@ public override async Task ExecuteAsync(CommandContext context, OpenPullReq new ConsoleInputProvider(console), outputProvider, new GitOperations(outputProvider, settings.GetGitOperationSettings()), - new GitHubOperations(console, settings.GetGitHubOperationSettings()), + new GitHubOperations(outputProvider, settings.GetGitHubOperationSettings()), new StackConfig()); await handler.Handle(new OpenPullRequestsCommandInputs(settings.Name)); diff --git a/src/Stack/Commands/Stack/CleanupStackCommand.cs b/src/Stack/Commands/Stack/CleanupStackCommand.cs index 29331dc..03f684d 100644 --- a/src/Stack/Commands/Stack/CleanupStackCommand.cs +++ b/src/Stack/Commands/Stack/CleanupStackCommand.cs @@ -33,7 +33,7 @@ public override async Task ExecuteAsync(CommandContext context, CleanupStac new ConsoleInputProvider(console), outputProvider, new GitOperations(outputProvider, settings.GetGitOperationSettings()), - new GitHubOperations(console, settings.GetGitHubOperationSettings()), + new GitHubOperations(outputProvider, settings.GetGitHubOperationSettings()), new StackConfig()); await handler.Handle(new CleanupStackCommandInputs(settings.Name, settings.Force)); diff --git a/src/Stack/Commands/Stack/DeleteStackCommand.cs b/src/Stack/Commands/Stack/DeleteStackCommand.cs index 6142127..1061147 100644 --- a/src/Stack/Commands/Stack/DeleteStackCommand.cs +++ b/src/Stack/Commands/Stack/DeleteStackCommand.cs @@ -33,7 +33,7 @@ public override async Task ExecuteAsync(CommandContext context, DeleteStack new ConsoleInputProvider(console), outputProvider, new GitOperations(outputProvider, settings.GetGitOperationSettings()), - new GitHubOperations(console, settings.GetGitHubOperationSettings()), + new GitHubOperations(outputProvider, settings.GetGitHubOperationSettings()), new StackConfig()); var response = await handler.Handle(new DeleteStackCommandInputs(settings.Name, settings.Force)); diff --git a/src/Stack/Commands/Stack/StackStatusCommand.cs b/src/Stack/Commands/Stack/StackStatusCommand.cs index ce2500c..8c2b66a 100644 --- a/src/Stack/Commands/Stack/StackStatusCommand.cs +++ b/src/Stack/Commands/Stack/StackStatusCommand.cs @@ -30,7 +30,7 @@ public override async Task ExecuteAsync(CommandContext context, StackStatus new ConsoleInputProvider(console), outputProvider, new GitOperations(outputProvider, settings.GetGitOperationSettings()), - new GitHubOperations(console, settings.GetGitHubOperationSettings()), + new GitHubOperations(outputProvider, settings.GetGitHubOperationSettings()), new StackConfig()); await handler.Handle(new StackStatusCommandInputs(settings.Name, settings.All)); diff --git a/src/Stack/Commands/Stack/UpdateStackCommand.cs b/src/Stack/Commands/Stack/UpdateStackCommand.cs index abde171..b4ae374 100644 --- a/src/Stack/Commands/Stack/UpdateStackCommand.cs +++ b/src/Stack/Commands/Stack/UpdateStackCommand.cs @@ -31,7 +31,7 @@ public override async Task ExecuteAsync(CommandContext context, UpdateStack new ConsoleInputProvider(console), outputProvider, new GitOperations(outputProvider, settings.GetGitOperationSettings()), - new GitHubOperations(console, settings.GetGitHubOperationSettings()), + new GitHubOperations(outputProvider, settings.GetGitHubOperationSettings()), new StackConfig()); await handler.Handle(new UpdateStackCommandInputs(settings.Name, settings.Force)); diff --git a/src/Stack/Git/GitHubOperations.cs b/src/Stack/Git/GitHubOperations.cs index a927109..e8957d8 100644 --- a/src/Stack/Git/GitHubOperations.cs +++ b/src/Stack/Git/GitHubOperations.cs @@ -1,6 +1,7 @@ using System.Text; using Octopus.Shellfish; using Spectre.Console; +using Stack.Infrastructure; namespace Stack.Git; @@ -48,7 +49,7 @@ public interface IGitHubOperations void OpenPullRequest(GitHubPullRequest pullRequest); } -public class GitHubOperations(IAnsiConsole console, GitHubOperationSettings settings) : IGitHubOperations +public class GitHubOperations(IOutputProvider outputProvider, GitHubOperationSettings settings) : IGitHubOperations { public GitHubPullRequest? GetPullRequest(string branch) { @@ -91,7 +92,7 @@ public void OpenPullRequest(GitHubPullRequest pullRequest) private string ExecuteGitHubCommandAndReturnOutput(string command) { if (settings.Verbose) - console.MarkupLine($"[grey]gh {command}[/]"); + outputProvider.Debug($"gh {command}"); var infoBuilder = new StringBuilder(); var errorBuilder = new StringBuilder(); @@ -106,13 +107,13 @@ private string ExecuteGitHubCommandAndReturnOutput(string command) if (result != 0) { - console.MarkupLine($"[red]{errorBuilder}[/]"); + outputProvider.Error($"{errorBuilder}"); throw new Exception("Failed to execute gh command."); } if (settings.Verbose && infoBuilder.Length > 0) { - console.MarkupLine($"[grey]{infoBuilder}[/]"); + outputProvider.Debug($"{infoBuilder}"); } return infoBuilder.ToString(); @@ -121,7 +122,7 @@ private string ExecuteGitHubCommandAndReturnOutput(string command) private void ExecuteGitHubCommand(string command) { if (settings.Verbose) - console.MarkupLine($"[grey]gh {command}[/]"); + outputProvider.Debug($"gh {command}"); if (!settings.DryRun) { @@ -144,14 +145,14 @@ private void ExecuteGitHubCommandInternal(string command) if (result != 0) { - console.MarkupLine($"[red]{errorBuilder}[/]"); + outputProvider.Error($"{errorBuilder}"); throw new Exception("Failed to execute gh command."); } else { if (infoBuilder.Length > 0) { - console.MarkupLine($"[grey]{infoBuilder}[/]"); + outputProvider.Debug($"{infoBuilder}"); } } } From d591429613ef1e3e689e2db10257709d5f6ed5d1 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Mon, 16 Dec 2024 17:41:36 +1100 Subject: [PATCH 04/24] More wip --- .../Commands/Branch/AddBranchCommandHandlerTests.cs | 4 ++-- src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs index 96afdaf..962b52b 100644 --- a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs @@ -19,16 +19,16 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_AddsBran var sourceBranch = Some.BranchName(); var anotherBranch = Some.BranchName(); var branchToAdd = Some.BranchName(); - var repo = new TestGitRepositoryBuilder() + using var repo = new TestGitRepositoryBuilder() .WithBranch(sourceBranch) .WithBranch(anotherBranch) .WithBranch(branchToAdd) .Build(); - var gitOperations = new GitOperations(Substitute.For(), new GitOperationSettings(false, false, repo.LocalDirectory.DirectoryPath)); var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); var stacks = new List( diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs index 19c0213..7e420b1 100644 --- a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -1,6 +1,7 @@ using System; using LibGit2Sharp; using Microsoft.VisualBasic; +using Stack.Git; namespace Stack.Tests.Helpers; @@ -49,6 +50,7 @@ public class TestGitRepository(TemporaryDirectory LocalDirectory, TemporaryDirec 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() { From d92f9975de9be5d27d6e86e3bef3b05846a42384 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 08:25:31 +1100 Subject: [PATCH 05/24] More add branch tests with proper Git repo --- .../Branch/AddBranchCommandHandlerTests.cs | 172 ++++++++++-------- 1 file changed, 97 insertions(+), 75 deletions(-) diff --git a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs index 962b52b..3a905fa 100644 --- a/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/AddBranchCommandHandlerTests.cs @@ -59,29 +59,32 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_AddsBran public async Task WhenStackNameProvided_DoesNotAskForStackName_AddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-5"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); // Act await handler.Handle(new AddBranchCommandInputs("Stack1", null)); @@ -90,8 +93,8 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_AddsBranchFromSta inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, branchToAdd]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } @@ -99,27 +102,30 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_AddsBranchFromSta public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_AddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]) ]); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-5"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToAdd); // Act await handler.Handle(new AddBranchCommandInputs(null, null)); @@ -128,7 +134,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_AddsBranchFromSt inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, branchToAdd]) }); } @@ -136,21 +142,25 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_AddsBranchFromSt public async Task WhenStackNameProvided_ButStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -166,22 +176,25 @@ await handler.Invoking(async h => await h.Handle(new AddBranchCommandInputs(inva public async Task WhenBranchNameProvided_DoesNotAskForBranchName_AddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -191,13 +204,13 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_AddsBranchFromS inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); // Act - await handler.Handle(new AddBranchCommandInputs(null, "branch-5")); + await handler.Handle(new AddBranchCommandInputs(null, branchToAdd)); // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, branchToAdd]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Select(Questions.SelectBranch, Arg.Any()); } @@ -206,29 +219,32 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_AddsBranchFromS public async Task WhenBranchNameProvided_ButBranchDoesNotExistLocally_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); // Act and assert - var invalidBranchName = Some.Name(); + var invalidBranchName = Some.BranchName(); await handler.Invoking(async h => await h.Handle(new AddBranchCommandInputs(null, invalidBranchName))) .Should() .ThrowAsync() @@ -239,54 +255,60 @@ await handler.Invoking(async h => await h.Handle(new AddBranchCommandInputs(null public async Task WhenBranchNameProvided_ButBranchAlreadyExistsInStack_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, branchToAdd]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); // Act and assert - await handler.Invoking(async h => await h.Handle(new AddBranchCommandInputs(null, "branch-5"))) + await handler.Invoking(async h => await h.Handle(new AddBranchCommandInputs(null, branchToAdd))) .Should() .ThrowAsync() - .WithMessage($"Branch 'branch-5' already exists in stack 'Stack1'."); + .WithMessage($"Branch '{branchToAdd}' already exists in stack 'Stack1'."); } [Fact] public async Task WhenAllInputsProvided_DoesNotAskForAnything_AddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToAdd = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .WithBranch(branchToAdd) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new AddBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -294,13 +316,13 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_AddsBranchFromStac .Do(ci => stacks = ci.ArgAt>(0)); // Act - await handler.Handle(new AddBranchCommandInputs("Stack1", "branch-5")); + await handler.Handle(new AddBranchCommandInputs("Stack1", branchToAdd)); // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, branchToAdd]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.ReceivedCalls().Should().BeEmpty(); } From 3eddc88999e6c144132d42fcf63a960bf75d53e9 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 08:28:04 +1100 Subject: [PATCH 06/24] Change single new branch test to use proper Git repo --- .../Branch/NewBranchCommandHandlerTests.cs | 28 ++++++++++--------- src/Stack/Commands/Branch/NewBranchCommand.cs | 1 - 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs index b587912..e4c951e 100644 --- a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs @@ -15,22 +15,24 @@ public class NewBranchCommandHandlerTests public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_CreatesAndAddsBranchToStackAndSwitchesToBranch() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -38,7 +40,7 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_CreatesA .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns("branch-5"); + inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(true); // Act @@ -47,10 +49,10 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_CreatesA // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); - gitOperations.Received().ChangeBranch("branch-5"); + gitOperations.GetCurrentBranch().Should().Be(newBranch); } [Fact] diff --git a/src/Stack/Commands/Branch/NewBranchCommand.cs b/src/Stack/Commands/Branch/NewBranchCommand.cs index 00f9c96..d9601b6 100644 --- a/src/Stack/Commands/Branch/NewBranchCommand.cs +++ b/src/Stack/Commands/Branch/NewBranchCommand.cs @@ -61,7 +61,6 @@ public async Task Handle(NewBranchCommandInputs inputs { await Task.CompletedTask; - var defaultBranch = gitOperations.GetDefaultBranch(); var remoteUri = gitOperations.GetRemoteUri(); var currentBranch = gitOperations.GetCurrentBranch(); var branches = gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate(); From f2956a0dceff727fd97d00fb932090b2c6240f4e Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 08:28:43 +1100 Subject: [PATCH 07/24] Cleanup --- src/Stack/Commands/Branch/AddBranchCommand.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Stack/Commands/Branch/AddBranchCommand.cs b/src/Stack/Commands/Branch/AddBranchCommand.cs index 57ccf93..51d4371 100644 --- a/src/Stack/Commands/Branch/AddBranchCommand.cs +++ b/src/Stack/Commands/Branch/AddBranchCommand.cs @@ -57,7 +57,6 @@ public async Task Handle(AddBranchCommandInputs inputs { await Task.CompletedTask; - // var defaultBranch = gitOperations.GetDefaultBranch(); var remoteUri = gitOperations.GetRemoteUri(); var currentBranch = gitOperations.GetCurrentBranch(); var branches = gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate(); From 6530c2864b77b98b1e326e534d3c0b0f9db1577f Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 08:41:25 +1100 Subject: [PATCH 08/24] More new branch tests with Git repo --- .../Branch/NewBranchCommandHandlerTests.cs | 224 ++++++++++-------- src/Stack/Git/GitOperations.cs | 6 - 2 files changed, 121 insertions(+), 109 deletions(-) diff --git a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs index e4c951e..8c1ccb3 100644 --- a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs @@ -56,25 +56,27 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_CreatesA } [Fact] - public async Task WhenSwitchBranchIsFalse_CreatsAndAddsBranchToStackButDoesNotSwitchToBranch() + public async Task WhenSwitchBranchIsFalse_CreatesAndAddsBranchToStackButDoesNotSwitchToBranch() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -82,7 +84,7 @@ public async Task WhenSwitchBranchIsFalse_CreatsAndAddsBranchToStackButDoesNotSw .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns("branch-5"); + inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(false); // Act @@ -91,39 +93,41 @@ public async Task WhenSwitchBranchIsFalse_CreatsAndAddsBranchToStackButDoesNotSw // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); - gitOperations.DidNotReceive().ChangeBranch("branch-5"); + gitOperations.GetCurrentBranch().Should().NotBe(newBranch); } [Fact] public async Task WhenStackNameProvided_DoesNotAskForStackName_CreatesAndAddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns("branch-5"); + inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); // Act await handler.Handle(new NewBranchCommandInputs("Stack1", null, false)); @@ -132,8 +136,8 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_CreatesAndAddsBra inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } @@ -141,28 +145,30 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_CreatesAndAddsBra public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_CreatesAndAddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]) ]); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns("branch-5"); + inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); // Act await handler.Handle(new NewBranchCommandInputs(null, null, false)); @@ -171,7 +177,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_CreatesAndAddsBr inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), }); } @@ -179,21 +185,24 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_CreatesAndAddsBr public async Task WhenStackNameProvided_ButStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -209,22 +218,24 @@ await handler.Invoking(async h => await h.Handle(new NewBranchCommandInputs(inva public async Task WhenBranchNameProvided_DoesNotAskForBranchName_CreatesAndAddsBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -234,13 +245,13 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_CreatesAndAddsB inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); // Act - await handler.Handle(new NewBranchCommandInputs(null, "branch-5", false)); + await handler.Handle(new NewBranchCommandInputs(null, newBranch, false)); // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Text(Questions.BranchName); } @@ -249,22 +260,23 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_CreatesAndAddsB public async Task WhenBranchNameProvided_ButBranchAlreadyExistLocally_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(true); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -272,64 +284,68 @@ public async Task WhenBranchNameProvided_ButBranchAlreadyExistLocally_Throws() // Act and assert var invalidBranchName = Some.Name(); - await handler.Invoking(async h => await h.Handle(new NewBranchCommandInputs(null, "branch-5", false))) + await handler.Invoking(async h => await h.Handle(new NewBranchCommandInputs(null, anotherBranch, false))) .Should() .ThrowAsync() - .WithMessage($"Branch 'branch-5' already exists locally."); + .WithMessage($"Branch '{anotherBranch}' already exists locally."); } [Fact] public async Task WhenBranchNameProvided_ButBranchAlreadyExistsInStack_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); // Act and assert - await handler.Invoking(async h => await h.Handle(new NewBranchCommandInputs(null, "branch-5", false))) + await handler.Invoking(async h => await h.Handle(new NewBranchCommandInputs(null, newBranch, false))) .Should() .ThrowAsync() - .WithMessage($"Branch 'branch-5' already exists in stack 'Stack1'."); + .WithMessage($"Branch '{newBranch}' already exists in stack 'Stack1'."); } [Fact] public async Task WhenAllInputsProvided_DoesNotAskForAnything_CreatesAndAddsBranchFromStackAndChangesToBranch() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -337,13 +353,13 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_CreatesAndAddsBran .Do(ci => stacks = ci.ArgAt>(0)); // Act - await handler.Handle(new NewBranchCommandInputs("Stack1", "branch-5", true)); + await handler.Handle(new NewBranchCommandInputs("Stack1", newBranch, true)); // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.ReceivedCalls().Should().BeEmpty(); } @@ -352,22 +368,24 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_CreatesAndAddsBran public async Task WhenStackHasANameWithMultipleWords_SuggestsAGoodDefaultNewBranchName() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(anotherBranch) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new NewBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-5").Returns(false); - var stacks = new List( [ - new("A stack with multiple words", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("A stack with multiple words", repo.RemoteUri, sourceBranch, [anotherBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -375,7 +393,7 @@ public async Task WhenStackHasANameWithMultipleWords_SuggestsAGoodDefaultNewBran .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("A stack with multiple words"); - inputProvider.Text(Questions.BranchName, "a-stack-with-multiple-words-2").Returns("branch-5"); + inputProvider.Text(Questions.BranchName, "a-stack-with-multiple-words-2").Returns(newBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(true); // Act @@ -384,9 +402,9 @@ public async Task WhenStackHasANameWithMultipleWords_SuggestsAGoodDefaultNewBran // Assert stacks.Should().BeEquivalentTo(new List { - new("A stack with multiple words", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("A stack with multiple words", repo.RemoteUri, sourceBranch, [anotherBranch, newBranch]), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); - gitOperations.Received().ChangeBranch("branch-5"); + gitOperations.GetCurrentBranch().Should().Be(newBranch); } } diff --git a/src/Stack/Git/GitOperations.cs b/src/Stack/Git/GitOperations.cs index 27be14a..60c9b5a 100644 --- a/src/Stack/Git/GitOperations.cs +++ b/src/Stack/Git/GitOperations.cs @@ -24,7 +24,6 @@ public interface IGitOperations void DeleteLocalBranch(string branchName); void MergeFromLocalSourceBranch(string sourceBranchName); string GetCurrentBranch(); - string GetDefaultBranch(); bool DoesLocalBranchExist(string branchName); bool DoesRemoteBranchExist(string branchName); string[] GetBranchesThatExistLocally(string[] branches); @@ -97,11 +96,6 @@ public string GetCurrentBranch() return ExecuteGitCommandAndReturnOutput("branch --show-current").Trim(); } - public string GetDefaultBranch() - { - return ExecuteGitCommandAndReturnOutput("symbolic-ref refs/remotes/origin/HEAD").Trim().Replace("refs/remotes/origin/", ""); - } - public bool DoesRemoteBranchExist(string branchName) { return ExecuteGitCommandAndReturnOutput($"ls-remote --heads origin {branchName}").Trim().Length > 0; From d05eceb5b2e1b7834cc4b342b207ef838620f19b Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 09:01:54 +1100 Subject: [PATCH 09/24] Add initial cleanup stack test with proper Git repo --- .../Stack/CleanupStackCommandHandlerTests.cs | 28 +++++++++++-------- .../Helpers/TestGitRepositoryBuilder.cs | 22 ++++++++++++--- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs index d4f10a2..36ae998 100644 --- a/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs @@ -15,25 +15,29 @@ public class CleanupStackCommandHandlerTests public async Task WhenBranchExistsLocally_ButNotInRemote_BranchIsDeletedLocally() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]), - new("Stack2", remoteUri, "branch-3", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); + + var remoteUri = Some.HttpsUri().ToString(); stackConfig.Load().Returns(stacks); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); @@ -43,7 +47,7 @@ public async Task WhenBranchExistsLocally_ButNotInRemote_BranchIsDeletedLocally( await handler.Handle(CleanupStackCommandInputs.Empty); // Assert - gitOperations.Received().DeleteLocalBranch("branch-2"); + gitOperations.GetBranchesThatExistLocally([branchToCleanup, branchToKeep]).Should().BeEquivalentTo([branchToKeep]); } [Fact] diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs index 7e420b1..6ebdd6d 100644 --- a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -7,11 +7,17 @@ namespace Stack.Tests.Helpers; public class TestGitRepositoryBuilder { - List branches = []; + List<(string Name, bool PushToRemote)> branches = []; public TestGitRepositoryBuilder WithBranch(string branch) { - branches.Add(branch); + branches.Add((branch, false)); + return this; + } + + public TestGitRepositoryBuilder WithBranch(string branch, bool pushToRemote) + { + branches.Add((branch, pushToRemote)); return this; } @@ -21,7 +27,7 @@ public TestGitRepository Build() var remoteDirectory = new TemporaryDirectory(remote); var localDirectory = TemporaryDirectory.Create(); - Repository.Init(remoteDirectory.DirectoryPath, true); + 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); @@ -30,7 +36,15 @@ public TestGitRepository Build() foreach (var branch in branches) { - repo.CreateBranch(branch); + repo.CreateBranch(branch.Name); + + if (branch.PushToRemote) + { + repo.Branches.Update(repo.Branches[branch.Name], + b => b.Remote = repo.Network.Remotes["origin"].Name, + b => b.UpstreamBranch = $"refs/heads/{branch.Name}"); + repo.Network.Push(repo.Branches[branch.Name]); + } } return new TestGitRepository(localDirectory, remoteDirectory, repo); From 149e3199fc6dfc3b84ed071f6dc779e1332bdce8 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 09:29:48 +1100 Subject: [PATCH 10/24] Some cleanup --- src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs index 6ebdd6d..cf5e083 100644 --- a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -36,14 +36,14 @@ public TestGitRepository Build() foreach (var branch in branches) { - repo.CreateBranch(branch.Name); + var newBranch = repo.CreateBranch(branch.Name); if (branch.PushToRemote) { - repo.Branches.Update(repo.Branches[branch.Name], + repo.Branches.Update(newBranch, b => b.Remote = repo.Network.Remotes["origin"].Name, - b => b.UpstreamBranch = $"refs/heads/{branch.Name}"); - repo.Network.Push(repo.Branches[branch.Name]); + b => b.UpstreamBranch = newBranch.CanonicalName); + repo.Network.Push(newBranch); } } From eff579ba1deb20e1a4cd9f1096834bfe7cc2acb4 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 17:11:04 +1100 Subject: [PATCH 11/24] Make all cleanup tests use proper Git repo --- .../Stack/CleanupStackCommandHandlerTests.cs | 146 ++++++++++-------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs index 36ae998..1c17622 100644 --- a/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs @@ -54,24 +54,26 @@ public async Task WhenBranchExistsLocally_ButNotInRemote_BranchIsDeletedLocally( public async Task WhenBranchExistsLocally_AndInRemote_BranchIsNotDeletedLocally() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToKeep = Some.BranchName(); + var anotherBranchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToKeep, true) + .WithBranch(anotherBranchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1", "branch-2"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]), - new("Stack2", remoteUri, "branch-3", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToKeep, anotherBranchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -82,31 +84,33 @@ public async Task WhenBranchExistsLocally_AndInRemote_BranchIsNotDeletedLocally( await handler.Handle(CleanupStackCommandInputs.Empty); // Assert - gitOperations.DidNotReceive().DeleteLocalBranch("branch-2"); + gitOperations.GetBranchesThatExistLocally([branchToKeep, anotherBranchToKeep]).Should().BeEquivalentTo([branchToKeep, anotherBranchToKeep]); } [Fact] public async Task WhenConfirmationIsFalse_DoesNotDeleteAnyBranches() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]), - new("Stack2", remoteUri, "branch-3", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -117,31 +121,33 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteAnyBranches() await handler.Handle(CleanupStackCommandInputs.Empty); // Assert - gitOperations.DidNotReceive().DeleteLocalBranch("branch-2"); + gitOperations.GetBranchesThatExistLocally([branchToKeep, branchToCleanup]).Should().BeEquivalentTo([branchToKeep, branchToCleanup]); } [Fact] public async Task WhenStackNameIsProvided_ItIsNotAskedFor() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]), - new("Stack2", remoteUri, "branch-3", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -158,24 +164,26 @@ public async Task WhenStackNameIsProvided_ItIsNotAskedFor() public async Task WhenForceIsProvided_ItIsNotAskedFor() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]), - new("Stack2", remoteUri, "branch-3", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -192,24 +200,26 @@ public async Task WhenForceIsProvided_ItIsNotAskedFor() public async Task WhenStackNameIsProvided_ButStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]), - new("Stack2", remoteUri, "branch-3", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -227,23 +237,25 @@ await handler.Invoking(async h => await h.Handle(new CleanupStackCommandInputs(i public async Task WhenOnlyASingleStackExists_StackIsSelectedAutomatically() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 CleanupStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-2"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-2"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), ]); stackConfig.Load().Returns(stacks); From 3459f445f4b18f036b6073a8485103b3a4aec762 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 17:14:07 +1100 Subject: [PATCH 12/24] Change delete stack test that cleans up branches to use proper Git repo --- .../Stack/DeleteStackCommandHandlerTests.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs index bb2ace0..2baa724 100644 --- a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs @@ -252,24 +252,26 @@ await handler public async Task WhenThereAreLocalBranchesThatAreNotInTheRemote_AsksToCleanup_AndDeletesThemBeforeDeletingStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch, true) + .WithBranch(branchToCleanup, false) + .WithBranch(branchToKeep, true) + .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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.GetBranchesThatExistLocally(Arg.Any()).Returns(["branch-1", "branch-3"]); - gitOperations.GetBranchesThatExistInRemote(Arg.Any()).Returns(["branch-1"]); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToCleanup, branchToKeep]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -288,9 +290,9 @@ public async Task WhenThereAreLocalBranchesThatAreNotInTheRemote_AsksToCleanup_A response.Should().Be(new DeleteStackCommandResponse("Stack1")); stacks.Should().BeEquivalentTo(new List { - new("Stack2", remoteUri, "branch-2", []) + new("Stack2", repo.RemoteUri, sourceBranch, []) }); - gitOperations.Received().DeleteLocalBranch("branch-3"); + gitOperations.GetBranchesThatExistLocally([branchToCleanup, branchToKeep]).Should().BeEquivalentTo([branchToKeep]); } [Fact] From 460892de8057c24756704b04ada6b4e2d81b3886 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 17:16:42 +1100 Subject: [PATCH 13/24] Make all delete tests use proper Git repo --- .../Stack/DeleteStackCommandHandlerTests.cs | 136 +++++++++--------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs index 2baa724..a64df89 100644 --- a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs @@ -15,22 +15,22 @@ public class DeleteStackCommandHandlerTests public async Task WhenNoInputsAreProvided_AsksForName_AndConfirmation_AndDeletesStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -47,7 +47,7 @@ public async Task WhenNoInputsAreProvided_AsksForName_AndConfirmation_AndDeletes response.Should().Be(new DeleteStackCommandResponse("Stack1")); stacks.Should().BeEquivalentTo(new List { - new("Stack2", remoteUri, "branch-2", []) + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } @@ -55,22 +55,22 @@ public async Task WhenNoInputsAreProvided_AsksForName_AndConfirmation_AndDeletes public async Task WhenConfirmationIsFalse_DoesNotDeleteStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -87,8 +87,8 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteStack() response.Should().Be(new DeleteStackCommandResponse(null)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } @@ -96,22 +96,22 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteStack() public async Task WhenNameIsProvided_AsksForConfirmation_AndDeletesStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -127,7 +127,7 @@ public async Task WhenNameIsProvided_AsksForConfirmation_AndDeletesStack() response.Should().Be(new DeleteStackCommandResponse("Stack1")); stacks.Should().BeEquivalentTo(new List { - new("Stack2", remoteUri, "branch-2", []) + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); @@ -137,22 +137,22 @@ public async Task WhenNameIsProvided_AsksForConfirmation_AndDeletesStack() public async Task WhenForceIsProvided_DoesNotAskForConfirmation_AndDeletesStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -168,7 +168,7 @@ public async Task WhenForceIsProvided_DoesNotAskForConfirmation_AndDeletesStack( response.Should().Be(new DeleteStackCommandResponse("Stack1")); stacks.Should().BeEquivalentTo(new List { - new("Stack2", remoteUri, "branch-2", []) + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Confirm(Questions.ConfirmDeleteStack); @@ -178,22 +178,22 @@ public async Task WhenForceIsProvided_DoesNotAskForConfirmation_AndDeletesStack( public async Task WhenNameAndForceAreProvided_DoesNotAskForAnyInput_AndDeletesStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -207,7 +207,7 @@ public async Task WhenNameAndForceAreProvided_DoesNotAskForAnyInput_AndDeletesSt response.Should().Be(new DeleteStackCommandResponse("Stack1")); stacks.Should().BeEquivalentTo(new List { - new("Stack2", remoteUri, "branch-2", []) + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.ReceivedCalls().Should().BeEmpty(); @@ -217,22 +217,22 @@ public async Task WhenNameAndForceAreProvided_DoesNotAskForAnyInput_AndDeletesSt public async Task WhenStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []), - new("Stack2", remoteUri, "branch-2", []) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -299,21 +299,21 @@ public async Task WhenThereAreLocalBranchesThatAreNotInTheRemote_AsksToCleanup_A public async Task WhenOnlyOneStackExists_DoesNotAskForStackName() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToCleanup = Some.BranchName(); + var branchToKeep = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder().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 DeleteStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", []) + new("Stack1", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig From 35ce798d163244751139b30e394152e1bed58407 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 17:30:16 +1100 Subject: [PATCH 14/24] Cleanup --- .../Stack/DeleteStackCommandHandlerTests.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs index a64df89..1e20364 100644 --- a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs @@ -16,8 +16,6 @@ public async Task WhenNoInputsAreProvided_AsksForName_AndConfirmation_AndDeletes { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); @@ -56,8 +54,6 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteStack() { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); @@ -97,8 +93,6 @@ public async Task WhenNameIsProvided_AsksForConfirmation_AndDeletesStack() { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); @@ -138,8 +132,6 @@ public async Task WhenForceIsProvided_DoesNotAskForConfirmation_AndDeletesStack( { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); @@ -179,8 +171,6 @@ public async Task WhenNameAndForceAreProvided_DoesNotAskForAnyInput_AndDeletesSt { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); @@ -218,8 +208,6 @@ public async Task WhenStackDoesNotExist_Throws() { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); @@ -300,8 +288,6 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName() { // Arrange var sourceBranch = Some.BranchName(); - var branchToCleanup = Some.BranchName(); - var branchToKeep = Some.BranchName(); using var repo = new TestGitRepositoryBuilder().Build(); var stackConfig = Substitute.For(); From 6c4c6c87b71804a3b67cd519745eaf4e18896c34 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 17 Dec 2024 17:30:45 +1100 Subject: [PATCH 15/24] Change new stack tests to use proper Git repo --- .../Stack/NewStackCommandHandlerTests.cs | 225 ++++++++++-------- 1 file changed, 126 insertions(+), 99 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/NewStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/NewStackCommandHandlerTests.cs index 755fd51..c37a384 100644 --- a/src/Stack.Tests/Commands/Stack/NewStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/NewStackCommandHandlerTests.cs @@ -15,17 +15,19 @@ public class NewStackCommandHandlerTests public async Task WithANewBranch_AndSwitchingToTheBranch_TheStackIsCreatedAndTheCurrentBranchIsChanged() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig @@ -33,40 +35,43 @@ public async Task WithANewBranch_AndSwitchingToTheBranch_TheStackIsCreatedAndThe .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Text(Questions.StackName).Returns("Stack1"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(true); inputProvider.Select(Questions.AddOrCreateBranch, Arg.Any(), Arg.Any>()).Returns(BranchAction.Create); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns("new-branch"); + inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(true); // Act var response = await handler.Handle(NewStackCommandInputs.Empty); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", BranchAction.Create, "new-branch")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, BranchAction.Create, newBranch)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["new-branch"]) + new("Stack1", repo.RemoteUri, sourceBranch, [newBranch]) }); - gitOperations.Received().ChangeBranch("new-branch"); + gitOperations.GetCurrentBranch().Should().Be(newBranch); } [Fact] public async Task WithAnExistingBranch_AndSwitchingToTheBranch_TheStackIsCreatedAndTheCurrentBranchIsChanged() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var existingBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(existingBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig @@ -74,39 +79,44 @@ public async Task WithAnExistingBranch_AndSwitchingToTheBranch_TheStackIsCreated .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Text(Questions.StackName).Returns("Stack1"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(true); inputProvider.Select(Questions.AddOrCreateBranch, Arg.Any(), Arg.Any>()).Returns(BranchAction.Add); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-2"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(existingBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(true); // Act var response = await handler.Handle(NewStackCommandInputs.Empty); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", BranchAction.Add, "branch-2")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, BranchAction.Add, existingBranch)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-2"]) + new("Stack1", repo.RemoteUri, sourceBranch, [existingBranch]) }); - gitOperations.Received().ChangeBranch("branch-2"); + gitOperations.GetCurrentBranch().Should().Be(existingBranch); } [Fact] public async Task WithNoBranch_TheStackIsCreatedAndTheCurrentBranchIsNotChanged() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var existingBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(existingBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); + gitOperations.ChangeBranch(existingBranch); var stacks = new List(); stackConfig.Load().Returns(stacks); @@ -116,36 +126,40 @@ public async Task WithNoBranch_TheStackIsCreatedAndTheCurrentBranchIsNotChanged( inputProvider.Text(Questions.StackName).Returns("Stack1"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(false); // Act var response = await handler.Handle(NewStackCommandInputs.Empty); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", null, null)); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, null, null)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", []) + new("Stack1", repo.RemoteUri, sourceBranch, []) }); - gitOperations.DidNotReceive().ChangeBranch(Arg.Any()); + gitOperations.GetCurrentBranch().Should().Be(existingBranch); } [Fact] public async Task WithANewBranch_AndNotSwitchingToTheBranch_TheStackIsCreatedAndTheCurrentBranchIsNotChanged() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); + gitOperations.ChangeBranch(sourceBranch); var stacks = new List(); stackConfig.Load().Returns(stacks); @@ -155,39 +169,44 @@ public async Task WithANewBranch_AndNotSwitchingToTheBranch_TheStackIsCreatedAnd inputProvider.Text(Questions.StackName).Returns("Stack1"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(true); inputProvider.Select(Questions.AddOrCreateBranch, Arg.Any(), Arg.Any>()).Returns(BranchAction.Create); - inputProvider.Text(Questions.BranchName, Arg.Any()).Returns("new-branch-1"); + inputProvider.Text(Questions.BranchName, Arg.Any()).Returns(newBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(false); // Act var response = await handler.Handle(NewStackCommandInputs.Empty); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", BranchAction.Create, "new-branch-1")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, BranchAction.Create, newBranch)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["new-branch-1"]) + new("Stack1", repo.RemoteUri, sourceBranch, [newBranch]) }); - gitOperations.DidNotReceive().ChangeBranch("new-branch-1"); + gitOperations.GetCurrentBranch().Should().Be(sourceBranch); } [Fact] public async Task WithAnExistingBranch_AndNotSwitchingToTheBranch_TheStackIsCreatedAndTheCurrentBranchIsNotChanged() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var existingBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(existingBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); + gitOperations.ChangeBranch(sourceBranch); var stacks = new List(); stackConfig.Load().Returns(stacks); @@ -196,47 +215,48 @@ public async Task WithAnExistingBranch_AndNotSwitchingToTheBranch_TheStackIsCrea .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Text(Questions.StackName).Returns("Stack1"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(true); inputProvider.Select(Questions.AddOrCreateBranch, Arg.Any(), Arg.Any>()).Returns(BranchAction.Add); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-2"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(existingBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(false); // Act var response = await handler.Handle(NewStackCommandInputs.Empty); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", BranchAction.Add, "branch-2")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, BranchAction.Add, existingBranch)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-2"]) + new("Stack1", repo.RemoteUri, sourceBranch, [existingBranch]) }); - gitOperations.DidNotReceive().ChangeBranch("branch-2"); + gitOperations.GetCurrentBranch().Should().Be(sourceBranch); } [Fact] public async Task WhenStackNameIsProvidedInInputs_TheProviderIsNotAskedForAName_AndTheStackIsCreatedWithTheName() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(false); var inputs = new NewStackCommandInputs("Stack1", null, null); @@ -245,10 +265,10 @@ public async Task WhenStackNameIsProvidedInInputs_TheProviderIsNotAskedForAName_ var response = await handler.Handle(inputs); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", null, null)); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, null, null)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", []) + new("Stack1", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Text(Questions.StackName); @@ -258,17 +278,18 @@ public async Task WhenStackNameIsProvidedInInputs_TheProviderIsNotAskedForAName_ public async Task WhenSourceBranchIsProvidedInInputs_TheProviderIsNotAskedForTheBranch_AndTheStackIsCreatedWithTheSourceBranch() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig @@ -278,16 +299,16 @@ public async Task WhenSourceBranchIsProvidedInInputs_TheProviderIsNotAskedForThe inputProvider.Text(Questions.StackName).Returns("Stack1"); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(false); - var inputs = new NewStackCommandInputs(null, "branch-1", null); + var inputs = new NewStackCommandInputs(null, sourceBranch, null); // Act var response = await handler.Handle(inputs); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", null, null)); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, null, null)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", []) + new("Stack1", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Select(Questions.SelectBranch, Arg.Any()); @@ -297,17 +318,19 @@ public async Task WhenSourceBranchIsProvidedInInputs_TheProviderIsNotAskedForThe public async Task WhenBranchNameIsProvidedInInputs_TheProviderIsNotAskedForTheBranchName_AndTheStackIsCreatedWithTheBranchName() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig @@ -315,19 +338,19 @@ public async Task WhenBranchNameIsProvidedInInputs_TheProviderIsNotAskedForTheBr .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Text(Questions.StackName).Returns("Stack1"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); // Note there shouldn't be any more inputs required at all - var inputs = new NewStackCommandInputs(null, null, "new-branch"); + var inputs = new NewStackCommandInputs(null, null, newBranch); // Act var response = await handler.Handle(inputs); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", BranchAction.Create, "new-branch")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, BranchAction.Create, newBranch)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["new-branch"]) + new("Stack1", repo.RemoteUri, sourceBranch, [newBranch]) }); inputProvider.Received().Text(Questions.StackName); @@ -340,33 +363,35 @@ public async Task WhenBranchNameIsProvidedInInputs_TheProviderIsNotAskedForTheBr public async Task WhenAllInputsAreProvided_TheProviderIsNotAskedForAnything_AndTheStackIsCreatedCorrectly() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - var inputs = new NewStackCommandInputs("Stack1", "branch-1", "new-branch"); + var inputs = new NewStackCommandInputs("Stack1", sourceBranch, newBranch); // Act var response = await handler.Handle(inputs); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", "branch-1", BranchAction.Create, "new-branch")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("Stack1", sourceBranch, BranchAction.Create, newBranch)); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["new-branch"]) + new("Stack1", repo.RemoteUri, sourceBranch, [newBranch]) }); inputProvider.ReceivedCalls().Should().BeEmpty(); @@ -376,17 +401,19 @@ public async Task WhenAllInputsAreProvided_TheProviderIsNotAskedForAnything_AndT public async Task WhenAStackHasANameWithMultipleWords_SuggestsAGoodDefaultNewBranchName() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var newBranch = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .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 NewStackCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetLocalBranchesOrderedByMostRecentCommitterDate().Returns(["branch-1", "branch-2"]); - var stacks = new List(); stackConfig.Load().Returns(stacks); stackConfig @@ -394,22 +421,22 @@ public async Task WhenAStackHasANameWithMultipleWords_SuggestsAGoodDefaultNewBra .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Text(Questions.StackName).Returns("A stack with multiple words"); - inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns("branch-1"); + inputProvider.Select(Questions.SelectSourceBranch, Arg.Any()).Returns(sourceBranch); inputProvider.Confirm(Questions.ConfirmAddOrCreateBranch).Returns(true); inputProvider.Select(Questions.AddOrCreateBranch, Arg.Any(), Arg.Any>()).Returns(BranchAction.Create); - inputProvider.Text(Questions.BranchName, "a-stack-with-multiple-words-1").Returns("new-branch"); + inputProvider.Text(Questions.BranchName, "a-stack-with-multiple-words-1").Returns(newBranch); inputProvider.Confirm(Questions.ConfirmSwitchToBranch).Returns(true); // Act var response = await handler.Handle(NewStackCommandInputs.Empty); // Assert - response.Should().BeEquivalentTo(new NewStackCommandResponse("A stack with multiple words", "branch-1", BranchAction.Create, "new-branch")); + response.Should().BeEquivalentTo(new NewStackCommandResponse("A stack with multiple words", sourceBranch, BranchAction.Create, newBranch)); stacks.Should().BeEquivalentTo(new List { - new("A stack with multiple words", remoteUri, "branch-1", ["new-branch"]) + new("A stack with multiple words", repo.RemoteUri, sourceBranch, [newBranch]) }); - gitOperations.Received().ChangeBranch("new-branch"); + gitOperations.GetCurrentBranch().Should().Be(newBranch); } } From 78cd1dc482e20fdf702a4b270314f6d2c58577e6 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Wed, 18 Dec 2024 08:56:39 +1100 Subject: [PATCH 16/24] Use proper Git repository for remove branch tests --- .../Branch/RemoveBranchCommandHandlerTests.cs | 178 ++++++++++-------- 1 file changed, 97 insertions(+), 81 deletions(-) diff --git a/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs index ffa3255..1467603 100644 --- a/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/RemoveBranchCommandHandlerTests.cs @@ -7,7 +7,7 @@ using Stack.Infrastructure; using Stack.Tests.Helpers; -namespace Stack.Tests.Commands.Stack; +namespace Stack.Tests.Commands.Branch; public class RemoveBranchCommandHandlerTests { @@ -15,21 +15,23 @@ public class RemoveBranchCommandHandlerTests public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_RemovesBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -37,7 +39,7 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_RemovesB .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-3"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToRemove); inputProvider.Confirm(Questions.ConfirmRemoveBranch).Returns(true); // Act @@ -46,8 +48,8 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_RemovesB // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } @@ -55,28 +57,30 @@ public async Task WhenNoInputsProvided_AsksForStackAndBranchAndConfirms_RemovesB public async Task WhenStackNameProvided_DoesNotAskForStackName_RemovesBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-3"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToRemove); inputProvider.Confirm(Questions.ConfirmRemoveBranch).Returns(true); // Act @@ -86,8 +90,8 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_RemovesBranchFrom inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); } @@ -95,21 +99,23 @@ public async Task WhenStackNameProvided_DoesNotAskForStackName_RemovesBranchFrom public async Task WhenStackNameProvided_ButStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -125,21 +131,23 @@ await handler.Invoking(async h => await h.Handle(new RemoveBranchCommandInputs(i public async Task WhenBranchNameProvided_DoesNotAskForBranchName_RemovesBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -150,13 +158,13 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_RemovesBranchFr inputProvider.Confirm(Questions.ConfirmRemoveBranch).Returns(true); // Act - await handler.Handle(new RemoveBranchCommandInputs(null, "branch-3", false)); + await handler.Handle(new RemoveBranchCommandInputs(null, branchToRemove, false)); // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Select(Questions.SelectBranch, Arg.Any()); } @@ -165,21 +173,23 @@ public async Task WhenBranchNameProvided_DoesNotAskForBranchName_RemovesBranchFr public async Task WhenBranchNameProvided_ButBranchDoesNotExistInStack_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); @@ -197,21 +207,23 @@ await handler.Invoking(async h => await h.Handle(new RemoveBranchCommandInputs(n public async Task WhenForceProvided_DoesNotAskForConfirmation_RemovesBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -219,7 +231,7 @@ public async Task WhenForceProvided_DoesNotAskForConfirmation_RemovesBranchFromS .Do(ci => stacks = ci.ArgAt>(0)); inputProvider.Select(Questions.SelectStack, Arg.Any()).Returns("Stack1"); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-3"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToRemove); // Act await handler.Handle(new RemoveBranchCommandInputs(null, null, true)); @@ -227,8 +239,8 @@ public async Task WhenForceProvided_DoesNotAskForConfirmation_RemovesBranchFromS // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Confirm(Questions.ConfirmRemoveBranch); } @@ -237,21 +249,23 @@ public async Task WhenForceProvided_DoesNotAskForConfirmation_RemovesBranchFromS public async Task WhenAllInputsProvided_DoesNotAskForAnything_RemovesBranchFromStack() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]), + new("Stack2", repo.RemoteUri, sourceBranch, []) ]); stackConfig.Load().Returns(stacks); stackConfig @@ -259,13 +273,13 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_RemovesBranchFromS .Do(ci => stacks = ci.ArgAt>(0)); // Act - await handler.Handle(new RemoveBranchCommandInputs("Stack1", "branch-3", true)); + await handler.Handle(new RemoveBranchCommandInputs("Stack1", branchToRemove, true)); // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-5"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, []), + new("Stack2", repo.RemoteUri, sourceBranch, []) }); inputProvider.ReceivedCalls().Should().BeEmpty(); } @@ -274,27 +288,29 @@ public async Task WhenAllInputsProvided_DoesNotAskForAnything_RemovesBranchFromS public async Task WhenOnlyOneStackExists_DoesNotAskForStackName() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branchToRemove = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToRemove) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new RemoveBranchCommandHandler(inputProvider, outputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3", "branch-5"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToRemove]) ]); stackConfig.Load().Returns(stacks); stackConfig .WhenForAnyArgs(s => s.Save(Arg.Any>())) .Do(ci => stacks = ci.ArgAt>(0)); - inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns("branch-3"); + inputProvider.Select(Questions.SelectBranch, Arg.Any()).Returns(branchToRemove); inputProvider.Confirm(Questions.ConfirmRemoveBranch).Returns(true); // Act @@ -303,7 +319,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName() // Assert stacks.Should().BeEquivalentTo(new List { - new("Stack1", remoteUri, "branch-1", ["branch-5"]) + new("Stack1", repo.RemoteUri, sourceBranch, []) }); inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); From 38c48e80de7b1a208cb3c501d5d293ad1f54f255 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Wed, 18 Dec 2024 08:57:38 +1100 Subject: [PATCH 17/24] Fix namespace --- src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs index 8c1ccb3..aec5421 100644 --- a/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Branch/NewBranchCommandHandlerTests.cs @@ -7,7 +7,7 @@ using Stack.Infrastructure; using Stack.Tests.Helpers; -namespace Stack.Tests.Commands.Stack; +namespace Stack.Tests.Commands.Branch; public class NewBranchCommandHandlerTests { From 8e335c0d97d72553bb6ce248ff0fef4bbee256dc Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Wed, 18 Dec 2024 09:05:30 +1100 Subject: [PATCH 18/24] Use proper Git repository for stack switch tests --- .../Stack/StackSwitchCommandHandlerTests.cs | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/StackSwitchCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/StackSwitchCommandHandlerTests.cs index 60f682d..a790a22 100644 --- a/src/Stack.Tests/Commands/Stack/StackSwitchCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/StackSwitchCommandHandlerTests.cs @@ -15,59 +15,70 @@ public class StackSwitchCommandHandlerTests public async Task WhenNoInputsAreProvided_AsksForBranch_ChangesToBranch() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToSwitchTo = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToSwitchTo) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); + var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new StackSwitchCommandHandler(inputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); + gitOperations.ChangeBranch(sourceBranch); var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToSwitchTo]), + new("Stack2", repo.RemoteUri, sourceBranch, [anotherBranch]) ]); stackConfig.Load().Returns(stacks); - inputProvider.SelectGrouped(Questions.SelectBranch, Arg.Any[]>()).Returns("branch-3"); + inputProvider.SelectGrouped(Questions.SelectBranch, Arg.Any[]>()).Returns(branchToSwitchTo); // Act await handler.Handle(new StackSwitchCommandInputs(null)); // Assert - gitOperations.Received().ChangeBranch("branch-3"); + gitOperations.GetCurrentBranch().Should().Be(branchToSwitchTo); } [Fact] public async Task WhenBranchIsProvided_DoesNotAskForBranch_ChangesToBranch() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToSwitchTo = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToSwitchTo) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); + var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new StackSwitchCommandHandler(inputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-3").Returns(true); + gitOperations.ChangeBranch(sourceBranch); var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToSwitchTo]), + new("Stack2", repo.RemoteUri, sourceBranch, [anotherBranch]) ]); stackConfig.Load().Returns(stacks); // Act - await handler.Handle(new StackSwitchCommandInputs("branch-3")); + await handler.Handle(new StackSwitchCommandInputs(branchToSwitchTo)); // Assert - gitOperations.Received().ChangeBranch("branch-3"); + gitOperations.GetCurrentBranch().Should().Be(branchToSwitchTo); inputProvider.ReceivedCalls().Should().BeEmpty(); } @@ -75,27 +86,33 @@ public async Task WhenBranchIsProvided_DoesNotAskForBranch_ChangesToBranch() public async Task WhenBranchIsProvided_AndBranchDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var anotherBranch = Some.BranchName(); + var branchToSwitchTo = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(sourceBranch) + .WithBranch(branchToSwitchTo) + .Build(); + var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); + var outputProvider = Substitute.For(); + var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var handler = new StackSwitchCommandHandler(inputProvider, gitOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - gitOperations.GetCurrentBranch().Returns("branch-1"); - gitOperations.DoesLocalBranchExist("branch-3").Returns(false); + gitOperations.ChangeBranch(sourceBranch); var stacks = new List( [ - new("Stack1", remoteUri, "branch-1", ["branch-3"]), - new("Stack2", remoteUri, "branch-2", ["branch-4"]) + new("Stack1", repo.RemoteUri, sourceBranch, [branchToSwitchTo]), + new("Stack2", repo.RemoteUri, sourceBranch, [anotherBranch]) ]); stackConfig.Load().Returns(stacks); // Act and assert - await handler.Invoking(h => h.Handle(new StackSwitchCommandInputs("branch-3"))) + var invalidBranchName = Some.BranchName(); + await handler.Invoking(h => h.Handle(new StackSwitchCommandInputs(invalidBranchName))) .Should().ThrowAsync() - .WithMessage("Branch 'branch-3' does not exist."); + .WithMessage($"Branch '{invalidBranchName}' does not exist."); } } From e44bdf0199d32f194297f69af0e1d5812f8fd8b9 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Thu, 19 Dec 2024 09:23:35 +1100 Subject: [PATCH 19/24] Improvements to git repo building --- .../Stack/StackStatusCommandHandlerTests.cs | 348 +++++++----------- .../Helpers/TestGitRepositoryBuilder.cs | 230 ++++++++++-- 2 files changed, 346 insertions(+), 232 deletions(-) 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(); } From 6f61ec3f0200365bee48777a43a4e93f5215b623 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Thu, 19 Dec 2024 15:57:55 +1100 Subject: [PATCH 20/24] Use proper Git repository for stack update tests --- .../Stack/UpdateStackCommandHandlerTests.cs | 287 ++++++++++-------- .../Helpers/TestGitRepositoryBuilder.cs | 10 + 2 files changed, 169 insertions(+), 128 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs index 77144bb..b868d63 100644 --- a/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs @@ -15,163 +15,161 @@ public class UpdateStackCommandHandlerTests public async Task WhenMultipleBranchesExistInAStack_UpdatesAndMergesEachBranchInSequence() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .WithNumberOfEmptyCommits(b => b.OnBranch(branch1).PushToRemote(), 3) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + var tipOfBranch1 = repo.GetTipOfBranch(branch1); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + 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"); inputProvider.Confirm(Questions.ConfirmUpdateStack).Returns(true); - gitOperations.DoesRemoteBranchExist(Arg.Any()).Returns(true); - // Act await handler.Handle(new UpdateStackCommandInputs(null, false)); // Assert - gitOperations.Received().UpdateBranch("branch-1"); - gitOperations.Received().UpdateBranch("branch-2"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-1"); - gitOperations.Received().PushBranch("branch-2"); - - gitOperations.Received().UpdateBranch("branch-2"); - gitOperations.Received().UpdateBranch("branch-3"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-2"); - gitOperations.Received().PushBranch("branch-3"); + repo.GetCommitsReachableFromBranch(branch1).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfBranch1); } [Fact] public async Task WhenABranchInTheStackNoLongerExistsOnTheRemote_SkipsOverUpdatingThatBranch() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10)) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + 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"); inputProvider.Confirm(Questions.ConfirmUpdateStack).Returns(true); - var branchesThatExistInRemote = new List(["branch-1", "branch-3"]); - - gitOperations.DoesRemoteBranchExist(Arg.Is(b => branchesThatExistInRemote.Contains(b))).Returns(true); - // Act await handler.Handle(new UpdateStackCommandInputs(null, false)); // Assert - gitOperations.Received().UpdateBranch("branch-1"); - gitOperations.Received().UpdateBranch("branch-3"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-1"); - gitOperations.Received().PushBranch("branch-3"); - - gitOperations.DidNotReceive().UpdateBranch("branch-2"); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); } [Fact] public async Task WhenABranchInTheStackExistsOnTheRemote_ButThePullRequestIsMerged_SkipsOverUpdatingThatBranch() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + 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"); inputProvider.Confirm(Questions.ConfirmUpdateStack).Returns(true); - var branchesThatExistInRemote = new List(["branch-1", "branch-2", "branch-3"]); - - gitOperations.DoesRemoteBranchExist(Arg.Is(b => branchesThatExistInRemote.Contains(b))).Returns(true); - gitHubOperations.GetPullRequest("branch-2").Returns(new GitHubPullRequest(1, Some.Name(), Some.Name(), GitHubPullRequestStates.Merged, Some.HttpsUri(), false)); + gitHubOperations.GetPullRequest(branch1).Returns(new GitHubPullRequest(1, Some.Name(), Some.Name(), GitHubPullRequestStates.Merged, Some.HttpsUri(), false)); // Act await handler.Handle(new UpdateStackCommandInputs(null, false)); // Assert - gitOperations.Received().UpdateBranch("branch-1"); - gitOperations.Received().UpdateBranch("branch-3"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-1"); - gitOperations.Received().PushBranch("branch-3"); - - gitOperations.DidNotReceive().UpdateBranch("branch-2"); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); } [Fact] public async Task WhenNameIsProvided_DoesNotAskForName_UpdatesCorrectStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .WithNumberOfEmptyCommits(b => b.OnBranch(branch1).PushToRemote(), 3) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + var tipOfBranch1 = repo.GetTipOfBranch(branch1); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, []); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); inputProvider.Confirm(Questions.ConfirmUpdateStack).Returns(true); - gitOperations.DoesRemoteBranchExist(Arg.Any()).Returns(true); - // Act await handler.Handle(new UpdateStackCommandInputs("Stack1", false)); // Assert - gitOperations.Received().UpdateBranch("branch-1"); - gitOperations.Received().UpdateBranch("branch-2"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-1"); - gitOperations.Received().PushBranch("branch-2"); - - gitOperations.Received().UpdateBranch("branch-2"); - gitOperations.Received().UpdateBranch("branch-3"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-2"); - gitOperations.Received().PushBranch("branch-3"); - + repo.GetCommitsReachableFromBranch(branch1).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfBranch1); inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); } @@ -179,20 +177,29 @@ public async Task WhenNameIsProvided_DoesNotAskForName_UpdatesCorrectStack() public async Task WhenForceIsProvided_DoesNotAskForConfirmation() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .WithNumberOfEmptyCommits(b => b.OnBranch(branch1).PushToRemote(), 3) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + var tipOfBranch1 = repo.GetTipOfBranch(branch1); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, []); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -202,6 +209,9 @@ public async Task WhenForceIsProvided_DoesNotAskForConfirmation() await handler.Handle(new UpdateStackCommandInputs(null, true)); // Assert + repo.GetCommitsReachableFromBranch(branch1).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfBranch1); inputProvider.DidNotReceive().Confirm(Questions.ConfirmUpdateStack); } @@ -209,20 +219,29 @@ public async Task WhenForceIsProvided_DoesNotAskForConfirmation() public async Task WhenNameIsProvided_ButStackDoesNotExist_Throws() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .WithNumberOfEmptyCommits(b => b.OnBranch(branch1).PushToRemote(), 3) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + var tipOfBranch1 = repo.GetTipOfBranch(branch1); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + var stack2 = new Config.Stack("Stack2", repo.RemoteUri, sourceBranch, []); var stacks = new List([stack1, stack2]); stackConfig.Load().Returns(stacks); @@ -237,74 +256,86 @@ await handler.Invoking(async h => await h.Handle(new UpdateStackCommandInputs(in public async Task WhenOnASpecificBranchInTheStack_TheSameBranchIsSetAsCurrentAfterTheUpdate() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .WithNumberOfEmptyCommits(b => b.OnBranch(branch1).PushToRemote(), 3) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + var tipOfBranch1 = repo.GetTipOfBranch(branch1); + 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 UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); - var remoteUri = Some.HttpsUri().ToString(); - - gitOperations.GetRemoteUri().Returns(remoteUri); - // We are on a specific branch in the stack - gitOperations.GetCurrentBranch().Returns("branch-2"); + gitOperations.ChangeBranch(branch1); - var stack1 = new Config.Stack("Stack1", remoteUri, "branch-1", ["branch-2", "branch-3"]); - var stack2 = new Config.Stack("Stack2", remoteUri, "branch-2", ["branch-4", "branch-5"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); + 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"); inputProvider.Confirm(Questions.ConfirmUpdateStack).Returns(true); - gitOperations.DoesRemoteBranchExist(Arg.Any()).Returns(true); - // Act await handler.Handle(new UpdateStackCommandInputs(null, false)); // Assert - gitOperations.Received().ChangeBranch("branch-2"); + repo.GetCommitsReachableFromBranch(branch1).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfBranch1); + gitOperations.GetCurrentBranch().Should().Be(branch1); } [Fact] public async Task WhenOnlyASingleStackExists_DoesNotAskForStackName_UpdatesStack() { // Arrange - var gitOperations = Substitute.For(); - var gitHubOperations = Substitute.For(); + var sourceBranch = Some.BranchName(); + var branch1 = Some.BranchName(); + var branch2 = Some.BranchName(); + using var repo = new TestGitRepositoryBuilder() + .WithBranch(builder => builder.WithName(sourceBranch).PushToRemote()) + .WithBranch(builder => builder.WithName(branch1).FromSourceBranch(sourceBranch).WithNumberOfEmptyCommits(10).PushToRemote()) + .WithBranch(builder => builder.WithName(branch2).FromSourceBranch(branch1).WithNumberOfEmptyCommits(1).PushToRemote()) + .WithNumberOfEmptyCommits(b => b.OnBranch(sourceBranch).PushToRemote(), 5) + .WithNumberOfEmptyCommits(b => b.OnBranch(branch1).PushToRemote(), 3) + .Build(); + + var tipOfSourceBranch = repo.GetTipOfBranch(sourceBranch); + var tipOfBranch1 = repo.GetTipOfBranch(branch1); + 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 UpdateStackCommandHandler(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-2", "branch-3"]); + var stack1 = new Config.Stack("Stack1", repo.RemoteUri, sourceBranch, [branch1, branch2]); var stacks = new List([stack1]); stackConfig.Load().Returns(stacks); inputProvider.Confirm(Questions.ConfirmUpdateStack).Returns(true); - gitOperations.DoesRemoteBranchExist(Arg.Any()).Returns(true); - // Act await handler.Handle(new UpdateStackCommandInputs(null, false)); // Assert - gitOperations.Received().UpdateBranch("branch-1"); - gitOperations.Received().UpdateBranch("branch-2"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-1"); - gitOperations.Received().PushBranch("branch-2"); - - gitOperations.Received().UpdateBranch("branch-2"); - gitOperations.Received().UpdateBranch("branch-3"); - gitOperations.Received().MergeFromLocalSourceBranch("branch-2"); - gitOperations.Received().PushBranch("branch-3"); + repo.GetCommitsReachableFromBranch(branch1).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfSourceBranch); + repo.GetCommitsReachableFromBranch(branch2).Should().Contain(tipOfBranch1); inputProvider.DidNotReceive().Select(Questions.SelectStack, Arg.Any()); } diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs index e048dd4..e84e98e 100644 --- a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -246,6 +246,16 @@ public class TestGitRepository(TemporaryDirectory LocalDirectory, TemporaryDirec public string RemoteUri => RemoteDirectory.DirectoryPath; public GitOperationSettings GitOperationSettings => new GitOperationSettings(false, false, LocalDirectory.DirectoryPath); + public Commit GetTipOfBranch(string branchName) + { + return LocalRepository.Branches[branchName].Tip; + } + + public List GetCommitsReachableFromBranch(string branchName) + { + return [.. LocalRepository.Branches[branchName].Commits]; + } + public void Dispose() { GC.SuppressFinalize(this); From 02e2fbb5d6de5a35ad157008bfe831065519f809 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Thu, 19 Dec 2024 18:34:46 +1100 Subject: [PATCH 21/24] Use test output provider for debugging --- .../Stack/UpdateStackCommandHandlerTests.cs | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs index b868d63..e7f2409 100644 --- a/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs @@ -6,10 +6,54 @@ using Stack.Tests.Helpers; using Stack.Infrastructure; using Stack.Commands.Helpers; +using Xunit.Abstractions; namespace Stack.Tests.Commands.Stack; -public class UpdateStackCommandHandlerTests +public class TestOutputProvider(ITestOutputHelper testOutputHelper) : IOutputProvider +{ + public void Debug(string message) + { + testOutputHelper.WriteLine($"DEBUG: {message}"); + } + + public void Error(string message) + { + testOutputHelper.WriteLine($"ERROR: {message}"); + } + + public void Information(string message) + { + testOutputHelper.WriteLine($"INFO: {message}"); + } + + public void Rule(string message) + { + testOutputHelper.WriteLine($"RULE: {message}"); + } + + public void Status(string message, Action action) + { + testOutputHelper.WriteLine($"STATUS: {message}"); + action(); + } + + public void Tree(string header, string[] items) + { + testOutputHelper.WriteLine($"TREE: {header}"); + foreach (var item in items) + { + testOutputHelper.WriteLine($" {item}"); + } + } + + public void Warning(string message) + { + testOutputHelper.WriteLine($"WARNING: {message}"); + } +} + +public class UpdateStackCommandHandlerTests(ITestOutputHelper testOutputHelper) { [Fact] public async Task WhenMultipleBranchesExistInAStack_UpdatesAndMergesEachBranchInSequence() @@ -31,7 +75,7 @@ public async Task WhenMultipleBranchesExistInAStack_UpdatesAndMergesEachBranchIn var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -71,7 +115,7 @@ public async Task WhenABranchInTheStackNoLongerExistsOnTheRemote_SkipsOverUpdati var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -109,7 +153,7 @@ public async Task WhenABranchInTheStackExistsOnTheRemote_ButThePullRequestIsMerg var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -151,7 +195,7 @@ public async Task WhenNameIsProvided_DoesNotAskForName_UpdatesCorrectStack() var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -193,7 +237,7 @@ public async Task WhenForceIsProvided_DoesNotAskForConfirmation() var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -235,7 +279,7 @@ public async Task WhenNameIsProvided_ButStackDoesNotExist_Throws() var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -272,7 +316,7 @@ public async Task WhenOnASpecificBranchInTheStack_TheSameBranchIsSetAsCurrentAft var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); @@ -318,7 +362,7 @@ public async Task WhenOnlyASingleStackExists_DoesNotAskForStackName_UpdatesStack var stackConfig = Substitute.For(); var inputProvider = Substitute.For(); - var outputProvider = Substitute.For(); + var outputProvider = new TestOutputProvider(testOutputHelper); var gitOperations = new GitOperations(outputProvider, repo.GitOperationSettings); var gitHubOperations = Substitute.For(); var handler = new UpdateStackCommandHandler(inputProvider, outputProvider, gitOperations, gitHubOperations, stackConfig); From f8340f19a6c8ae2c2e328db8cfa7d1f8dea04eda Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Thu, 19 Dec 2024 18:39:13 +1100 Subject: [PATCH 22/24] Set committer details to get test to run --- .github/workflows/build-and-test.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 292b5f7..749c96d 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -64,6 +64,9 @@ jobs: - name: Build run: dotnet build --no-restore -c Release + - name: Set committer details + run: git config --global user.email "github-actions[bot]@users.noreply.github.com" && git config --global user.name "github-actions[bot]" + - name: Test run: dotnet test --no-build -c Release --verbosity normal --logger "trx;LogFileName=${{ github.workspace }}/tests/test_results.trx" From eb98f90bfd0f5d5e0909dbdcf59f5c38fec90414 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Thu, 19 Dec 2024 18:42:59 +1100 Subject: [PATCH 23/24] Set user.name and user.email in tests --- .github/workflows/build-and-test.yaml | 3 --- src/Stack.Tests/Helpers/Some.cs | 1 + src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs | 6 ++++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 749c96d..292b5f7 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -64,9 +64,6 @@ jobs: - name: Build run: dotnet build --no-restore -c Release - - name: Set committer details - run: git config --global user.email "github-actions[bot]@users.noreply.github.com" && git config --global user.name "github-actions[bot]" - - name: Test run: dotnet test --no-build -c Release --verbosity normal --logger "trx;LogFileName=${{ github.workspace }}/tests/test_results.trx" diff --git a/src/Stack.Tests/Helpers/Some.cs b/src/Stack.Tests/Helpers/Some.cs index 1991841..b28803f 100644 --- a/src/Stack.Tests/Helpers/Some.cs +++ b/src/Stack.Tests/Helpers/Some.cs @@ -8,4 +8,5 @@ public static class Some public static string ShortName() => Guid.NewGuid().ToString("N").Substring(0, 8); public static string BranchName() => $"branch-{ShortName()}"; public static Uri HttpsUri() => new($"https://{ShortName()}.com"); + public static string Email() => $"{ShortName()}@{ShortName()}.com"; } diff --git a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs index e84e98e..14dfcf8 100644 --- a/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs +++ b/src/Stack.Tests/Helpers/TestGitRepositoryBuilder.cs @@ -209,6 +209,12 @@ public TestGitRepository Build() var remoteRepo = new Repository(Repository.Init(remoteDirectory.DirectoryPath, true)); var localRepo = new Repository(Repository.Clone(remote, localDirectory.DirectoryPath)); + + // Ensure that we can commit to this repository when tests run + // in a context where the user's name and email are not set. + localRepo.Config.Add("user.name", Some.Name()); + localRepo.Config.Add("user.email", Some.Email()); + var defaultBranchName = Some.BranchName(); localRepo.Refs.UpdateTarget("HEAD", "refs/heads/" + defaultBranchName); From 780da87f969307e7067f3e91596536ad35baac81 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Thu, 19 Dec 2024 18:45:59 +1100 Subject: [PATCH 24/24] Move TestOutputProvider to separate file --- .../Stack/UpdateStackCommandHandlerTests.cs | 43 ----------------- src/Stack.Tests/Helpers/TestOutputProvider.cs | 47 +++++++++++++++++++ 2 files changed, 47 insertions(+), 43 deletions(-) create mode 100644 src/Stack.Tests/Helpers/TestOutputProvider.cs diff --git a/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs index e7f2409..b90042d 100644 --- a/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/UpdateStackCommandHandlerTests.cs @@ -10,49 +10,6 @@ namespace Stack.Tests.Commands.Stack; -public class TestOutputProvider(ITestOutputHelper testOutputHelper) : IOutputProvider -{ - public void Debug(string message) - { - testOutputHelper.WriteLine($"DEBUG: {message}"); - } - - public void Error(string message) - { - testOutputHelper.WriteLine($"ERROR: {message}"); - } - - public void Information(string message) - { - testOutputHelper.WriteLine($"INFO: {message}"); - } - - public void Rule(string message) - { - testOutputHelper.WriteLine($"RULE: {message}"); - } - - public void Status(string message, Action action) - { - testOutputHelper.WriteLine($"STATUS: {message}"); - action(); - } - - public void Tree(string header, string[] items) - { - testOutputHelper.WriteLine($"TREE: {header}"); - foreach (var item in items) - { - testOutputHelper.WriteLine($" {item}"); - } - } - - public void Warning(string message) - { - testOutputHelper.WriteLine($"WARNING: {message}"); - } -} - public class UpdateStackCommandHandlerTests(ITestOutputHelper testOutputHelper) { [Fact] diff --git a/src/Stack.Tests/Helpers/TestOutputProvider.cs b/src/Stack.Tests/Helpers/TestOutputProvider.cs new file mode 100644 index 0000000..89a7198 --- /dev/null +++ b/src/Stack.Tests/Helpers/TestOutputProvider.cs @@ -0,0 +1,47 @@ +using Stack.Infrastructure; +using Xunit.Abstractions; + +namespace Stack.Tests.Helpers; + +public class TestOutputProvider(ITestOutputHelper testOutputHelper) : IOutputProvider +{ + public void Debug(string message) + { + testOutputHelper.WriteLine($"DEBUG: {message}"); + } + + public void Error(string message) + { + testOutputHelper.WriteLine($"ERROR: {message}"); + } + + public void Information(string message) + { + testOutputHelper.WriteLine($"INFO: {message}"); + } + + public void Rule(string message) + { + testOutputHelper.WriteLine($"RULE: {message}"); + } + + public void Status(string message, Action action) + { + testOutputHelper.WriteLine($"STATUS: {message}"); + action(); + } + + public void Tree(string header, string[] items) + { + testOutputHelper.WriteLine($"TREE: {header}"); + foreach (var item in items) + { + testOutputHelper.WriteLine($" {item}"); + } + } + + public void Warning(string message) + { + testOutputHelper.WriteLine($"WARNING: {message}"); + } +}