diff --git a/src/MigrationTools.Clients.AzureDevops.ObjectModel.Tests/ProcessorEnrichers/TfsNodeStructureTests.cs b/src/MigrationTools.Clients.AzureDevops.ObjectModel.Tests/ProcessorEnrichers/TfsNodeStructureTests.cs index 2c8510150..8ef065246 100644 --- a/src/MigrationTools.Clients.AzureDevops.ObjectModel.Tests/ProcessorEnrichers/TfsNodeStructureTests.cs +++ b/src/MigrationTools.Clients.AzureDevops.ObjectModel.Tests/ProcessorEnrichers/TfsNodeStructureTests.cs @@ -11,11 +11,30 @@ namespace MigrationTools.ProcessorEnrichers.Tests public class TfsNodeStructureTests { private ServiceProvider _services; + private TfsNodeStructure _structure; [TestInitialize] public void Setup() { _services = ServiceProviderHelper.GetServices(); + _structure = _services.GetRequiredService(); + _structure.ApplySettings(new TfsNodeStructureSettings + { + FoundNodes = new Dictionary(), + SourceProjectName = "SourceServer", + TargetProjectName = "TargetServer", + }); + _structure.Configure(new TfsNodeStructureOptions + { + AreaMaps = new Dictionary + { + { "SourceServer", "TargetServer" } + }, + IterationMaps = new Dictionary + { + { "SourceServer", "TargetServer" } + }, + }); } [TestMethod(), TestCategory("L0"), TestCategory("AzureDevOps.ObjectModel")] @@ -46,5 +65,168 @@ public void GetTfsNodeStructure_WithDifferentAreaPath() Assert.AreEqual(newNodeName, @"TargetProject\test\PUL"); } + + [TestMethod, TestCategory("L0")] + public void TestFixAreaPath_WhenNoAreaPathOrIterationPath_DoesntChangeQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(WIQLQueryBit, targetWIQLQueryBit); + } + + + [TestMethod, TestCategory("L0")] + public void TestFixAreaPath_WhenAreaPathInQuery_ChangesQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'TargetServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L1")] + public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesEnabled_ChangesQuery() + { + var nodeStructure = _services.GetRequiredService(); + + // For this test we use the prefixing of the project node and no remapping rule + + + nodeStructure.Configure(new TfsNodeStructureOptions + { + AreaMaps = new Dictionary() + { + { "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" } + }, + IterationMaps = new Dictionary(){ + { "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" } + }, + }); + + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'TargetServer\SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQuery = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQuery); + } + + [TestMethod, TestCategory("L1")] + public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesDisabled_SupportsWhitespaces() + { + var nodeStructure = _services.GetRequiredService(); + + nodeStructure.ApplySettings(new TfsNodeStructureSettings + { + FoundNodes = new Dictionary(), + SourceProjectName = "Source Project", + TargetProjectName = "Target Project", + }); + + // For this test we use no remapping rule + nodeStructure.Configure(new TfsNodeStructureOptions + { + AreaMaps = new Dictionary(), + IterationMaps = new Dictionary(), + }); + + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Source Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Target Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "Source Project", "Target Project", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L1")] + public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesEnabled_SupportsWhitespaces() + { + var nodeStructure = _services.GetRequiredService(); + + nodeStructure.ApplySettings(new TfsNodeStructureSettings + { + FoundNodes = new Dictionary(), + SourceProjectName = "Source Project", + TargetProjectName = "Target Project", + }); + + // For this test we use the prefixing of the project node and no remapping rules + nodeStructure.Configure(new TfsNodeStructureOptions + { + AreaMaps = new Dictionary() + { + { "^Source Project\\\\(.*)" , "Target Project\\Source Project\\$1" } + }, + IterationMaps = new Dictionary(){ + { "^Source Project\\\\(.*)" , "Target Project\\Source Project\\$1" } + }, + }); + + var WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Source Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + var expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Target Project\Source Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + var targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "Source Project", "Target Project", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L0")] + public void TestFixAreaPath_WhenMultipleAreaPathInQuery_ChangesQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' OR [System.AreaPath] = 'SourceServer\Area\Path2' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'TargetServer\Area\Path1' OR [System.AreaPath] = 'TargetServer\Area\Path2' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L0")] + public void TestFixAreaPath_WhenAreaPathAtEndOfQuery_ChangesQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') AND [System.AreaPath] = 'SourceServer\Area\Path1'"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') AND [System.AreaPath] = 'TargetServer\Area\Path1'"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L0")] + public void TestFixIterationPath_WhenInQuery_ChangesQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.IterationPath] = 'SourceServer\Iteration\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.IterationPath] = 'TargetServer\Iteration\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L0")] + public void TestFixAreaPathAndIteration_WhenMultipleOccuranceInQuery_ChangesQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'SourceServer\Area\Path1' OR [System.AreaPath] = 'SourceServer\Area\Path2') AND ([System.IterationPath] = 'SourceServer\Iteration\Path1' OR [System.IterationPath] = 'SourceServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'TargetServer\Area\Path1' OR [System.AreaPath] = 'TargetServer\Area\Path2') AND ([System.IterationPath] = 'TargetServer\Iteration\Path1' OR [System.IterationPath] = 'TargetServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } + + [TestMethod, TestCategory("L0")] + public void TestFixAreaPathAndIteration_WhenMultipleOccuranceWithMixtureOrEqualAndUnderOperatorsInQuery_ChangesQuery() + { + string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'SourceServer\Area\Path1' OR [System.AreaPath] UNDER 'SourceServer\Area\Path2') AND ([System.IterationPath] UNDER 'SourceServer\Iteration\Path1' OR [System.IterationPath] = 'SourceServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'TargetServer\Area\Path1' OR [System.AreaPath] UNDER 'TargetServer\Area\Path2') AND ([System.IterationPath] UNDER 'TargetServer\Iteration\Path1' OR [System.IterationPath] = 'TargetServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; + + string targetWIQLQueryBit = _structure.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); + + Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + } } } \ No newline at end of file diff --git a/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsNodeStructure.cs b/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsNodeStructure.cs index 6794b225c..134275d1a 100644 --- a/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsNodeStructure.cs +++ b/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsNodeStructure.cs @@ -692,5 +692,56 @@ public string GetMappingForMissingItem(NodeStructureItem missingItem) return null; } + private const string RegexPatternForAreaAndIterationPathsFix = "\\[?(?System.AreaPath|System.IterationPath)+\\]?[^']*'(?[^']*(?:''.[^']*)*)'"; + + public string FixAreaPathAndIterationPathForTargetQuery(string sourceWIQLQuery, string sourceProject, string targetProject, ILogger? contextLog) + { + + string targetWIQLQuery = sourceWIQLQuery; + + if (string.IsNullOrWhiteSpace(targetWIQLQuery)) + { + return targetWIQLQuery; + } + + var matches = Regex.Matches(targetWIQLQuery, RegexPatternForAreaAndIterationPathsFix); + + + if (string.IsNullOrWhiteSpace(sourceProject) + || string.IsNullOrWhiteSpace(targetProject) + || sourceProject == targetProject) + { + return targetWIQLQuery; + } + + foreach (Match match in matches) + { + var value = match.Groups["value"].Value; + if (string.IsNullOrWhiteSpace(value) || !value.StartsWith(sourceProject)) + continue; + + var fieldType = match.Groups["key"].Value; + TfsNodeStructureType structureType; + switch (fieldType) + { + case "System.AreaPath": + structureType = TfsNodeStructureType.Area; + break; + case "System.IterationPath": + structureType = TfsNodeStructureType.Iteration; + break; + default: + throw new InvalidOperationException($"Field type {fieldType} is not supported for query remapping."); + } + + var remappedPath = GetNewNodeName(value, structureType); + targetWIQLQuery = targetWIQLQuery.Replace(value, remappedPath); + } + + contextLog?.Information("[FilterWorkItemsThatAlreadyExistInTarget] is enabled. Source project {sourceProject} is replaced with target project {targetProject} on the WIQLQueryBit which resulted into this target WIQLQueryBit \"{targetWIQLQueryBit}\" .", sourceProject, targetProject, targetWIQLQuery); + + return targetWIQLQuery; + } + } } diff --git a/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsUserMappingEnricher.cs b/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsUserMappingEnricher.cs index 3cab3a638..21442ee58 100644 --- a/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsUserMappingEnricher.cs +++ b/src/MigrationTools.Clients.AzureDevops.ObjectModel/ProcessorEnrichers/TfsUserMappingEnricher.cs @@ -25,13 +25,32 @@ public class TfsUserMappingEnricher : WorkItemProcessorEnricher IGroupSecurityService _gssSourse; private IGroupSecurityService _gssTarget; + private IGroupSecurityService GssSource + { get { + if (_gssSourse == null) + { + _gssSourse = Engine.Source.GetService(); + } + return _gssSourse; + } } + + private IGroupSecurityService GssTarget + { + get + { + if (_gssTarget == null) + { + _gssTarget = Engine.Target.GetService(); + } + return _gssTarget; + } + } + public TfsUserMappingEnricherOptions Options { get; private set; } public TfsUserMappingEnricher(IServiceProvider services, ILogger logger) : base(services, logger) { Engine = services.GetRequiredService(); - _gssSourse = Engine.Source.GetService(); - _gssTarget = Engine.Target.GetService(); } public override void Configure(IProcessorEnricherOptions options) @@ -154,9 +173,9 @@ public List GetUsersInSourceMappedToTarget() Log.LogDebug("TfsUserMappingEnricher::GetUsersInSourceMappedToTarget"); if (Options.Enabled) { - var sourceUsers = GetUsersListFromServer(_gssSourse); + var sourceUsers = GetUsersListFromServer(GssSource); Log.LogDebug($"TfsUserMappingEnricher::GetUsersInSourceMappedToTarget [SourceUsersCount|{sourceUsers.Count}]"); - var targetUsers = GetUsersListFromServer(_gssTarget); + var targetUsers = GetUsersListFromServer(GssTarget); Log.LogDebug($"TfsUserMappingEnricher::GetUsersInSourceMappedToTarget [targetUsersCount|{targetUsers.Count}]"); return sourceUsers.Select(sUser => new IdentityMapData { Source = sUser, target = targetUsers.SingleOrDefault(tUser => tUser.FriendlyName == sUser.FriendlyName) }).ToList(); } diff --git a/src/VstsSyncMigrator.Core.Tests/WorkItemMigrationTests.cs b/src/VstsSyncMigrator.Core.Tests/WorkItemMigrationTests.cs index 5d4cff41e..02f95049f 100644 --- a/src/VstsSyncMigrator.Core.Tests/WorkItemMigrationTests.cs +++ b/src/VstsSyncMigrator.Core.Tests/WorkItemMigrationTests.cs @@ -18,217 +18,13 @@ namespace VstsSyncMigrator.Core.Tests public class WorkItemMigrationTests { private ServiceProvider _services; - private WorkItemMigrationContext _underTest; [TestInitialize] public void Setup() { - _services = ServiceProviderHelper.GetServices(); - var nodeStructure = _services.GetRequiredService(); - nodeStructure.ApplySettings(new TfsNodeStructureSettings - { - FoundNodes = new Dictionary(), - SourceProjectName = "SourceServer", - TargetProjectName = "TargetServer", - }); - nodeStructure.Configure(new TfsNodeStructureOptions - { - AreaMaps = new Dictionary - { - { "SourceServer", "TargetServer" } - }, - IterationMaps = new Dictionary - { - { "SourceServer", "TargetServer" } - }, - }); - - //IMigrationEngine xx = _services.GetRequiredService(); - // IOptions < EngineConfiguration> engine = _services.GetRequiredService>(); - - - _underTest = new WorkItemMigrationContext(_services.GetRequiredService(), - _services, - _services.GetRequiredService(), - _services.GetRequiredService>(), - _services.GetRequiredService(), - _services.GetRequiredService< TfsAttachmentEnricher>(), - nodeStructure, - _services.GetRequiredService(), - _services.GetRequiredService(), - _services.GetRequiredService(), - _services.GetRequiredService(), - _services.GetRequiredService(), - _services.GetRequiredService>()); - - _underTest.Configure(new WorkItemMigrationConfig - { - - }); - - } - - [TestMethod, TestCategory("L0")] - public void TestFixAreaPath_WhenNoAreaPathOrIterationPath_DoesntChangeQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(WIQLQueryBit, targetWIQLQueryBit); - } - - - [TestMethod, TestCategory("L0")] - public void TestFixAreaPath_WhenAreaPathInQuery_ChangesQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'TargetServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } - - [TestMethod, TestCategory("L1")] - public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesEnabled_ChangesQuery() - { - var nodeStructure = _services.GetRequiredService(); - - // For this test we use the prefixing of the project node and no remapping rule - - - nodeStructure.Configure(new TfsNodeStructureOptions - { - AreaMaps = new Dictionary( ) - { - { "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" } - }, - IterationMaps = new Dictionary(){ - { "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" } - }, - }); - - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'TargetServer\SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQuery = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQuery); - } - - [TestMethod, TestCategory("L1")] - public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesDisabled_SupportsWhitespaces() - { - var nodeStructure = _services.GetRequiredService(); - - nodeStructure.ApplySettings(new TfsNodeStructureSettings - { - FoundNodes = new Dictionary(), - SourceProjectName = "Source Project", - TargetProjectName = "Target Project", - }); - - // For this test we use no remapping rule - nodeStructure.Configure(new TfsNodeStructureOptions - { - AreaMaps = new Dictionary(), - IterationMaps = new Dictionary(), - }); - - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Source Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Target Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "Source Project", "Target Project", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); + _services = ServiceProviderHelper.GetServices(); } - [TestMethod, TestCategory("L1")] - public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesEnabled_SupportsWhitespaces() - { - var nodeStructure = _services.GetRequiredService(); - - nodeStructure.ApplySettings(new TfsNodeStructureSettings - { - FoundNodes = new Dictionary(), - SourceProjectName = "Source Project", - TargetProjectName = "Target Project", - }); - - // For this test we use the prefixing of the project node and no remapping rules - nodeStructure.Configure(new TfsNodeStructureOptions - { - AreaMaps = new Dictionary() - { - { "^Source Project\\\\(.*)" , "Target Project\\Source Project\\$1" } - }, - IterationMaps = new Dictionary(){ - { "^Source Project\\\\(.*)" , "Target Project\\Source Project\\$1" } - }, - }); - - var WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Source Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - var expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'Target Project\Source Project\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - var targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "Source Project", "Target Project", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } - - [TestMethod, TestCategory("L0")] - public void TestFixAreaPath_WhenMultipleAreaPathInQuery_ChangesQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' OR [System.AreaPath] = 'SourceServer\Area\Path2' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'TargetServer\Area\Path1' OR [System.AreaPath] = 'TargetServer\Area\Path2' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } - - [TestMethod, TestCategory("L0")] - public void TestFixAreaPath_WhenAreaPathAtEndOfQuery_ChangesQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') AND [System.AreaPath] = 'SourceServer\Area\Path1'"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') AND [System.AreaPath] = 'TargetServer\Area\Path1'"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } - - [TestMethod, TestCategory("L0")] - public void TestFixIterationPath_WhenInQuery_ChangesQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.IterationPath] = 'SourceServer\Iteration\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.IterationPath] = 'TargetServer\Iteration\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } - - [TestMethod, TestCategory("L0")] - public void TestFixAreaPathAndIteration_WhenMultipleOccuranceInQuery_ChangesQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'SourceServer\Area\Path1' OR [System.AreaPath] = 'SourceServer\Area\Path2') AND ([System.IterationPath] = 'SourceServer\Iteration\Path1' OR [System.IterationPath] = 'SourceServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'TargetServer\Area\Path1' OR [System.AreaPath] = 'TargetServer\Area\Path2') AND ([System.IterationPath] = 'TargetServer\Iteration\Path1' OR [System.IterationPath] = 'TargetServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } - - [TestMethod, TestCategory("L0")] - public void TestFixAreaPathAndIteration_WhenMultipleOccuranceWithMixtureOrEqualAndUnderOperatorsInQuery_ChangesQuery() - { - string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'SourceServer\Area\Path1' OR [System.AreaPath] UNDER 'SourceServer\Area\Path2') AND ([System.IterationPath] UNDER 'SourceServer\Iteration\Path1' OR [System.IterationPath] = 'SourceServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - string expectTargetQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND ([System.AreaPath] = 'TargetServer\Area\Path1' OR [System.AreaPath] UNDER 'TargetServer\Area\Path2') AND ([System.IterationPath] UNDER 'TargetServer\Iteration\Path1' OR [System.IterationPath] = 'TargetServer\Iteration\Path2') AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')"; - - string targetWIQLQueryBit = _underTest.FixAreaPathAndIterationPathForTargetQuery(WIQLQueryBit, "SourceServer", "TargetServer", null); - - Assert.AreEqual(expectTargetQueryBit, targetWIQLQueryBit); - } + } } \ No newline at end of file diff --git a/src/VstsSyncMigrator.Core/Execution/MigrationContext/WorkItemMigrationContext.cs b/src/VstsSyncMigrator.Core/Execution/MigrationContext/WorkItemMigrationContext.cs index 5b2bf667c..10c68e5a7 100644 --- a/src/VstsSyncMigrator.Core/Execution/MigrationContext/WorkItemMigrationContext.cs +++ b/src/VstsSyncMigrator.Core/Execution/MigrationContext/WorkItemMigrationContext.cs @@ -49,7 +49,6 @@ namespace VstsSyncMigrator.Engine /// Work Items public class WorkItemMigrationContext : MigrationProcessorBase { - private const string RegexPatternForAreaAndIterationPathsFix = "\\[?(?System.AreaPath|System.IterationPath)+\\]?[^']*'(?[^']*(?:''.[^']*)*)'"; private static int _count = 0; private static int _current = 0; @@ -191,7 +190,7 @@ protected override void InternalExecute() "[FilterWorkItemsThatAlreadyExistInTarget] is enabled. Searching for work items that have already been migrated to the target...", sourceWorkItems.Count()); - string targetWIQLQuery = FixAreaPathAndIterationPathForTargetQuery(_config.WIQLQuery, + string targetWIQLQuery = _nodeStructureEnricher.FixAreaPathAndIterationPathForTargetQuery(_config.WIQLQuery, Engine.Source.WorkItems.Project.Name, Engine.Target.WorkItems.Project.Name, contextLog); sourceWorkItems = ((TfsWorkItemMigrationClient)Engine.Target.WorkItems).FilterExistingWorkItems( @@ -363,55 +362,6 @@ private void ValidatePatTokenRequirement() } } - internal string FixAreaPathAndIterationPathForTargetQuery(string sourceWIQLQuery, string sourceProject, string targetProject, ILogger? contextLog) - { - - string targetWIQLQuery = sourceWIQLQuery; - - if (string.IsNullOrWhiteSpace(targetWIQLQuery)) - { - return targetWIQLQuery; - } - - var matches = Regex.Matches(targetWIQLQuery, RegexPatternForAreaAndIterationPathsFix); - - - if (string.IsNullOrWhiteSpace(sourceProject) - || string.IsNullOrWhiteSpace(targetProject) - || sourceProject == targetProject) - { - return targetWIQLQuery; - } - - foreach (Match match in matches) - { - var value = match.Groups["value"].Value; - if (string.IsNullOrWhiteSpace(value) || !value.StartsWith(sourceProject)) - continue; - - var fieldType = match.Groups["key"].Value; - TfsNodeStructureType structureType; - switch (fieldType) - { - case "System.AreaPath": - structureType = TfsNodeStructureType.Area; - break; - case "System.IterationPath": - structureType = TfsNodeStructureType.Iteration; - break; - default: - throw new InvalidOperationException($"Field type {fieldType} is not supported for query remapping."); - } - - var remappedPath = _nodeStructureEnricher.GetNewNodeName(value, structureType); - targetWIQLQuery = targetWIQLQuery.Replace(value, remappedPath); - } - - contextLog?.Information("[FilterWorkItemsThatAlreadyExistInTarget] is enabled. Source project {sourceProject} is replaced with target project {targetProject} on the WIQLQueryBit which resulted into this target WIQLQueryBit \"{targetWIQLQueryBit}\" .", sourceProject, targetProject, targetWIQLQuery); - - return targetWIQLQuery; - } - private static bool IsNumeric(string val, NumberStyles numberStyle) { double result;