From 09236e826ab1fef37ec6bd62c2a9ed0e930182ec Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 10 Aug 2026 15:50:24 +1000 Subject: [PATCH] tests(git-provider): Add basic registration tests - make ModuleCore internals visible to ModuleTests --- src/ModuleCore/Git/GitManager.cs | 107 +++++++++++------- src/ModuleCore/ModuleCore.csproj | 6 + tests/ModuleTests/Git/AddRegistrationTests.cs | 36 ++++++ .../BasicRepoRegistration_0.verified.txt | 2 + .../BasicRepoRegistration_1.verified.txt | 2 + .../BasicRepoRegistration_2.verified.txt | 2 + .../Git/TestData/AddRegistrationTestData.cs | 18 +++ 7 files changed, 135 insertions(+), 38 deletions(-) create mode 100644 tests/ModuleTests/Git/AddRegistrationTests.cs create mode 100644 tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_0.verified.txt create mode 100644 tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_1.verified.txt create mode 100644 tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_2.verified.txt create mode 100644 tests/ModuleTests/Git/TestData/AddRegistrationTestData.cs diff --git a/src/ModuleCore/Git/GitManager.cs b/src/ModuleCore/Git/GitManager.cs index 7dbfe8a..0d5af37 100644 --- a/src/ModuleCore/Git/GitManager.cs +++ b/src/ModuleCore/Git/GitManager.cs @@ -6,12 +6,18 @@ public class GitManager private static readonly Lazy GitManagerInstance = new(() => new GitManager()); public static GitManager Instance => GitManagerInstance.Value; + /// + /// Always returns a new clean instance of GitManager + /// + internal static GitManager InternalFreshInstance => new(); + /// /// Simply .ToString() /// private static readonly string DirectorySeparator = Path.DirectorySeparatorChar.ToString(); private readonly InternalDirectory _repositories; + private readonly Lock _readWriteLock = new(); private GitManager() { @@ -20,6 +26,7 @@ public class GitManager _repositories = new InternalDirectory() { Name = DirectorySeparator, + InternalPath = DirectorySeparator }; } @@ -29,18 +36,35 @@ public class GitManager /// /// /// - /// - public void RegisterRepo(string absoluteRepositoryLocation, string registrationName) + /// The normalised string the repository was registered against + public string RegisterRepo(string absoluteRepositoryLocation, string registrationName) { - // Regardless of if we get a name or not, the fully qualified version for us - // starts with a - var directorySegmentsFromName = NameToSegments( - string.IsNullOrWhiteSpace(registrationName) + // Depending on the caller, it might be possible that they've scripted automatic repo registration. Because I + // don't really want to account to all the subtle ways that can be parallised, I just naively lock on every + // registration attempt. This method should be quick regardless, and I could use ConcurrentDictionary except + // that means every instance of InternalDirectory would need it and yeah nah fuck that I can just lock at the + // top level + lock (_readWriteLock) + { + var normalisedName = NormaliseNamePath(string.IsNullOrWhiteSpace(registrationName) ? new DirectoryInfo(absoluteRepositoryLocation).Name - : registrationName - ); + : registrationName); - _repositories.Add(absoluteRepositoryLocation, directorySegmentsFromName); + // Regardless of if we get a name or not, the fully qualified version for us + // starts with a / + var directorySegmentsFromName = NameToSegments(normalisedName); + + var added = _repositories.Add(absoluteRepositoryLocation, directorySegmentsFromName); + + // Not sure about this, the Add should throw any exceptions on duplicate/failures but for now I'll leave this + // here + if (added == null) + { + throw new Exception("Failed to register location"); + } + + return normalisedName; + } } /// @@ -50,7 +74,7 @@ public class GitManager /// private Queue NameToSegments(string name) { - var segments = NormaliseNamePath(name).Split(DirectorySeparator); + var segments = name.Split(DirectorySeparator); return segments.Length == 1 ? new Queue([DirectorySeparator, name]) @@ -84,48 +108,55 @@ public class GitManager public Dictionary Children { get; set; } = []; + internal string InternalPath { get; set; } + /// /// If not null, this is the absolute location of a registered git repository /// public string? FullRepositoryPath { get; set; } - internal void Add(string absoluteRepositoryLocation, Queue directorySegmentsFromName) + /// + /// + /// + /// + /// + /// + /// + internal InternalDirectory? Add(string absoluteRepositoryLocation, Queue directorySegmentsFromName) { var topStack = directorySegmentsFromName.Dequeue(); - if (topStack == Name) - { - // We're at the end of the directory segments so we can safely say we're at the end of the tree so - // we add it to the relevant dictionary - if (directorySegmentsFromName.Count == 0) - { - FullRepositoryPath = absoluteRepositoryLocation; - //Children.Add(topStack, directory); - } - else - { - var nextSegment = directorySegmentsFromName.Peek(); - // Attempt to get the next level of the directory. If we don't have a key entry, create one - if (!Children.TryGetValue(nextSegment, out var nextChild)) - { - nextChild = new InternalDirectory() - { - Name = nextSegment, - }; - Children.Add(nextSegment, nextChild); - } - - // add the next - nextChild.Add(absoluteRepositoryLocation, directorySegmentsFromName); - } - } - else + if (topStack != Name) { // logically it shouldn't be possible to have a value on top of the stack that _doesn't_ exist, but // just incase we throw as this should only happen if an Add is attempted on the root and the queue was // not correctly rooted to / throw new Exception($"Directory segment does not seem to exist: {topStack}"); } + + // We're at the end of the directory segments so we can safely say we're at the end of the tree so + // we add it to the relevant dictionary + if (directorySegmentsFromName.Count == 0) + { + FullRepositoryPath = absoluteRepositoryLocation; + return this; + } + + var nextSegment = directorySegmentsFromName.Peek(); + + // Attempt to get the next level of the directory. If we don't have a key entry, create one + if (!Children.TryGetValue(nextSegment, out var nextChild)) + { + nextChild = new InternalDirectory() + { + Name = nextSegment, + InternalPath = Path.Combine(InternalPath, nextSegment) + }; + Children.Add(nextSegment, nextChild); + } + + // add the next + return nextChild.Add(absoluteRepositoryLocation, directorySegmentsFromName); } } } \ No newline at end of file diff --git a/src/ModuleCore/ModuleCore.csproj b/src/ModuleCore/ModuleCore.csproj index 4ef1a45..2f41110 100644 --- a/src/ModuleCore/ModuleCore.csproj +++ b/src/ModuleCore/ModuleCore.csproj @@ -7,4 +7,10 @@ latestmajor + + + <_Parameter1>ModuleTests + + + diff --git a/tests/ModuleTests/Git/AddRegistrationTests.cs b/tests/ModuleTests/Git/AddRegistrationTests.cs new file mode 100644 index 0000000..0597e61 --- /dev/null +++ b/tests/ModuleTests/Git/AddRegistrationTests.cs @@ -0,0 +1,36 @@ +using System.Text; +using ModuleCore.Calendar; +using ModuleCore.Git; +using ModuleTests.Git.TestData; + +namespace ModuleTests.Git; + +public class AddRegistrationTests +{ + private static readonly VerifySettings Settings; + + static AddRegistrationTests() + { + Settings = new VerifySettings(); + var testBaseDirectory = Path.Join(TestConstants.SnapshotFolderName, nameof(AddRegistrationTests)); + + Settings.UseDirectory(testBaseDirectory); + Settings.DisableDiff(); + } + + [Theory] + [ClassData(typeof(AddRegistrationTestData))] + public Task BasicRepoRegistration((int testId, string path) testData) + { + Settings.UseFileName($"{nameof(BasicRepoRegistration)}_{testData.testId}"); + + var gitManager = GitManager.InternalFreshInstance; + + var repoRegistration = gitManager.RegisterRepo("Test:/some/test/repo", testData.path); + var sb = new StringBuilder(); + sb.AppendLine($"Attempted to register: {testData.path}") + .AppendLine($"Registration result: {repoRegistration}"); + + return Verify(sb, Settings); + } +} \ No newline at end of file diff --git a/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_0.verified.txt b/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_0.verified.txt new file mode 100644 index 0000000..3183455 --- /dev/null +++ b/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_0.verified.txt @@ -0,0 +1,2 @@ +Attempted to register: test +Registration result: test diff --git a/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_1.verified.txt b/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_1.verified.txt new file mode 100644 index 0000000..e9b945d --- /dev/null +++ b/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_1.verified.txt @@ -0,0 +1,2 @@ +Attempted to register: test\path +Registration result: test\path diff --git a/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_2.verified.txt b/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_2.verified.txt new file mode 100644 index 0000000..3c0b092 --- /dev/null +++ b/tests/ModuleTests/Git/Snapshots/AddRegistrationTests/BasicRepoRegistration_2.verified.txt @@ -0,0 +1,2 @@ +Attempted to register: other/path +Registration result: other\path diff --git a/tests/ModuleTests/Git/TestData/AddRegistrationTestData.cs b/tests/ModuleTests/Git/TestData/AddRegistrationTestData.cs new file mode 100644 index 0000000..3668ba2 --- /dev/null +++ b/tests/ModuleTests/Git/TestData/AddRegistrationTestData.cs @@ -0,0 +1,18 @@ +using System.Linq; + +namespace ModuleTests.Git.TestData; + +public class AddRegistrationTestData : TestDataEnumerator<(int testId, string path)> +{ + public AddRegistrationTestData() + { + Data = new List() + { + "test", + $"test{Path.DirectorySeparatorChar}path", + $"other{Path.AltDirectorySeparatorChar}path" + } + .Select((x, i) => (i, x)) + .ToList(); + } +} \ No newline at end of file