1+ using System . Collections . Generic ;
2+ using System . Linq ;
3+ using Microsoft . Extensions . Configuration ;
4+ using Microsoft . Extensions . DependencyInjection ;
5+ using Microsoft . Extensions . Logging ;
6+ using Microsoft . VisualStudio . TestTools . UnitTesting ;
7+ using MigrationTools . Options ;
8+ using MigrationTools . Services ;
9+
10+ namespace MigrationTools . Tests . Options
11+ {
12+ [ TestClass ]
13+ public class OptionsConfigurationUpgraderTfsNodeStructureTests
14+ {
15+ [ TestMethod ]
16+ public void TfsNodeStructureOptions_DictionaryMappingsFormat_ShouldConvertToArrayFormat ( )
17+ {
18+ // Arrange
19+ var oldConfigJson = @"{
20+ ""TfsNodeStructureTool"": {
21+ ""Enabled"": true,
22+ ""Areas"": {
23+ ""Filters"": [],
24+ ""Mappings"": {
25+ ""Foo\\\\AAA\\\\123\\\\(.+)"": ""FooDest\\AAA\\$1"",
26+ ""Foo\\\\(.+)"": ""FooDest\\$1""
27+ }
28+ },
29+ ""Iterations"": {
30+ ""Filters"": [],
31+ ""Mappings"": {
32+ ""Bar\\\\(.+)"": ""BarDest\\$1""
33+ }
34+ }
35+ }
36+ }" ;
37+
38+ var tempFile = System . IO . Path . GetTempFileName ( ) ;
39+ try
40+ {
41+ System . IO . File . WriteAllText ( tempFile , oldConfigJson ) ;
42+
43+ var configuration = new ConfigurationBuilder ( )
44+ . AddJsonFile ( tempFile , optional : false )
45+ . Build ( ) ;
46+
47+ var services = new ServiceCollection ( ) ;
48+ services . AddLogging ( ) ;
49+ services . AddSingleton < ITelemetryLogger , TelemetryLoggerMock > ( ) ;
50+ var serviceProvider = services . BuildServiceProvider ( ) ;
51+
52+ var logger = serviceProvider . GetService < ILogger < OptionsConfigurationBuilder > > ( ) ;
53+ var telemetryLogger = serviceProvider . GetService < ITelemetryLogger > ( ) ;
54+
55+ var upgrader = new OptionsConfigurationUpgrader ( configuration , logger , telemetryLogger , serviceProvider ) ;
56+ var section = configuration . GetSection ( "TfsNodeStructureTool" ) ;
57+
58+ // Act - Test the dictionary detection and configuration parsing
59+ var areasMappingsSection = section . GetSection ( "Areas:Mappings" ) ;
60+ var iterationsMappingsSection = section . GetSection ( "Iterations:Mappings" ) ;
61+
62+ // Assert - Verify the old format is correctly detected
63+ Assert . IsTrue ( areasMappingsSection . Exists ( ) , "Areas.Mappings section should exist" ) ;
64+ Assert . IsTrue ( iterationsMappingsSection . Exists ( ) , "Iterations.Mappings section should exist" ) ;
65+
66+ var areasChildren = areasMappingsSection . GetChildren ( ) . ToList ( ) ;
67+ var iterationsChildren = iterationsMappingsSection . GetChildren ( ) . ToList ( ) ;
68+
69+ Assert . AreEqual ( 2 , areasChildren . Count , "Should have 2 areas mappings" ) ;
70+ Assert . AreEqual ( 1 , iterationsChildren . Count , "Should have 1 iterations mapping" ) ;
71+
72+ // Verify the dictionary format detection logic
73+ var firstAreaChild = areasChildren . FirstOrDefault ( ) ;
74+ Assert . IsNotNull ( firstAreaChild , "Should have first area mapping" ) ;
75+ Assert . IsFalse ( int . TryParse ( firstAreaChild . Key , out _ ) , "Key should not be numeric (dictionary format)" ) ;
76+
77+ // Verify the actual key-value pairs
78+ var areaMapping1 = areasChildren . FirstOrDefault ( c => c . Key . Contains ( "Foo\\ \\ (.+)" ) ) ;
79+ var areaMapping2 = areasChildren . FirstOrDefault ( c => c . Key . Contains ( "Foo\\ \\ AAA\\ \\ 123" ) ) ;
80+ var iterationMapping1 = iterationsChildren . FirstOrDefault ( c => c . Key . Contains ( "Bar\\ \\ (.+)" ) ) ;
81+
82+ Assert . IsNotNull ( areaMapping1 , "Should find the first area mapping" ) ;
83+ Assert . IsNotNull ( areaMapping2 , "Should find the second area mapping" ) ;
84+ Assert . IsNotNull ( iterationMapping1 , "Should find the iteration mapping" ) ;
85+
86+ Assert . AreEqual ( "FooDest\\ $1" , areaMapping1 . Value , "First area mapping value should be correct" ) ;
87+ Assert . AreEqual ( "FooDest\\ AAA\\ $1" , areaMapping2 . Value , "Second area mapping value should be correct" ) ;
88+ Assert . AreEqual ( "BarDest\\ $1" , iterationMapping1 . Value , "Iteration mapping value should be correct" ) ;
89+ }
90+ finally
91+ {
92+ if ( System . IO . File . Exists ( tempFile ) )
93+ {
94+ System . IO . File . Delete ( tempFile ) ;
95+ }
96+ }
97+ }
98+
99+ [ TestMethod ]
100+ public void TfsNodeStructureOptions_ArrayMappingsFormat_ShouldNotBeModified ( )
101+ {
102+ // Arrange - Already in new format
103+ var newConfigJson = @"{
104+ ""TfsNodeStructureTool"": {
105+ ""Enabled"": true,
106+ ""Areas"": {
107+ ""Filters"": [],
108+ ""Mappings"": [
109+ {
110+ ""Match"": ""Foo\\\\(.+)"",
111+ ""Replacement"": ""FooDest\\$1""
112+ }
113+ ]
114+ }
115+ }
116+ }" ;
117+
118+ var tempFile = System . IO . Path . GetTempFileName ( ) ;
119+ try
120+ {
121+ System . IO . File . WriteAllText ( tempFile , newConfigJson ) ;
122+
123+ var configuration = new ConfigurationBuilder ( )
124+ . AddJsonFile ( tempFile , optional : false )
125+ . Build ( ) ;
126+
127+ var section = configuration . GetSection ( "TfsNodeStructureTool" ) ;
128+
129+ // Act & Assert - Verify the new format is correctly detected as array format
130+ var areasMappingsSection = section . GetSection ( "Areas:Mappings" ) ;
131+ Assert . IsTrue ( areasMappingsSection . Exists ( ) , "Areas.Mappings section should exist" ) ;
132+
133+ var areasChildren = areasMappingsSection . GetChildren ( ) . ToList ( ) ;
134+ Assert . AreEqual ( 1 , areasChildren . Count , "Should have 1 areas mapping" ) ;
135+
136+ var firstChild = areasChildren . FirstOrDefault ( ) ;
137+ Assert . IsNotNull ( firstChild , "Should have first mapping" ) ;
138+
139+ // In array format, keys should be numeric indices
140+ Assert . IsTrue ( int . TryParse ( firstChild . Key , out _ ) , "Key should be numeric (array format)" ) ;
141+ }
142+ finally
143+ {
144+ if ( System . IO . File . Exists ( tempFile ) )
145+ {
146+ System . IO . File . Delete ( tempFile ) ;
147+ }
148+ }
149+ }
150+ }
151+ }
0 commit comments