From 0ee099f5a57d3149f49baadf61e6ace3eb635314 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Wed, 25 Aug 2021 17:07:50 -0700 Subject: [PATCH 1/5] start unit tests Start with the easy stuff --- .gitignore | 1 + Walrus.CLI/Program.cs | 3 +- Walrus.Core.Tests/UnitTest1.cs | 13 ---- Walrus.Core.Tests/Walrus.Core.Tests.csproj | 5 ++ Walrus.Core.Tests/WalrusServiceTests.cs | 80 ++++++++++++++++++++++ Walrus.Core/WalrusService.cs | 4 +- 6 files changed, 91 insertions(+), 15 deletions(-) delete mode 100644 Walrus.Core.Tests/UnitTest1.cs create mode 100644 Walrus.Core.Tests/WalrusServiceTests.cs diff --git a/.gitignore b/.gitignore index da57c3b..968854f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /dist/ /Walrus.CLI/Properties/launchSettings.json /.idea/ +/Walrus.sln.DotSettings.user diff --git a/Walrus.CLI/Program.cs b/Walrus.CLI/Program.cs index 298b1c9..1adfe9b 100644 --- a/Walrus.CLI/Program.cs +++ b/Walrus.CLI/Program.cs @@ -9,6 +9,7 @@ using System.CommandLine.Parsing; using System.IO; using System.Threading.Tasks; + using Microsoft.Extensions.Logging.Abstractions; using Walrus.Core; class Program @@ -85,7 +86,7 @@ private static ServiceProvider ConfigureServices() var configuration = new ConfigurationBuilder() .AddJsonFile(configFile, optional: true) .Build(); - + var services = new ServiceCollection() .AddLogging(configure => { diff --git a/Walrus.Core.Tests/UnitTest1.cs b/Walrus.Core.Tests/UnitTest1.cs deleted file mode 100644 index 7e35d14..0000000 --- a/Walrus.Core.Tests/UnitTest1.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Walrus.Core.Tests -{ - using Xunit; - - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} diff --git a/Walrus.Core.Tests/Walrus.Core.Tests.csproj b/Walrus.Core.Tests/Walrus.Core.Tests.csproj index cc7744a..5a1207c 100644 --- a/Walrus.Core.Tests/Walrus.Core.Tests.csproj +++ b/Walrus.Core.Tests/Walrus.Core.Tests.csproj @@ -7,6 +7,7 @@ + @@ -19,4 +20,8 @@ + + + + diff --git a/Walrus.Core.Tests/WalrusServiceTests.cs b/Walrus.Core.Tests/WalrusServiceTests.cs new file mode 100644 index 0000000..e745442 --- /dev/null +++ b/Walrus.Core.Tests/WalrusServiceTests.cs @@ -0,0 +1,80 @@ +namespace Walrus.Core.Tests +{ + using System; + using System.Collections.Generic; + using Microsoft.Extensions.Logging.Abstractions; + using Xunit; + + public class WalrusServiceTests + { + /// + /// Assert that constructor parameters are validated + /// + [Fact] + public void LoggerRequired() + { + // Setup + NullLogger logger = null; + var config = WalrusConfig.Default; + + // Execute + // Assert + Assert.Throws(() => new WalrusService(logger, config)); + } + + /// + /// Assert that constructor parameters are validated + /// + [Fact] + public void ConfigRequired() + { + // Setup + var logger = new NullLogger(); + WalrusConfig config = null; + + // Execute + // Assert + Assert.Throws(() => new WalrusService(logger, config)); + } + + + /// + /// Assert that query parameters are validated + /// + [Fact] + public void QueryRequired() + { + // Setup + var logger = new NullLogger(); + var config = WalrusConfig.Default; + + // Execute + var service = new WalrusService(logger, config); + + // Assert + Assert.Throws(() => service.ExecuteQuery(null)); + } + + + /// + /// Assert that configuration is captured + /// + [Fact] + public void ConfigStored() + { + // Setup + var logger = new NullLogger(); + var config = WalrusConfig.Default; + config.AuthorAliases = new Dictionary> {{"test", new[] {"hello@example.com"}}}; + config.RepositoryRoots = new[] {"/home/user/code", "C:\\code\\foo"}; + + // Execute + var service = new WalrusService(logger, config); + + // Assert + Assert.Equal(config.DirectoryScanDepth, service.Config.DirectoryScanDepth); + Assert.Equal(config.RepositoryRoots, service.Config.RepositoryRoots); + Assert.Equal(config.AuthorAliases, service.Config.AuthorAliases); + } + } +} diff --git a/Walrus.Core/WalrusService.cs b/Walrus.Core/WalrusService.cs index 93a5a62..3c767ad 100644 --- a/Walrus.Core/WalrusService.cs +++ b/Walrus.Core/WalrusService.cs @@ -11,7 +11,7 @@ /// public class WalrusService : IWalrusService { - private ILogger _logger; + private readonly ILogger _logger; /// /// Create a new WalrusService @@ -33,6 +33,8 @@ public WalrusService(ILogger logger, WalrusConfig config) /// public IEnumerable ExecuteQuery(WalrusQuery query) { + Ensure.IsNotNull(nameof(query), query); + var searchRoot = query.CurrentDirectory ? Environment.CurrentDirectory : null; var commits = From 6c8cc16a327c2e902786402d56988c927985e2ed Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Wed, 25 Aug 2021 20:20:52 -0700 Subject: [PATCH 2/5] [core] refactor to make mockable LibGit2Sharp is surprisingly rigid and not easily testable. This refactor moves the responsibilities around to reduce how much injection is required to use the library. The repository provider is now responsible for generating repository and commit objects through a mockable interface. --- Walrus.CLI/Commands/BaseCommand.cs | 2 +- Walrus.CLI/Commands/QueryCommand.cs | 1 + Walrus.CLI/Commands/ShowCommand.cs | 14 +- Walrus.CLI/Program.cs | 4 +- Walrus.Core/IWalrusConfig.cs | 28 ++++ Walrus.Core/IWalrusService.cs | 14 +- Walrus.Core/Internal/Utilities.cs | 2 +- Walrus.Core/{ => Repository}/CommitGroup.cs | 0 Walrus.Core/Repository/IRepositoryProvider.cs | 20 +++ Walrus.Core/Repository/RepositoryProvider.cs | 119 +++++++++++++++++ Walrus.Core/Repository/WalrusCommit.cs | 51 ++++++++ Walrus.Core/Repository/WalrusRepository.cs | 41 ++++++ Walrus.Core/WalrusCommit.cs | 65 ---------- Walrus.Core/WalrusConfig.cs | 19 +-- Walrus.Core/WalrusQuery.cs | 13 +- Walrus.Core/WalrusRepository.cs | 122 ------------------ Walrus.Core/WalrusService.cs | 43 +++--- 17 files changed, 309 insertions(+), 249 deletions(-) create mode 100644 Walrus.Core/IWalrusConfig.cs rename Walrus.Core/{ => Repository}/CommitGroup.cs (100%) create mode 100644 Walrus.Core/Repository/IRepositoryProvider.cs create mode 100644 Walrus.Core/Repository/RepositoryProvider.cs create mode 100644 Walrus.Core/Repository/WalrusCommit.cs create mode 100644 Walrus.Core/Repository/WalrusRepository.cs delete mode 100644 Walrus.Core/WalrusCommit.cs delete mode 100644 Walrus.Core/WalrusRepository.cs diff --git a/Walrus.CLI/Commands/BaseCommand.cs b/Walrus.CLI/Commands/BaseCommand.cs index a0617e2..d423dcf 100644 --- a/Walrus.CLI/Commands/BaseCommand.cs +++ b/Walrus.CLI/Commands/BaseCommand.cs @@ -30,6 +30,6 @@ protected BaseCommand(IWalrusService walrus) public abstract string Description { get; } /// - public Command Command { get; private set; } + public Command Command { get; } } } diff --git a/Walrus.CLI/Commands/QueryCommand.cs b/Walrus.CLI/Commands/QueryCommand.cs index 62eb30b..5fee86e 100644 --- a/Walrus.CLI/Commands/QueryCommand.cs +++ b/Walrus.CLI/Commands/QueryCommand.cs @@ -59,6 +59,7 @@ public QueryCommand(IWalrusService walrus, ILogger logger) : base( Command.Handler = CommandHandler.Create((WalrusQuery query) => { + // Inject config into the query so we can resolve user aliases query.AddConfiguration(walrus.Config); HandleQuery(query); diff --git a/Walrus.CLI/Commands/ShowCommand.cs b/Walrus.CLI/Commands/ShowCommand.cs index b90df7f..72df313 100644 --- a/Walrus.CLI/Commands/ShowCommand.cs +++ b/Walrus.CLI/Commands/ShowCommand.cs @@ -28,17 +28,11 @@ public ShowCommand(IWalrusService walrus, ILogger logger) : base(wa { new Command("config", "Show configuration") { - Handler = CommandHandler.Create(() => - { - ShowConfig(); - }) + Handler = CommandHandler.Create(ShowConfig) }, new Command("repos", "Show known repositories") { - Handler = CommandHandler.Create(() => - { - ShowRepositories(); - }) + Handler = CommandHandler.Create(ShowRepositories) }, }; @@ -73,8 +67,8 @@ private void ShowConfig() private void ShowRepositories() { _logger.LogDebug("ShowRepositories"); - - var repos = Walrus.GetRepositories(); + + var repos = Walrus.GetAllRepositories(null, false); var count = 0; foreach (var repo in repos) { diff --git a/Walrus.CLI/Program.cs b/Walrus.CLI/Program.cs index 1adfe9b..f89524c 100644 --- a/Walrus.CLI/Program.cs +++ b/Walrus.CLI/Program.cs @@ -9,7 +9,6 @@ using System.CommandLine.Parsing; using System.IO; using System.Threading.Tasks; - using Microsoft.Extensions.Logging.Abstractions; using Walrus.Core; class Program @@ -100,8 +99,9 @@ private static ServiceProvider ConfigureServices() }); services.AddSingleton(configuration); - services.AddSingleton(p => p.GetRequiredService().Get() ?? + services.AddSingleton(p => p.GetRequiredService().Get() ?? WalrusConfig.Default); + services.AddTransient(); services.AddTransient(); services.AddCliCommands(); diff --git a/Walrus.Core/IWalrusConfig.cs b/Walrus.Core/IWalrusConfig.cs new file mode 100644 index 0000000..d64ca94 --- /dev/null +++ b/Walrus.Core/IWalrusConfig.cs @@ -0,0 +1,28 @@ +namespace Walrus.Core +{ + using System.Collections.Generic; + + /// + /// Configuration parameters for + /// + public interface IWalrusConfig + { + /// + /// When scanning for repositories, limit search depth to this many + /// directories from each root. + /// + int DirectoryScanDepth { get; set; } + + /// + /// List of repository roots to scan + /// + IList? RepositoryRoots { get; set; } + + /// + /// List of author aliases + /// + /// "some name" : { "email1@example.com", "email2@work.com" } + /// + IDictionary>? AuthorAliases { get; set; } + } +} \ No newline at end of file diff --git a/Walrus.Core/IWalrusService.cs b/Walrus.Core/IWalrusService.cs index 5ff757e..776fca2 100644 --- a/Walrus.Core/IWalrusService.cs +++ b/Walrus.Core/IWalrusService.cs @@ -4,15 +4,15 @@ using System.Collections.Generic; /// - /// Warus service provides access to system Git repositories + /// Walrus service provides access to system Git repositories /// public interface IWalrusService { /// /// Active configuration /// - WalrusConfig Config { get; } - + IWalrusConfig Config { get; } + /// /// Executes the specified query /// @@ -24,9 +24,11 @@ public interface IWalrusService /// /// Returns a list of all repositories that Walrus can see /// - /// Optional directory to search. If null or empty, - /// will be searched. + /// Directory to search for Git repositories. If null or empty + /// will be searched. + /// True to include all branches in commit listing for each repo /// List of repositories - IEnumerable GetRepositories(string? rootDirectory = null); + IEnumerable GetAllRepositories(string? rootDirectory, bool allBranches); + } } \ No newline at end of file diff --git a/Walrus.Core/Internal/Utilities.cs b/Walrus.Core/Internal/Utilities.cs index bef4e18..b4ec4f4 100644 --- a/Walrus.Core/Internal/Utilities.cs +++ b/Walrus.Core/Internal/Utilities.cs @@ -42,7 +42,7 @@ public static IEnumerable EnumerateDirectoriesToDepth(string root, int d /// /// Directories to filter /// List of valid Git repositories - public static IEnumerable GetValidRepositories(IEnumerable directories) + public static IEnumerable GetValidRepositories(IEnumerable directories) { foreach (var directory in directories) { diff --git a/Walrus.Core/CommitGroup.cs b/Walrus.Core/Repository/CommitGroup.cs similarity index 100% rename from Walrus.Core/CommitGroup.cs rename to Walrus.Core/Repository/CommitGroup.cs diff --git a/Walrus.Core/Repository/IRepositoryProvider.cs b/Walrus.Core/Repository/IRepositoryProvider.cs new file mode 100644 index 0000000..ed9e969 --- /dev/null +++ b/Walrus.Core/Repository/IRepositoryProvider.cs @@ -0,0 +1,20 @@ +namespace Walrus.Core +{ + using System; + using System.Collections.Generic; + + /// + /// Provides access to Git repositories + /// + public interface IRepositoryProvider + { + /// + /// Returns a list of all repositories under this directory up to the specified depth + /// + /// Directory to search for Git repositories + /// Recursively scan to this depth. Must be greater than or equal to zero. + /// List of repositories + /// is less than 0 + IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches); + } +} \ No newline at end of file diff --git a/Walrus.Core/Repository/RepositoryProvider.cs b/Walrus.Core/Repository/RepositoryProvider.cs new file mode 100644 index 0000000..ba958a8 --- /dev/null +++ b/Walrus.Core/Repository/RepositoryProvider.cs @@ -0,0 +1,119 @@ +namespace Walrus.Core +{ + using System; + using System.Collections.Generic; + using System.IO; + using Internal; + using LibGit2Sharp; + using Microsoft.Extensions.Logging; + + /// + /// Provides access to Git repositories on the file system + /// + public class RepositoryProvider : IRepositoryProvider + { + /// + public IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches) + { + Ensure.IsValid(nameof(scanDepth), scanDepth >= 0); + + var directories = Utilities.EnumerateDirectoriesToDepth(rootDirectory, scanDepth); + + foreach (var repo in Utilities.GetValidRepositories(directories)) + { + var repoPath = repo.Info.WorkingDirectory; + var commits = GetCommits(repo, allBranches); + + var repository = new WalrusRepository(repoPath, commits); + yield return repository; + } + } + + /// + /// Returns list of all commits in this repo that satisfy the query + /// + /// Repo to get commits from + /// True to include commits from all local branches + /// List of matching commits + private static IEnumerable GetCommits(IRepository repo, bool allBranches) + { + Ensure.IsNotNull(nameof(repo), repo); + + // List of tuple(branch name, commit iterator) + var iterators = new List<(string, IEnumerator)>(32); + + if (allBranches) + { + foreach (var branch in repo.Branches) + { + // Ignore remote branches because + // 1) they are slow + // 2) they might be shallow + if (branch.IsRemote) + { + continue; + } + + iterators.Add((branch.FriendlyName, branch.Commits.GetEnumerator())); + } + } + else + { + iterators.Add((repo.Head.FriendlyName, repo.Commits.GetEnumerator())); + } + + foreach (var commitIter in iterators) + { + foreach (var commit in SafeGitCommitEnumeration(repo, commitIter)) + { + yield return commit; + } + } + } + + + /// + /// LibGit2Sharp Commits iterator seems a bit crashy so we'll manually iterate + /// + /// Repository to get commits from + /// List of branches and their respective commit iterators + /// Commits that satisfy filter + private static IEnumerable SafeGitCommitEnumeration(IRepository repository, + (string, IEnumerator) branchIterator) + { + do + { + var (branchName, iter) = branchIterator; + var repoPath = repository.Info.WorkingDirectory!; + var repoName = Path.GetFileName(Path.GetDirectoryName(repoPath))!; + + try + { + // This may crash with an invalid object exception + if (!iter.MoveNext()) + { + break; + } + } + catch (Exception ex) + { + WalrusLog.Logger.LogError(ex, "Error enumerating commits on {Path}", + repository.Info.WorkingDirectory); + break; + } + + var commit = iter.Current; + yield return new WalrusCommit + { + Branch = branchName, + Message = commit.MessageShort, + Sha = commit.Sha, + Timestamp = commit.Author.When.DateTime, + AuthorEmail = commit.Author.Email, + RepoName = repoName, + RepoPath = repoPath + }; + } while (true); + } + } +} \ No newline at end of file diff --git a/Walrus.Core/Repository/WalrusCommit.cs b/Walrus.Core/Repository/WalrusCommit.cs new file mode 100644 index 0000000..c6d7a25 --- /dev/null +++ b/Walrus.Core/Repository/WalrusCommit.cs @@ -0,0 +1,51 @@ +namespace Walrus.Core +{ + using System; + + /// + /// Wraps commit information from Git + /// + public class WalrusCommit + { + /// + /// Branch this commit belongs to + /// + public string Branch { get; init; } = string.Empty; + + /// + /// Path to repository containing this commit + /// + public string RepoPath { get; init; } = string.Empty; + + /// + /// Name of Git repo this commit belongs to + /// + public string RepoName { get; init; } = string.Empty; + + /// + /// Commit message text + /// + public string Message { get; init; } = string.Empty; + + /// + /// Commit hash + /// + public string Sha { get; init; } = string.Empty; + + /// + /// Email address of commit author + /// + public string AuthorEmail { get; init; } = string.Empty; + + /// + /// Date of commit + /// + public DateTime Timestamp { get; init; } + + /// + public override string ToString() + { + return $"[{RepoName}] {Timestamp} {AuthorEmail} {Sha} {Message}"; + } + } +} \ No newline at end of file diff --git a/Walrus.Core/Repository/WalrusRepository.cs b/Walrus.Core/Repository/WalrusRepository.cs new file mode 100644 index 0000000..e8280c0 --- /dev/null +++ b/Walrus.Core/Repository/WalrusRepository.cs @@ -0,0 +1,41 @@ +namespace Walrus.Core +{ + using System.Collections.Generic; + using System.IO; + using Walrus.Core.Internal; + + /// + /// Git repository + /// + public class WalrusRepository + { + /// + /// Create a new WalrusRepository based on a git repository + /// + /// Path to git repository + /// Repository commits + public WalrusRepository(string repositoryPath, IEnumerable commits) + { + Ensure.IsValid(nameof(repositoryPath), !string.IsNullOrEmpty(repositoryPath)); + + RepositoryPath = Path.GetDirectoryName(repositoryPath)!; + RepositoryName = Path.GetFileName(RepositoryPath)!; + Commits = commits; + } + + /// + /// Name of folder containing Git repo + /// + public string RepositoryName { get; } + + /// + /// Absolute path to Git repo + /// + public string RepositoryPath { get; } + + /// + /// All commits in this repository + /// + public IEnumerable Commits { get; } + } +} diff --git a/Walrus.Core/WalrusCommit.cs b/Walrus.Core/WalrusCommit.cs deleted file mode 100644 index 959c20d..0000000 --- a/Walrus.Core/WalrusCommit.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace Walrus.Core -{ - using LibGit2Sharp; - using System; - using Walrus.Core.Internal; - - /// - /// Wraps commit information from Git - /// - public class WalrusCommit - { - /// - /// Create a new commit - /// - /// Repo that commit belongs to - /// Commit - internal WalrusCommit(WalrusRepository repository, Commit commit) - { - Ensure.IsNotNull(nameof(repository), repository); - Ensure.IsNotNull(nameof(commit), commit); - - RepoPath = repository.RepositoryPath; - RepoName = repository.RepositoryName; - Message = commit.MessageShort; - Sha = commit.Sha; - AuthorEmail = commit.Author.Email; - Timestamp = commit.Author.When.DateTime; - } - /// - /// Path to repository containing this commit5 - /// - public string RepoPath { get; } - - /// - /// Name of Git repo this commit belongs to - /// - public string RepoName { get; } - - /// - /// Commit message text - /// - public string Message { get; } - - /// - /// Commit hash - /// - public string Sha { get; } - - /// - /// Email address of commit author - /// - public string AuthorEmail { get; } - - /// - /// Date of commit - /// - public DateTime Timestamp { get; } - - /// - public override string ToString() - { - return $"[{RepoName}] {Timestamp} {AuthorEmail} {Sha} {Message}"; - } - } -} diff --git a/Walrus.Core/WalrusConfig.cs b/Walrus.Core/WalrusConfig.cs index 27ef822..5dbb4c4 100644 --- a/Walrus.Core/WalrusConfig.cs +++ b/Walrus.Core/WalrusConfig.cs @@ -5,24 +5,15 @@ /// /// Walrus service configuration /// - public sealed class WalrusConfig + public sealed class WalrusConfig : IWalrusConfig { - /// - /// When scanning for repositories, limit search depth to this many - /// directories from each root. - /// + /// public int DirectoryScanDepth { get; set; } - - /// - /// List of repository roots to scan - /// + + /// public IList? RepositoryRoots { get; set; } - /// - /// List of author aliases - /// - /// "some name" : { "email1@example.com", "email2@work.com" } - /// + /// public IDictionary>? AuthorAliases { get; set; } /// diff --git a/Walrus.Core/WalrusQuery.cs b/Walrus.Core/WalrusQuery.cs index 1b10e49..9006078 100644 --- a/Walrus.Core/WalrusQuery.cs +++ b/Walrus.Core/WalrusQuery.cs @@ -1,6 +1,5 @@ namespace Walrus.Core { - using LibGit2Sharp; using System; using System.Collections.Generic; using Walrus.Core.Internal; @@ -62,7 +61,7 @@ public class WalrusQuery /// - the user alias feature. /// /// Service configuration - public void AddConfiguration(WalrusConfig config) + public void AddConfiguration(IWalrusConfig config) { Ensure.IsNotNull(nameof(config), config); @@ -77,21 +76,21 @@ public void AddConfiguration(WalrusConfig config) /// /// Commit to test /// True if commit satisfies this query - public bool IsMatch(Commit commit) + public bool IsMatch(WalrusCommit commit) { var isMatch = true; // Direct email (non-alias) takes precedence over aliases if (!string.IsNullOrEmpty(AuthorEmail)) { - isMatch = commit.Author.Email == AuthorEmail; + isMatch = commit.AuthorEmail == AuthorEmail; } else if (_aliasEmails is not null) { - isMatch = _aliasEmails.Contains(commit.Author.Email); + isMatch = _aliasEmails.Contains(commit.AuthorEmail); } - return isMatch && commit.Author.When >= After && commit.Author.When < Before; + return isMatch && commit.Timestamp >= After && commit.Timestamp < Before; } /// @@ -99,7 +98,7 @@ public bool IsMatch(Commit commit) /// /// Configuration to check /// True if the query should check for email aliases - private bool HasMatchingAliasMap(WalrusConfig config) + private bool HasMatchingAliasMap(IWalrusConfig config) { return !string.IsNullOrEmpty(AuthorAlias) && config.AuthorAliases is not null && diff --git a/Walrus.Core/WalrusRepository.cs b/Walrus.Core/WalrusRepository.cs deleted file mode 100644 index b5a5334..0000000 --- a/Walrus.Core/WalrusRepository.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace Walrus.Core -{ - using LibGit2Sharp; - using Microsoft.Extensions.Logging; - using System; - using System.Collections.Generic; - using System.IO; - using Walrus.Core.Internal; - - /// - /// This wrapper provide safe access to some aspects of LibGit2Sharp.Repository - /// that I found to be a little unstable. - /// - public class WalrusRepository - { - private readonly Repository _repository; - - /// - /// Create a new WalrusRepository based on a git repository - /// - /// Repository to wrap - internal WalrusRepository(Repository repository) - { - Ensure.IsNotNull(nameof(repository), repository); - - _repository = repository; - RepositoryPath = Path.GetDirectoryName(repository.Info?.WorkingDirectory)!; - RepositoryName = Path.GetFileName(RepositoryPath)!; - } - - /// - /// Name of folder containing Git repo - /// - public string RepositoryName { get; } - - /// - /// Absolute path to Git repo - /// - public string RepositoryPath { get; } - - /// - /// Returns list of all commits in this repo that satisfy the query - /// - /// Commit query - /// List of matching commits - public IEnumerable GetCommits(WalrusQuery query) - { - Ensure.IsNotNull(nameof(query), query); - - var iterators = new List>(32); - - if (query.AllBranches) - { - - foreach (var branch in _repository.Branches) - { - // Ignore remote branches because - // 1) they are slow - // 2) they might be shallow - if (branch.IsRemote) - { - continue; - } - - iterators.Add(branch.Commits.GetEnumerator()); - } - } - else - { - iterators.Add(_repository.Commits.GetEnumerator()); - } - - foreach (var commitIter in iterators) - { - - foreach (var commit in SafeGitCommitEnumeration(commitIter, query)) - { - yield return commit; - } - } - - } - - - /// - /// LibGit2Sharp Commits iterator seems a bit crashy so we'll manually iterate - /// - /// Commit iterator - /// Return commits on or after this date - /// Return commit before this date - /// Commits that satisfy filter - private IEnumerable SafeGitCommitEnumeration(IEnumerator commitIter, WalrusQuery query) - { - do - { - try - { - // This may crash with an invalid object exception - if (!commitIter.MoveNext()) - { - break; - } - } - catch (Exception ex) - { - WalrusLog.Logger.LogError(ex, "Error enumerating commits on {Path}", _repository.Info.WorkingDirectory); - break; - } - - var commit = commitIter.Current; - if (query.IsMatch(commit)) - { - yield return new WalrusCommit(this, commit); - } - - } while (true); - - - yield break; - } - } -} diff --git a/Walrus.Core/WalrusService.cs b/Walrus.Core/WalrusService.cs index 3c767ad..78ff0a9 100644 --- a/Walrus.Core/WalrusService.cs +++ b/Walrus.Core/WalrusService.cs @@ -12,57 +12,60 @@ public class WalrusService : IWalrusService { private readonly ILogger _logger; + private readonly IRepositoryProvider _repositoryProvider; /// /// Create a new WalrusService /// /// Logging context + /// Repository provider /// Service configuration - public WalrusService(ILogger logger, WalrusConfig config) + public WalrusService(ILogger logger, IRepositoryProvider repositoryProvider, IWalrusConfig config) { Ensure.IsNotNull(nameof(logger), logger); + Ensure.IsNotNull(nameof(repositoryProvider), repositoryProvider); Ensure.IsNotNull(nameof(config), config); _logger = logger; + _repositoryProvider = repositoryProvider; Config = config; } /// - public WalrusConfig Config { get; } + public IWalrusConfig Config { get; } /// public IEnumerable ExecuteQuery(WalrusQuery query) { Ensure.IsNotNull(nameof(query), query); - + var searchRoot = query.CurrentDirectory ? Environment.CurrentDirectory : null; - + var commits = - GetRepositories(searchRoot) + GetAllRepositories(searchRoot, query.AllBranches) .Where(r => FilterRepo(r, query)) .AsParallel() - .Select(r => r.GetCommits(query)) - .SelectMany(c => c); + .SelectMany(r => r.Commits.Where(query.IsMatch)); // Wrap GroupBy in a CommitGroup so we can have keys of different types var grouped = query.GroupBy switch { WalrusQuery.QueryGrouping.Repo => commits .GroupBy(c => c.RepoName) - .Select(g => new CommitGroup(g.Key, + .Select(g => new CommitGroup(g.Key, g.OrderBy(c => c.Timestamp))), - + WalrusQuery.QueryGrouping.Date => commits .OrderBy(c => c.Timestamp) .GroupBy(c => c.Timestamp.Date) - .Select(g => new CommitGroup(g.Key, + .Select(g => new CommitGroup(g.Key, g.OrderBy(c => c.Timestamp))), - + WalrusQuery.QueryGrouping.Author => commits .GroupBy(c => c.AuthorEmail) .Select(g => new CommitGroup(g.Key, g.OrderBy(c => c.Timestamp))), - + _ => throw new ArgumentOutOfRangeException(nameof(query.GroupBy)) }; @@ -71,11 +74,11 @@ public IEnumerable ExecuteQuery(WalrusQuery query) /// - public IEnumerable GetRepositories(string? rootDirectory = null) + public IEnumerable GetAllRepositories(string? rootDirectory = null, bool allBranches = false) { var searchPaths = string.IsNullOrEmpty(rootDirectory) ? Config.RepositoryRoots : new[] {rootDirectory}; - - if (searchPaths is null) + + if (searchPaths is null) { _logger.LogWarning("No repository search paths were specified"); @@ -84,12 +87,9 @@ public IEnumerable GetRepositories(string? rootDirectory = nul foreach (var root in searchPaths) { - var directories = Utilities.EnumerateDirectoriesToDepth(root, Config.DirectoryScanDepth); - - foreach (var repo in Utilities.GetValidRepositories(directories)) + foreach (var repo in _repositoryProvider.GetRepositories(root, Config.DirectoryScanDepth, allBranches)) { - var repository = new WalrusRepository(repo); - yield return repository; + yield return repo; } } } @@ -102,8 +102,9 @@ public IEnumerable GetRepositories(string? rootDirectory = nul /// True if filter should be included private static bool FilterRepo(WalrusRepository repository, WalrusQuery query) { - return string.IsNullOrEmpty(query.RepoName) || + var b = string.IsNullOrEmpty(query.RepoName) || repository.RepositoryName.Equals(query.RepoName, StringComparison.CurrentCultureIgnoreCase); + return b; } } } \ No newline at end of file From 47d15b4713f76b4f735a5a881ec8aa004bab274b Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Wed, 25 Aug 2021 20:21:18 -0700 Subject: [PATCH 3/5] implement tests now that we can mock core Implement new integration and unit tests now that things are mockable. --- Walrus.Core.Tests/ConstantDateTimes.cs | 18 +++ Walrus.Core.Tests/MockRepoProvider.cs | 51 ++++++ .../WalrusServiceRepositoryQueryTests.cs | 147 ++++++++++++++++++ .../WalrusServiceRepositoryTests.cs | 79 ++++++++++ Walrus.Core.Tests/WalrusServiceTests.cs | 46 ++++-- 5 files changed, 328 insertions(+), 13 deletions(-) create mode 100644 Walrus.Core.Tests/ConstantDateTimes.cs create mode 100644 Walrus.Core.Tests/MockRepoProvider.cs create mode 100644 Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs create mode 100644 Walrus.Core.Tests/WalrusServiceRepositoryTests.cs diff --git a/Walrus.Core.Tests/ConstantDateTimes.cs b/Walrus.Core.Tests/ConstantDateTimes.cs new file mode 100644 index 0000000..664e51f --- /dev/null +++ b/Walrus.Core.Tests/ConstantDateTimes.cs @@ -0,0 +1,18 @@ +namespace Walrus.Core.Tests +{ + using System; + + /// + /// Provides fixed datetime values for test consistency + /// + public static class ConstantDateTimes + { + public static DateTime Today = new DateTime(2021, 8, 25, 19, 35, 00); + + public static DateTime Tomorrow = Today.AddDays(1); + + public static DateTime Yesterday = Today.AddDays(-1); + + public static DateTime LastWeek = Today.AddDays(-7); + } +} \ No newline at end of file diff --git a/Walrus.Core.Tests/MockRepoProvider.cs b/Walrus.Core.Tests/MockRepoProvider.cs new file mode 100644 index 0000000..eebfbb7 --- /dev/null +++ b/Walrus.Core.Tests/MockRepoProvider.cs @@ -0,0 +1,51 @@ +namespace Walrus.Core.Tests +{ + using System.Collections.Generic; + + /// + /// Repo provider for unit tests + /// + public class MockRepoProvider : IRepositoryProvider + { + /// + public IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches) + { + return new[] + { + new WalrusRepository("/home/user/code/project_1", new[] + { + new WalrusCommit + { + Branch = "main", + Message = "[bugfix] fix issue #3133", + Sha = "0ee099f5a57d3149f49baadf61e6ace3eb635314", + Timestamp = ConstantDateTimes.LastWeek, + AuthorEmail = "test@example.com", + RepoName = "project_1", + RepoPath = "/home/user/code/project_1" + }, + new WalrusCommit + { + Branch = "main", + Message = "[bugfix] revert fix for issue #3133", + Sha = "8ea142ee47e99a2641a0e64cbc75cc0e0d373115", + Timestamp = ConstantDateTimes.Yesterday, + AuthorEmail = "test@example.com", + RepoName = "project_1", + RepoPath = "/home/user/code/project_1" + }, + new WalrusCommit + { + Branch = "main", + Message = "[bugfix] really fix issue #3133", + Sha = "2fe245c4374fcd72304333cead412926faa1dd2f", + Timestamp = ConstantDateTimes.Today, + AuthorEmail = "test@example.com", + RepoName = "project_1", + RepoPath = "/home/user/code/project_1" + } + }) + }; + } + } +} \ No newline at end of file diff --git a/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs b/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs new file mode 100644 index 0000000..bf9f086 --- /dev/null +++ b/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs @@ -0,0 +1,147 @@ +namespace Walrus.Core.Tests +{ + using System; + using System.Linq; + using Microsoft.Extensions.Logging.Abstractions; + using Xunit; + + /// + /// Test repository queries + /// + public class WalrusServiceRepositoryQueryTests + { + /// + /// Test that the default query returns expected values + /// + [Fact] + public void QueryDefault() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query); + + // Assert + Assert.NotEmpty(commits); + } + + /// + /// Test that the default query returns expected values when grouped by date + /// + [Fact] + public void QueryGroupByDate() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + GroupBy = WalrusQuery.QueryGrouping.Date + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, cg => Assert.True(cg.Key is DateTime)); + Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); + } + + /// + /// Test that the default query returns expected values when grouped by repo + /// + [Fact] + public void QueryGroupByRepo() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + GroupBy = WalrusQuery.QueryGrouping.Repo + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query).ToList(); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, cg => Assert.True(cg.Key is string)); + Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); + } + + /// + /// Test that the default query returns expected values when grouped by author + /// + [Fact] + public void QueryGroupByAuthor() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + GroupBy = WalrusQuery.QueryGrouping.Author + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query).ToList(); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, cg => Assert.True(cg.Key is string)); + Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); + } + + /// + /// Test that an invalid grouping throws an exception + /// + [Fact] + public void QueryGroupByInvalid() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + GroupBy = (WalrusQuery.QueryGrouping) 42 + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + + // Assert + Assert.Throws(() => service.ExecuteQuery(query)); + } + } +} \ No newline at end of file diff --git a/Walrus.Core.Tests/WalrusServiceRepositoryTests.cs b/Walrus.Core.Tests/WalrusServiceRepositoryTests.cs new file mode 100644 index 0000000..0b33f41 --- /dev/null +++ b/Walrus.Core.Tests/WalrusServiceRepositoryTests.cs @@ -0,0 +1,79 @@ +namespace Walrus.Core.Tests +{ + using System; + using System.Linq; + using Microsoft.Extensions.Logging.Abstractions; + using Xunit; + + public class WalrusServiceRepositoryTests + { + /// + /// Assert that when no search root is specified we get no results + /// + [Fact] + public void GetRepositoriesNoRoots() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var repos = service.GetAllRepositories(null, false).ToList(); + + // Assert + Assert.Empty(repos); + } + + /// + /// Assert that all fetched repositories have their properties set + /// + [Fact] + public void GetRepositories() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var repos = service.GetAllRepositories("test", false).ToList(); + + // Assert + Assert.NotEmpty(repos); + Assert.All(repos, r => Assert.True(!string.IsNullOrEmpty(r.RepositoryName))); + Assert.All(repos, r => Assert.True(!string.IsNullOrEmpty(r.RepositoryPath))); + Assert.All(repos, r => Assert.True(r.Commits.Any())); + } + + /// + /// Assert that all fetched repositories have their properties set + /// + [Fact] + public void GetCommitsRepositories() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var repos = service.GetAllRepositories("test", false).ToList(); + var commits = repos.SelectMany(r => r.Commits).ToList(); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, c => Assert.True(!string.IsNullOrEmpty(c.Branch))); + Assert.All(commits, c => Assert.True(!string.IsNullOrEmpty(c.RepoPath))); + Assert.All(commits, c => Assert.True(!string.IsNullOrEmpty(c.RepoName))); + Assert.All(commits, c => Assert.True(!string.IsNullOrEmpty(c.Message))); + Assert.All(commits, c => Assert.True(!string.IsNullOrEmpty(c.Sha))); + Assert.All(commits, c => Assert.True(!string.IsNullOrEmpty(c.AuthorEmail))); + Assert.All(commits, c => Assert.NotEqual(DateTime.MinValue, c.Timestamp)); + Assert.All(commits, c => Assert.Contains(c.RepoName, c.ToString())); + } + } +} \ No newline at end of file diff --git a/Walrus.Core.Tests/WalrusServiceTests.cs b/Walrus.Core.Tests/WalrusServiceTests.cs index e745442..8f81089 100644 --- a/Walrus.Core.Tests/WalrusServiceTests.cs +++ b/Walrus.Core.Tests/WalrusServiceTests.cs @@ -5,6 +5,9 @@ namespace Walrus.Core.Tests using Microsoft.Extensions.Logging.Abstractions; using Xunit; + /// + /// Test service constructor and properties + /// public class WalrusServiceTests { /// @@ -14,19 +17,19 @@ public class WalrusServiceTests public void LoggerRequired() { // Setup - NullLogger logger = null; + var repoProvider = new MockRepoProvider(); var config = WalrusConfig.Default; // Execute // Assert - Assert.Throws(() => new WalrusService(logger, config)); + Assert.Throws(() => new WalrusService(null, repoProvider, config)); } - + /// /// Assert that constructor parameters are validated /// [Fact] - public void ConfigRequired() + public void RepoProviderRequired() { // Setup var logger = new NullLogger(); @@ -34,10 +37,25 @@ public void ConfigRequired() // Execute // Assert - Assert.Throws(() => new WalrusService(logger, config)); + Assert.Throws(() => new WalrusService(logger, null, config)); + } + + /// + /// Assert that constructor parameters are validated + /// + [Fact] + public void ConfigRequired() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + + // Execute + // Assert + Assert.Throws(() => new WalrusService(logger, repoProvider, null)); } - - + + /// /// Assert that query parameters are validated /// @@ -46,16 +64,17 @@ public void QueryRequired() { // Setup var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); var config = WalrusConfig.Default; // Execute - var service = new WalrusService(logger, config); - + var service = new WalrusService(logger, repoProvider, config); + // Assert Assert.Throws(() => service.ExecuteQuery(null)); } - + /// /// Assert that configuration is captured /// @@ -64,17 +83,18 @@ public void ConfigStored() { // Setup var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); var config = WalrusConfig.Default; config.AuthorAliases = new Dictionary> {{"test", new[] {"hello@example.com"}}}; config.RepositoryRoots = new[] {"/home/user/code", "C:\\code\\foo"}; // Execute - var service = new WalrusService(logger, config); - + var service = new WalrusService(logger, repoProvider, config); + // Assert Assert.Equal(config.DirectoryScanDepth, service.Config.DirectoryScanDepth); Assert.Equal(config.RepositoryRoots, service.Config.RepositoryRoots); Assert.Equal(config.AuthorAliases, service.Config.AuthorAliases); } } -} +} \ No newline at end of file From 3b26d7306555f3b1f7d1dc6f23ff324c35c8a5e9 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Wed, 25 Aug 2021 20:44:45 -0700 Subject: [PATCH 4/5] add more tests Cover more of the complicated filter and query logic --- Walrus.Core.Tests/MockRepoProvider.cs | 33 +++++ .../WalrusServiceRepositoryQueryTests.cs | 136 ++++++++++++++++-- Walrus.Core/Repository/WalrusRepository.cs | 2 +- 3 files changed, 161 insertions(+), 10 deletions(-) diff --git a/Walrus.Core.Tests/MockRepoProvider.cs b/Walrus.Core.Tests/MockRepoProvider.cs index eebfbb7..bf2f43c 100644 --- a/Walrus.Core.Tests/MockRepoProvider.cs +++ b/Walrus.Core.Tests/MockRepoProvider.cs @@ -44,6 +44,39 @@ public IEnumerable GetRepositories(string rootDirectory, int s RepoName = "project_1", RepoPath = "/home/user/code/project_1" } + }), + new WalrusRepository("/home/user/code/project_2", new[] + { + new WalrusCommit + { + Branch = "main", + Message = "[bugfix] fix issue #2252", + Sha = "f47fb2990a0d9596e908a23a25e248b0b10a1f37", + Timestamp = ConstantDateTimes.LastWeek, + AuthorEmail = "test2@example.com", + RepoName = "project_2", + RepoPath = "/home/user/code/project_2" + }, + new WalrusCommit + { + Branch = "main", + Message = "[bugfix] revert fix for issue #2252", + Sha = "515fec7ad07d728044271e7fa6a93a34217613b3", + Timestamp = ConstantDateTimes.Yesterday, + AuthorEmail = "test2@example.com", + RepoName = "project_2", + RepoPath = "/home/user/code/project_2" + }, + new WalrusCommit + { + Branch = "main", + Message = "[bugfix] really fix issue #2252", + Sha = "0335f9a11ec0c5f83c0f412ed49e816f815d9089", + Timestamp = ConstantDateTimes.Today, + AuthorEmail = "test2@example.com", + RepoName = "project_2", + RepoPath = "/home/user/code/project_2" + } }) }; } diff --git a/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs b/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs index bf9f086..a2aa046 100644 --- a/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs +++ b/Walrus.Core.Tests/WalrusServiceRepositoryQueryTests.cs @@ -1,6 +1,7 @@ namespace Walrus.Core.Tests { using System; + using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging.Abstractions; using Xunit; @@ -11,7 +12,7 @@ public class WalrusServiceRepositoryQueryTests { /// - /// Test that the default query returns expected values + /// Test that query returns expected values /// [Fact] public void QueryDefault() @@ -34,9 +35,9 @@ public void QueryDefault() // Assert Assert.NotEmpty(commits); } - + /// - /// Test that the default query returns expected values when grouped by date + /// Test that query returns expected values when grouped by date /// [Fact] public void QueryGroupByDate() @@ -55,16 +56,16 @@ public void QueryGroupByDate() // Execute var service = new WalrusService(logger, repoProvider, config); - var commits = service.ExecuteQuery(query); + var commits = service.ExecuteQuery(query).ToList(); // Assert Assert.NotEmpty(commits); Assert.All(commits, cg => Assert.True(cg.Key is DateTime)); Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); } - + /// - /// Test that the default query returns expected values when grouped by repo + /// Test that query returns expected values when grouped by repo /// [Fact] public void QueryGroupByRepo() @@ -90,9 +91,9 @@ public void QueryGroupByRepo() Assert.All(commits, cg => Assert.True(cg.Key is string)); Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); } - + /// - /// Test that the default query returns expected values when grouped by author + /// Test that query returns expected values when grouped by author /// [Fact] public void QueryGroupByAuthor() @@ -139,9 +140,126 @@ public void QueryGroupByInvalid() // Execute var service = new WalrusService(logger, repoProvider, config); - + // Assert Assert.Throws(() => service.ExecuteQuery(query)); } + + /// + /// Test that querying and filtering by repo name works + /// + [Fact] + public void QueryFilterByRepo() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + GroupBy = WalrusQuery.QueryGrouping.Repo, + RepoName = "project_1" + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query).ToList(); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, cg => Assert.Equal("project_1", cg.Key as string)); + Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); + Assert.All(commits, cg => Assert.All(cg.Data, c => Assert.Equal("project_1", c.RepoName))); + } + + /// + /// Test that querying and filtering by author works + /// + [Fact] + public void QueryFilterByAuthor() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + var query = new WalrusQuery + { + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + GroupBy = WalrusQuery.QueryGrouping.Author, + AuthorEmail = "test@example.com" + }; + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query).ToList(); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, cg => Assert.Equal("test@example.com", cg.Key as string)); + Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); + Assert.All(commits, cg => Assert.All(cg.Data, c => Assert.Equal("test@example.com", c.AuthorEmail))); + } + + /// + /// Test that querying and filtering by author alias works + /// + [Fact] + public void QueryFilterByAuthorAlias() + { + // Setup + var logger = new NullLogger(); + var repoProvider = new MockRepoProvider(); + var config = WalrusConfig.Default; + config.RepositoryRoots = new[] {"test"}; + config.AuthorAliases = new Dictionary> + { + {"test", new[] {"test@examples.com", "test2@example.com"}} + }; + var query = new WalrusQuery + { + AllBranches = true, + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + CurrentDirectory = true, + GroupBy = WalrusQuery.QueryGrouping.Author, + AuthorAlias = "test" + }; + query.AddConfiguration(config); + + // Execute + var service = new WalrusService(logger, repoProvider, config); + var commits = service.ExecuteQuery(query).ToList(); + + // Assert + Assert.NotEmpty(commits); + Assert.All(commits, cg => Assert.True(config.AuthorAliases["test"].Contains(cg.Key as string))); + Assert.All(commits, cg => Assert.NotEmpty(cg.Data)); + } + + /// + /// Test that query has a useful ToString + /// + [Fact] + public void QueryToString() + { + // Setup + var query = new WalrusQuery + { + AllBranches = true, + After = ConstantDateTimes.LastWeek, + Before = ConstantDateTimes.Tomorrow, + CurrentDirectory = true, + GroupBy = WalrusQuery.QueryGrouping.Author, + AuthorAlias = "test" + }; + + // Assert + Assert.Contains("After", query.ToString()); + } } } \ No newline at end of file diff --git a/Walrus.Core/Repository/WalrusRepository.cs b/Walrus.Core/Repository/WalrusRepository.cs index e8280c0..e2bab93 100644 --- a/Walrus.Core/Repository/WalrusRepository.cs +++ b/Walrus.Core/Repository/WalrusRepository.cs @@ -18,7 +18,7 @@ public WalrusRepository(string repositoryPath, IEnumerable commits { Ensure.IsValid(nameof(repositoryPath), !string.IsNullOrEmpty(repositoryPath)); - RepositoryPath = Path.GetDirectoryName(repositoryPath)!; + RepositoryPath = repositoryPath; RepositoryName = Path.GetFileName(RepositoryPath)!; Commits = commits; } From 0436fe7b690174341e2f29f8acb8874af1ca12a6 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Wed, 25 Aug 2021 20:45:31 -0700 Subject: [PATCH 5/5] v0.1.8 release --- Walrus.CLI/Walrus.CLI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Walrus.CLI/Walrus.CLI.csproj b/Walrus.CLI/Walrus.CLI.csproj index bbdfe0c..85ec815 100644 --- a/Walrus.CLI/Walrus.CLI.csproj +++ b/Walrus.CLI/Walrus.CLI.csproj @@ -1,7 +1,7 @@ - 0.1.7 + 0.1.8