1+ namespace ServiceControl . MultiInstance . AcceptanceTests . Recoverability
2+ {
3+ using System ;
4+ using System . Linq ;
5+ using System . Threading . Tasks ;
6+ using AcceptanceTesting ;
7+ using AcceptanceTesting . EndpointTemplates ;
8+ using MessageFailures ;
9+ using MessageFailures . Api ;
10+ using NServiceBus ;
11+ using NServiceBus . AcceptanceTesting ;
12+ using NServiceBus . Settings ;
13+ using NUnit . Framework ;
14+ using ServiceControl . Infrastructure ;
15+ using TestSupport ;
16+
17+ class WhenRetryingSameMessageMultipleTimes : AcceptanceTest
18+ {
19+ [ Test ]
20+ public async Task WithNoEdit ( )
21+ {
22+ await Define < MyContext > ( )
23+ . WithEndpoint < FailureEndpoint > ( b => b . When ( bus => bus . SendLocal ( new MyMessage ( ) ) ) . DoNotFailOnErrorMessages ( ) )
24+ . Done ( async c =>
25+ {
26+ if ( c . RetryCount < 3 )
27+ {
28+ var result = await GetFailedMessage ( c , ServiceControlInstanceName , FailedMessageStatus . Unresolved ) ;
29+ if ( result . HasResult )
30+ {
31+ if ( result . Item . ProcessingAttempts . Count == c . RetryCount + 1 )
32+ {
33+ await this . Post < object > ( $ "/api/errors/{ result . Item . UniqueMessageId } /retry", null , null ,
34+ ServiceControlInstanceName ) ;
35+ c . RetryCount ++ ;
36+ }
37+ }
38+
39+ return false ;
40+ }
41+
42+ return await GetFailedMessage ( c , ServiceControlInstanceName , FailedMessageStatus . Resolved ) ;
43+ } )
44+ . Run ( TimeSpan . FromMinutes ( 2 ) ) ;
45+ }
46+
47+ [ Test ]
48+ public async Task WithEdit ( )
49+ {
50+ CustomServiceControlPrimarySettings = s => { s . AllowMessageEditing = true ; } ;
51+
52+ await Define < MyContext > ( )
53+ . WithEndpoint < FailureEndpoint > ( b =>
54+ b . When ( bus => bus . SendLocal ( new MyMessage ( ) ) ) . DoNotFailOnErrorMessages ( ) )
55+ . Done ( async c =>
56+ {
57+ if ( c . RetryCount < 3 )
58+ {
59+ var results = await GetAllFailedMessage ( ServiceControlInstanceName , FailedMessageStatus . Unresolved ) ;
60+ if ( ! results . HasResult )
61+ {
62+ return false ;
63+ }
64+
65+ var result = results . Items . Single ( ) ;
66+
67+ c . MessageId = result . MessageId ;
68+
69+ var failedMessage = await GetFailedMessage ( c , ServiceControlInstanceName , FailedMessageStatus . Unresolved ) ;
70+ if ( ! failedMessage . HasResult )
71+ {
72+ return false ;
73+ }
74+
75+ await this . Post < object > ( $ "/api/edit/{ failedMessage . Item . UniqueMessageId } ",
76+ new
77+ {
78+ MessageBody = $ "{{ \" Name\" : \" John{ c . RetryCount } \" }}",
79+ MessageHeaders = failedMessage . Item . ProcessingAttempts [ ^ 1 ] . Headers
80+ } , null ,
81+ ServiceControlInstanceName ) ;
82+ c . RetryCount ++ ;
83+
84+ return false ;
85+ }
86+
87+ return ! ( await GetAllFailedMessage ( ServiceControlInstanceName , FailedMessageStatus . Unresolved ) ) . HasResult ;
88+ } )
89+ . Run ( TimeSpan . FromMinutes ( 2 ) ) ;
90+ }
91+
92+ Task < SingleResult < FailedMessage > > GetFailedMessage ( MyContext c , string instance , FailedMessageStatus expectedStatus )
93+ {
94+ if ( c . MessageId == null )
95+ {
96+ return Task . FromResult ( SingleResult < FailedMessage > . Empty ) ;
97+ }
98+
99+ return this . TryGet < FailedMessage > ( "/api/errors/" + c . UniqueMessageId , f => f . Status == expectedStatus , instance ) ;
100+ }
101+
102+ Task < ManyResult < FailedMessageView > > GetAllFailedMessage ( string instance , FailedMessageStatus expectedStatus )
103+ {
104+ return this . TryGetMany < FailedMessageView > ( "/api/errors" , f => f . Status == expectedStatus , instance ) ;
105+ }
106+
107+ public class FailureEndpoint : EndpointConfigurationBuilder
108+ {
109+ public FailureEndpoint ( ) => EndpointSetup < DefaultServerWithoutAudit > ( c => { c . NoRetries ( ) ; } ) ;
110+
111+ public class MyMessageHandler : IHandleMessages < MyMessage >
112+ {
113+ readonly MyContext testContext ;
114+ readonly IReadOnlySettings settings ;
115+
116+ public MyMessageHandler ( MyContext testContext , IReadOnlySettings settings )
117+ {
118+ this . testContext = testContext ;
119+ this . settings = settings ;
120+ }
121+
122+ public Task Handle ( MyMessage message , IMessageHandlerContext context )
123+ {
124+ testContext . MessageId = context . MessageId . Replace ( @"\" , "-" ) ;
125+ testContext . EndpointNameOfReceivingEndpoint = settings . EndpointName ( ) ;
126+ Console . Out . WriteLine ( "Handling message" ) ;
127+
128+ if ( testContext . RetryCount < 3 )
129+ {
130+ Console . Out . WriteLine ( "Throwing exception for MyMessage" ) ;
131+ throw new Exception ( "Simulated exception" ) ;
132+ }
133+
134+ return Task . CompletedTask ;
135+ }
136+ }
137+ }
138+
139+
140+ public class MyMessage : ICommand
141+ {
142+ public string Name { get ; set ; }
143+ }
144+
145+ public class MyContext : ScenarioContext
146+ {
147+ public string MessageId { get ; set ; }
148+
149+ public string EndpointNameOfReceivingEndpoint { get ; set ; }
150+
151+ public string UniqueMessageId => DeterministicGuid . MakeId ( MessageId , EndpointNameOfReceivingEndpoint ) . ToString ( ) ;
152+ public int RetryCount { get ; set ; }
153+ }
154+ }
155+ }
0 commit comments