From 007694824469cb416a19951e93dc39527938ea29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:13:44 +0000 Subject: [PATCH 1/2] Initial plan From cd51b5775e19bd58f5789563e3aadda9857071ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:18:52 +0000 Subject: [PATCH 2/2] Enforce case-insensitive replacement in TfsNodeStructureTool and add test Agent-Logs-Url: https://github.com/nkdAgility/azure-devops-migration-tools/sessions/fdf18f43-12a5-4009-a641-35a9a4e7ba08 Co-authored-by: MrHinsh <5205575+MrHinsh@users.noreply.github.com> --- .../Tools/TfsNodeStructureTests.cs | 32 +++++++++++++++++++ .../Tools/TfsNodeStructureTool.cs | 4 +-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/MigrationTools.Clients.TfsObjectModel.Tests/Tools/TfsNodeStructureTests.cs b/src/MigrationTools.Clients.TfsObjectModel.Tests/Tools/TfsNodeStructureTests.cs index 53534ba9c..3f289430e 100644 --- a/src/MigrationTools.Clients.TfsObjectModel.Tests/Tools/TfsNodeStructureTests.cs +++ b/src/MigrationTools.Clients.TfsObjectModel.Tests/Tools/TfsNodeStructureTests.cs @@ -44,6 +44,38 @@ public void GetTfsNodeStructureTool_WithDifferentAreaPath() } + [TestMethod(), TestCategory("L0")] + public void GetNewNodeName_WithCaseDifferenceInSourcePath_AppliesMappingConsistently() + { + var options = new TfsNodeStructureToolOptions(); + options.Enabled = true; + options.Areas = new NodeOptions() + { + Mappings = [ + new() { Match = @"^SourceProject\\PUL", Replacement = "TargetProject\\test\\PUL" } + ] + }; + var nodeStructure = GetTfsNodeStructureTool(options); + + nodeStructure.ApplySettings(new TfsNodeStructureToolSettings + { + SourceProjectName = "SourceProject", + TargetProjectName = "TargetProject", + FoundNodes = new Dictionary + { + { @"TargetProject\Area\test\PUL", true } + } + }); + + // Source path uses different casing than the Match pattern — must still apply replacement + const string sourceNodeName = @"sourceproject\pul"; + const TfsNodeStructureType nodeStructureType = TfsNodeStructureType.Area; + + var newNodeName = nodeStructure.GetNewNodeName(sourceNodeName, nodeStructureType); + + Assert.AreEqual(@"TargetProject\test\PUL", newNodeName); + } + [TestMethod, TestCategory("L0")] public void TestFixAreaPath_WhenNoAreaPathOrIterationPath_DoesntChangeQuery() { diff --git a/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs b/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs index 569278044..6c4f8f43a 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs @@ -114,7 +114,7 @@ public string GetNewNodeName(string sourceNodePath, TfsNodeStructureType nodeStr if (Regex.IsMatch(sourceNodePath, mapper.Match, RegexOptions.IgnoreCase)) { Log.LogDebug("NodeStructureEnricher.GetNewNodeName::Mappers::{key}::Match", mapper.Match); - string replacement = Regex.Replace(sourceNodePath, mapper.Match, mapper.Replacement); + string replacement = Regex.Replace(sourceNodePath, mapper.Match, mapper.Replacement, RegexOptions.IgnoreCase); Log.LogDebug("NodeStructureEnricher.GetNewNodeName::Mappers::{key}::replaceWith({replace})", mapper.Match, replacement); return replacement; } @@ -130,7 +130,7 @@ public string GetNewNodeName(string sourceNodePath, TfsNodeStructureType nodeStr throw new NodePathNotAnchoredException($"This path is not anchored in the source project name: {sourceNodePath}"); } - return Regex.Replace(sourceNodePath, lastResortRule.Key, lastResortRule.Value); + return Regex.Replace(sourceNodePath, lastResortRule.Key, lastResortRule.Value, RegexOptions.IgnoreCase); } private KeyValuePair GetLastResortRemappingRule()