@@ -1018,23 +1018,81 @@ await elms.CaptureEach(
10181018 var cp = await registration . ControlPanel ( id . Instance ) . ShouldNotBeNullAsync ( ) ;
10191019 var messageWriter = registration . MessageWriters . For ( id . Instance ) ;
10201020 var effectStore = store . EffectsStore ;
1021+ var storedId = registration . MapToStoredId ( id . Instance ) ;
1022+
1023+ async Task < string > DumpAsync ( string phase )
1024+ {
1025+ string status ;
1026+ try { await cp . Refresh ( ) ; status = cp . Status . ToString ( ) ; }
1027+ catch ( Exception ex ) { status = $ "<refresh failed: { ex . GetType ( ) . Name } : { ex . Message } >"; }
1028+
1029+ string effectsDump ;
1030+ try
1031+ {
1032+ var effects = await effectStore . GetEffectResults ( storedId ) ;
1033+ effectsDump = string . Join ( ", " , effects . Select ( e => $ "{ e . Alias ?? "<null>" } :{ e . EffectId } :{ e . WorkStatus } ") ) ;
1034+ }
1035+ catch ( Exception ex ) { effectsDump = $ "<effects fetch failed: { ex . GetType ( ) . Name } : { ex . Message } >"; }
1036+
1037+ string messagesDump ;
1038+ try
1039+ {
1040+ var msgs = await store . MessageStore . GetMessages ( storedId ) ;
1041+ messagesDump = string . Join ( ", " , msgs . Select ( m =>
1042+ {
1043+ try { return $ "[{ m . Position } ]{ System . Text . Encoding . UTF8 . GetString ( m . MessageContent ) } "; }
1044+ catch { return $ "[{ m . Position } ]<{ m . MessageContent . Length } bytes>"; }
1045+ } ) ) ;
1046+ }
1047+ catch ( Exception ex ) { messagesDump = $ "<messages fetch failed: { ex . GetType ( ) . Name } : { ex . Message } >"; }
1048+
1049+ int [ ] iterationsSnapshot ;
1050+ try { iterationsSnapshot = iterations . ToArray ( ) ; }
1051+ catch { iterationsSnapshot = Array . Empty < int > ( ) ; }
1052+
1053+ return $ "phase={ phase } ; flag.Value={ flag . Value } ; status={ status } ; iterations=[{ string . Join ( "," , iterationsSnapshot ) } ]; effects=[{ effectsDump } ]; messages=[{ messagesDump } ]";
1054+ }
1055+
1056+ async Task WaitAsync ( string label , Func < Task < bool > > predicate )
1057+ {
1058+ try
1059+ {
1060+ await BusyWait . Until ( predicate , maxWait : TimeSpan . FromSeconds ( 30 ) ) ;
1061+ }
1062+ catch ( TimeoutException )
1063+ {
1064+ throw new TimeoutException ( $ "Wait '{ label } ' timed out after 30s. { await DumpAsync ( label ) } ") ;
1065+ }
1066+ }
10211067
10221068 for ( var i = 0 ; i < 6 ; i ++ )
10231069 {
1024- await BusyWait . Until ( ( ) => flag . Value == i , maxWait : TimeSpan . FromSeconds ( 30 ) ) ;
1025- await cp . BusyWaitUntil ( c => c . Status == Status . Suspended , maxWait : TimeSpan . FromSeconds ( 30 ) ) ;
1026- var storedEffects = await effectStore . GetEffectResults ( registration . MapToStoredId ( id . Instance ) ) ;
1027- storedEffects . Any ( e => e . Alias == "Before" ) . ShouldBeTrue ( ) ;
1028- storedEffects . Any ( e => e . Alias == "Loop" ) . ShouldBeTrue ( ) ;
1029- storedEffects . Single ( e => e . Alias == i . ToString ( ) ) . EffectId . ShouldBe ( new EffectId ( [ 1 , i , 0 ] ) ) ;
1030- storedEffects . Count ( e => e . Alias != null ) . ShouldBe ( 3 ) ;
1070+ await WaitAsync ( $ "flag.Value == { i } ", ( ) => Task . FromResult ( flag . Value == i ) ) ;
1071+ await WaitAsync ( $ "Status == Suspended (i={ i } )", async ( ) => { await cp . Refresh ( ) ; return cp . Status == Status . Suspended ; } ) ;
1072+
1073+ var storedEffects = await effectStore . GetEffectResults ( storedId ) ;
1074+ try
1075+ {
1076+ storedEffects . Any ( e => e . Alias == "Before" ) . ShouldBeTrue ( ) ;
1077+ storedEffects . Any ( e => e . Alias == "Loop" ) . ShouldBeTrue ( ) ;
1078+ storedEffects . Single ( e => e . Alias == i . ToString ( ) ) . EffectId . ShouldBe ( new EffectId ( [ 1 , i , 0 ] ) ) ;
1079+ storedEffects . Count ( e => e . Alias != null ) . ShouldBe ( 3 ) ;
1080+ }
1081+ catch ( Exception ex )
1082+ {
1083+ throw new Exception ( $ "Effect assertion failed at i={ i } . { await DumpAsync ( $ "effect-assertions i={ i } ") } ", ex ) ;
1084+ }
10311085
10321086 await messageWriter . AppendMessage ( i . ToString ( ) ) ;
10331087 }
10341088
1035- await cp . BusyWaitUntil ( c => c . Status == Status . Succeeded , maxWait : TimeSpan . FromSeconds ( 30 ) ) ;
1089+ await WaitAsync ( "Status == Succeeded (final)" , async ( ) => { await cp . Refresh ( ) ; return cp . Status == Status . Succeeded ; } ) ;
10361090
1037- iterations . SequenceEqual ( [ 0 , 1 , 2 , 3 , 4 , 5 ] ) . ShouldBeTrue ( ) ;
1091+ try { iterations . SequenceEqual ( [ 0 , 1 , 2 , 3 , 4 , 5 ] ) . ShouldBeTrue ( ) ; }
1092+ catch ( Exception ex )
1093+ {
1094+ throw new Exception ( $ "Final iterations check failed. { await DumpAsync ( "final-iterations" ) } ", ex ) ;
1095+ }
10381096 }
10391097
10401098 public abstract Task ChildEffectsAreClearedWhenParentEffectWithResultCompletes ( ) ;
0 commit comments