55using System ;
66using System . Collections . Generic ;
77using System . Linq ;
8+ using System . Threading . Tasks ;
89
910namespace Tests . EddiVoiceAttackService
1011{
@@ -16,8 +17,9 @@ namespace Tests.EddiVoiceAttackService
1617 public class MessageSerializerTests
1718 {
1819 private const string TestMessageId = "12345678-1234-1234-1234-123456789012" ;
20+ public TestContext TestContext { get ; set ; }
1921
20- [ TestMethod ]
22+ [ TestMethod ]
2123 public void Serialize_DisconnectMessage_ProducesLengthPrefixedJson ( )
2224 {
2325 // Arrange
@@ -206,29 +208,58 @@ public void Serialize_EventMessage_IncludesEventData ()
206208 }
207209
208210 [ TestMethod ]
209- public void Serialize_CommandMessage_IncludesCommandData ( )
211+ public void Serialize_EventMessage_WithModifyingCollection_DoesNotThrow ( )
210212 {
211- // Arrange
213+ // Arrange: Create a mutable dictionary that simulates concurrent modification
214+ var payload = new Dictionary < string , object > { { "key1" , "value1" } } ;
212215 var envelope = new MessageEnvelope
213216 {
214- Type = "Command " ,
217+ Type = "Event " ,
215218 Timestamp = "2025-01-20T15:30:45.123Z" ,
216219 Id = TestMessageId ,
217- Data = new CommandData
220+ Data = new EventData
218221 {
219- Command = "enable_monitor " ,
220- Target = "Journal Monitor " ,
221- Parameters = new Dictionary < string , object > { { "debug" , true } }
222+ EventType = "TestEvent " ,
223+ EventName = "Test " ,
224+ EventPayload = payload
222225 }
223226 } ;
224227
225- // Act
226- var serialized = MessageSerializer . Serialize ( envelope ) ;
227- var deserialized = MessageSerializer . Deserialize ( serialized ) ;
228+ // Act: Modify the dictionary in a separate task while serializing
229+ var serializationTask = Task . Run ( ( ) =>
230+ {
231+ // Serialize multiple times to increase chance of collision
232+ for ( var i = 0 ; i < 10 ; i ++ )
233+ {
234+ var serialized = MessageSerializer . Serialize ( envelope ) ;
235+ Assert . IsNotNull ( serialized ) ;
236+ }
237+ } , TestContext . CancellationToken ) ;
228238
229- // Assert
230- Assert . AreEqual ( "Command" , deserialized . Type ) ;
231- Assert . IsNotNull ( deserialized . Data ) ;
239+ var modificationTask = Task . Run ( ( ) =>
240+ {
241+ // Modify the dictionary while serialization is happening
242+ for ( var i = 0 ; i < 100 ; i ++ )
243+ {
244+ payload [ $ "key{ i } " ] = $ "value{ i } ";
245+ if ( ( i % 10 ) == 0 )
246+ {
247+ Task . Delay ( 1 , TestContext . CancellationToken ) . Wait ( TestContext . CancellationToken ) ; // Brief pause to increase race condition likelihood
248+ }
249+ }
250+ } , TestContext . CancellationToken ) ;
251+
252+ // Assert: Both tasks should complete without exception
253+ try
254+ {
255+ Task . WaitAll ( [ serializationTask , modificationTask ] , TimeSpan . FromSeconds ( 5 ) ) ;
256+ Assert . IsTrue ( serializationTask . IsCompletedSuccessfully ) ;
257+ Assert . IsTrue ( modificationTask . IsCompletedSuccessfully ) ;
258+ }
259+ catch ( AggregateException ex )
260+ {
261+ Assert . Fail ( $ "Serialization failed during concurrent modification: { ex . InnerException ? . Message } " ) ;
262+ }
232263 }
233264
234265 [ TestMethod ]
0 commit comments