1+ using Xunit ;
2+ using XESmartTarget . Core . Utils ;
3+ using System . Collections . Generic ;
4+ using System . Threading . Tasks ;
5+ using System . Linq ;
6+
7+ namespace XESmartTarget . Tests . Utils
8+ {
9+ public class SmartFormatHelperTests
10+ {
11+ [ Fact ]
12+ public void Format_WithStringDictionary_ReturnsFormattedString ( )
13+ {
14+ // Arrange
15+ var format = "Hello {name}, welcome to {place}!" ;
16+ var args = new Dictionary < string , string >
17+ {
18+ [ "name" ] = "Alice" ,
19+ [ "place" ] = "Wonderland"
20+ } ;
21+
22+ // Act
23+ var result = SmartFormatHelper . Format ( format , args ) ;
24+
25+ // Assert
26+ Xunit . Assert . Equal ( "Hello Alice, welcome to Wonderland!" , result ) ;
27+ }
28+
29+ [ Fact ]
30+ public void Format_WithObjectDictionary_ReturnsFormattedString ( )
31+ {
32+ // Arrange
33+ var format = "Count: {count}, Price: {price}" ;
34+ var args = new Dictionary < string , object >
35+ {
36+ [ "count" ] = 42 ,
37+ [ "price" ] = 99.99
38+ } ;
39+
40+ // Act
41+ var result = SmartFormatHelper . Format ( format , args ) ;
42+
43+ // Assert
44+ Xunit . Assert . Equal ( "Count: 42, Price: 99.99" , result ) ;
45+ }
46+
47+ [ Fact ]
48+ public void Format_WithMissingPlaceholder_ReturnsOriginalFormat ( )
49+ {
50+ // Arrange
51+ var format = "Hello {name}" ;
52+ var args = new Dictionary < string , string >
53+ {
54+ [ "other" ] = "value"
55+ } ;
56+
57+ // Act
58+ var result = SmartFormatHelper . Format ( format , args ) ;
59+
60+ // Assert
61+ Xunit . Assert . Equal ( format , result ) ;
62+ }
63+
64+ [ Fact ]
65+ public void Format_WithEmptyDictionary_HandlesGracefully ( )
66+ {
67+ // Arrange
68+ var format = "No placeholders here" ;
69+ var args = new Dictionary < string , string > ( ) ;
70+
71+ // Act
72+ var result = SmartFormatHelper . Format ( format , args ) ;
73+
74+ // Assert
75+ Xunit . Assert . Equal ( "No placeholders here" , result ) ;
76+ }
77+
78+ [ Fact ]
79+ public void Format_WithNumberFormatting_WorksCorrectly ( )
80+ {
81+ // Arrange
82+ var format = "{count:0000}" ;
83+ var args = new Dictionary < string , object >
84+ {
85+ [ "count" ] = 42
86+ } ;
87+
88+ // Act
89+ var result = SmartFormatHelper . Format ( format , args ) ;
90+
91+ // Assert
92+ Xunit . Assert . Contains ( "0042" , result ) ;
93+ }
94+
95+ [ Fact ]
96+ public void Format_WithSpecialCharacters_HandlesCorrectly ( )
97+ {
98+ // Arrange
99+ var format = "Value: {value}" ;
100+ var args = new Dictionary < string , string >
101+ {
102+ [ "value" ] = "Test\\ Value"
103+ } ;
104+
105+ // Act
106+ var result = SmartFormatHelper . Format ( format , args ) ;
107+
108+ // Assert
109+ Xunit . Assert . Equal ( "Value: Test\\ Value" , result ) ;
110+ }
111+
112+ [ Fact ]
113+ public void Format_WithNullValue_HandlesGracefully ( )
114+ {
115+ // Arrange
116+ var format = "Value: {value}" ;
117+ var args = new Dictionary < string , object >
118+ {
119+ [ "value" ] = null !
120+ } ;
121+
122+ // Act
123+ var result = SmartFormatHelper . Format ( format , args ) ;
124+
125+ // Assert
126+ Xunit . Assert . NotNull ( result ) ;
127+ }
128+
129+ [ Fact ]
130+ public async Task Format_ConcurrentCalls_ProduceCorrectResults ( )
131+ {
132+ // Arrange
133+ var format = "Thread: {thread}, Value: {value}" ;
134+ var tasks = new List < Task < ( int thread , string result ) > > ( ) ;
135+
136+ // Act - simulate concurrent formatting from multiple threads
137+ for ( int i = 0 ; i < 100 ; i ++ )
138+ {
139+ int threadId = i ;
140+ var task = Task . Run ( ( ) =>
141+ {
142+ var args = new Dictionary < string , object >
143+ {
144+ [ "thread" ] = threadId ,
145+ [ "value" ] = $ "Value_{ threadId } "
146+ } ;
147+ var result = SmartFormatHelper . Format ( format , args ) ;
148+ return ( threadId , result ) ;
149+ } ) ;
150+ tasks . Add ( task ) ;
151+ }
152+
153+ var results = await Task . WhenAll ( tasks ) ;
154+
155+ // Assert - verify each result is exactly what we expect
156+ foreach ( var ( threadId , result ) in results )
157+ {
158+ var expectedResult = $ "Thread: { threadId } , Value: Value_{ threadId } ";
159+ Xunit . Assert . Equal ( expectedResult , result ) ;
160+ }
161+ }
162+
163+ [ Fact ]
164+ public void Format_SequentialCallsSameThread_NoStateLeakage ( )
165+ {
166+ // Arrange & Act
167+ var result1 = SmartFormatHelper . Format ( "Hello {name}" , new Dictionary < string , string > { [ "name" ] = "Alice" } ) ;
168+ var result2 = SmartFormatHelper . Format ( "Goodbye {name}" , new Dictionary < string , string > { [ "name" ] = "Bob" } ) ;
169+ var result3 = SmartFormatHelper . Format ( "Hi {name}" , new Dictionary < string , string > { [ "name" ] = "Charlie" } ) ;
170+
171+ // Assert - each call should be completely independent
172+ Xunit . Assert . Equal ( "Hello Alice" , result1 ) ;
173+ Xunit . Assert . Equal ( "Goodbye Bob" , result2 ) ;
174+ Xunit . Assert . Equal ( "Hi Charlie" , result3 ) ;
175+
176+ // Verify no cross-contamination
177+ Xunit . Assert . DoesNotContain ( "Bob" , result1 ) ;
178+ Xunit . Assert . DoesNotContain ( "Alice" , result2 ) ;
179+ Xunit . Assert . DoesNotContain ( "Charlie" , result1 ) ;
180+ }
181+
182+ [ Fact ]
183+ public void Format_RapidSequentialCalls_NoMemoryLeak ( )
184+ {
185+ // Arrange & Act - call many times to check for memory issues
186+ for ( int i = 0 ; i < 10000 ; i ++ )
187+ {
188+ var args = new Dictionary < string , object >
189+ {
190+ [ "iteration" ] = i ,
191+ [ "value" ] = $ "Value_{ i } "
192+ } ;
193+ var result = SmartFormatHelper . Format ( "Iteration: {iteration}, {value}" , args ) ;
194+
195+ // Assert each result is correct
196+ Xunit . Assert . Contains ( $ "Iteration: { i } ", result ) ;
197+ Xunit . Assert . Contains ( $ "Value_{ i } ", result ) ;
198+ }
199+
200+ // If we got here without OutOfMemoryException, the test passes
201+ Xunit . Assert . True ( true ) ;
202+ }
203+
204+ [ Fact ]
205+ public void Format_WithCharacterLiterals_DoesNotConvert ( )
206+ {
207+ // Arrange - test that ConvertCharacterStringLiterals = false works
208+ var format = "Value: {value}\\ n" ;
209+ var args = new Dictionary < string , string >
210+ {
211+ [ "value" ] = "test"
212+ } ;
213+
214+ // Act
215+ var result = SmartFormatHelper . Format ( format , args ) ;
216+
217+ // Assert - should keep \\n as literal, not convert to newline
218+ Xunit . Assert . Contains ( "\\ n" , result ) ;
219+ Xunit . Assert . DoesNotContain ( "\n " , result ) ;
220+ }
221+
222+ [ Fact ]
223+ public void Format_ExceptionInFormatting_ReturnsOriginalFormat ( )
224+ {
225+ // Arrange - intentionally malformed format
226+ var format = "Bad format: {unclosed" ;
227+ var args = new Dictionary < string , string >
228+ {
229+ [ "test" ] = "value"
230+ } ;
231+
232+ // Act
233+ var result = SmartFormatHelper . Format ( format , args ) ;
234+
235+ // Assert - should return original format on exception
236+ Xunit . Assert . Equal ( format , result ) ;
237+ }
238+
239+ [ Theory ]
240+ [ InlineData ( "" , "" ) ]
241+ [ InlineData ( "No placeholders" , "No placeholders" ) ]
242+ [ InlineData ( "{name}" , "Alice" ) ]
243+ [ InlineData ( "{first} {last}" , "Alice Smith" ) ]
244+ public void Format_VariousInputs_ProducesExpectedOutput ( string format , string expected )
245+ {
246+ // Arrange
247+ var args = new Dictionary < string , string >
248+ {
249+ [ "name" ] = "Alice" ,
250+ [ "first" ] = "Alice" ,
251+ [ "last" ] = "Smith"
252+ } ;
253+
254+ // Act
255+ var result = SmartFormatHelper . Format ( format , args ) ;
256+
257+ // Assert
258+ if ( expected == "" )
259+ {
260+ Xunit . Assert . Equal ( format , result ) ;
261+ }
262+ else
263+ {
264+ Xunit . Assert . Contains ( expected , result ) ;
265+ }
266+ }
267+ }
268+ }
0 commit comments