1+ namespace ServiceControl . AcceptanceTests . Recoverability . ExternalIntegration
2+ {
3+ using System . Collections . Generic ;
4+ using System . Linq ;
5+ using System . Threading . Tasks ;
6+ using AcceptanceTesting ;
7+ using AcceptanceTesting . EndpointTemplates ;
8+ using Contracts ;
9+ using NServiceBus ;
10+ using NServiceBus . AcceptanceTesting ;
11+ using NUnit . Framework ;
12+ using ServiceControl . MessageFailures ;
13+ using ServiceControl . MessageFailures . Api ;
14+ using JsonSerializer = System . Text . Json . JsonSerializer ;
15+
16+ class When_a_failed_edit_is_resolved_by_retry : ExternalIntegrationAcceptanceTest
17+ {
18+ [ Test ]
19+ public async Task Should_publish_notification ( )
20+ {
21+ var context = await Define < EditMessageResolutionContext > ( )
22+ . WithEndpoint < EditMessageResolutionReceiver > ( b => b . When ( async ( bus , c ) =>
23+ {
24+ await bus . Subscribe < MessageFailureResolvedByRetry > ( ) ;
25+ } ) . When ( c => c . SendLocal ( new EditResolutionMessage ( ) ) ) . DoNotFailOnErrorMessages ( ) )
26+ . Done ( async ctx =>
27+ {
28+ if ( ! ctx . OriginalMessageHandled )
29+ {
30+ return false ;
31+ }
32+
33+ if ( ! ctx . EditedAndRetriedMessage )
34+ {
35+ var allFailedMessages =
36+ await this . TryGet < IList < FailedMessageView > > ( $ "/api/errors/?status=unresolved") ;
37+ if ( ! allFailedMessages . HasResult )
38+ {
39+ return false ;
40+ }
41+
42+ if ( allFailedMessages . Item . Count != 1 )
43+ {
44+ return false ;
45+ }
46+
47+ ctx . OriginalMessageFailureId = allFailedMessages . Item . First ( ) . Id ;
48+ ctx . EditedAndRetriedMessage = true ;
49+
50+ string editedMessage = JsonSerializer . Serialize ( new EditResolutionMessage
51+ {
52+ HasBeenEdited = true
53+ } ) ;
54+
55+ SingleResult < FailedMessage > failedMessage =
56+ await this . TryGet < FailedMessage > ( $ "/api/errors/{ ctx . OriginalMessageFailureId } ") ;
57+
58+ var editModel = new EditMessageModel
59+ {
60+ MessageBody = editedMessage ,
61+ MessageHeaders = failedMessage . Item . ProcessingAttempts . Last ( ) . Headers
62+ } ;
63+ await this . Post ( $ "/api/edit/{ ctx . OriginalMessageFailureId } ", editModel ) ;
64+ return false ;
65+ }
66+
67+ if ( ! ctx . RetriedMessage )
68+ {
69+ var allFailedMessagesAfterEdit =
70+ await this . TryGet < IList < FailedMessageView > > ( $ "/api/errors/?status=unresolved") ;
71+ if ( ! allFailedMessagesAfterEdit . HasResult )
72+ {
73+ return false ;
74+ }
75+
76+ if ( allFailedMessagesAfterEdit . Item . Count != 1 )
77+ {
78+ return false ;
79+ }
80+
81+ ctx . EditedMessageFailureId = allFailedMessagesAfterEdit . Item . First ( ) . Id ;
82+ ctx . RetriedMessage = true ;
83+
84+ await this . Post < object > ( $ "/api/errors/{ ctx . EditedMessageFailureId } /retry") ;
85+ return false ;
86+ }
87+
88+ if ( ! ctx . RetriedMessageHandled )
89+ {
90+ return false ;
91+ }
92+
93+ if ( ! ctx . MessageResolved )
94+ {
95+ return false ;
96+ }
97+
98+ return true ;
99+ } ) . Run ( ) ;
100+
101+ Assert . That ( context . ResolvedMessageId , Is . EqualTo ( context . OriginalMessageFailureId ) ) ;
102+ }
103+
104+
105+ class EditMessageResolutionContext : ScenarioContext
106+ {
107+ public bool OriginalMessageHandled { get ; set ; }
108+ public bool EditedMessage { get ; set ; }
109+ public string OriginalMessageFailureId { get ; set ; }
110+ public bool EditedMessageHandled { get ; set ; }
111+ public string ResolvedMessageId { get ; set ; }
112+ public bool MessageResolved { get ; set ; }
113+ public bool EditedAndRetriedMessgaeHandled { get ; set ; }
114+ public bool RetriedMessageHandled { get ; set ; }
115+ public string EditedMessageFailureId { get ; set ; }
116+ public bool EditedAndRetriedMessage { get ; set ; }
117+ public bool RetriedMessage { get ; set ; }
118+ }
119+
120+ class EditMessageResolutionReceiver : EndpointConfigurationBuilder
121+ {
122+ public EditMessageResolutionReceiver ( ) => EndpointSetup < DefaultServerWithoutAudit > ( c => c . NoRetries ( ) ) ;
123+
124+ class EditMessageResolutionHandler ( EditMessageResolutionContext testContext )
125+ : IHandleMessages < EditResolutionMessage > , IHandleMessages < MessageFailureResolvedByRetry >
126+ {
127+ public Task Handle ( EditResolutionMessage message , IMessageHandlerContext context )
128+ {
129+ if ( ! message . HasBeenEdited )
130+ {
131+ testContext . OriginalMessageHandled = true ;
132+ throw new SimulatedException ( ) ;
133+ }
134+
135+ if ( ! testContext . RetriedMessage )
136+ {
137+ testContext . EditedAndRetriedMessgaeHandled = true ;
138+ throw new SimulatedException ( ) ;
139+ }
140+
141+
142+ testContext . RetriedMessageHandled = true ;
143+ return Task . CompletedTask ;
144+ }
145+
146+ public Task Handle ( MessageFailureResolvedByRetry message , IMessageHandlerContext context )
147+ {
148+ testContext . ResolvedMessageId = message . FailedMessageId ;
149+ testContext . MessageResolved = true ;
150+ return Task . CompletedTask ;
151+ }
152+ }
153+ }
154+
155+ class EditResolutionMessage : IMessage
156+ {
157+ public bool HasBeenEdited { get ; init ; }
158+ public bool HasBeenRetried { get ; init ; }
159+ }
160+ }
161+ }
0 commit comments