Skip to content

Commit d6bef0f

Browse files
rewrote the resolved by edit & retry test
1 parent 1a20f53 commit d6bef0f

File tree

1 file changed

+86
-55
lines changed

1 file changed

+86
-55
lines changed
Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,127 @@
11
namespace ServiceControl.AcceptanceTests.Recoverability.ExternalIntegration
22
{
3-
using System;
43
using System.Collections.Generic;
4+
using System.Linq;
55
using System.Threading.Tasks;
66
using AcceptanceTesting;
77
using AcceptanceTesting.EndpointTemplates;
88
using Contracts;
99
using NServiceBus;
1010
using NServiceBus.AcceptanceTesting;
1111
using NUnit.Framework;
12-
using ServiceBus.Management.Infrastructure.Settings;
1312
using ServiceControl.MessageFailures;
13+
using ServiceControl.MessageFailures.Api;
1414
using JsonSerializer = System.Text.Json.JsonSerializer;
1515

1616
class When_a_failed_message_is_resolved_by_edit_and_retry : ExternalIntegrationAcceptanceTest
1717
{
1818
[Test]
1919
public async Task Should_publish_notification()
2020
{
21-
CustomConfiguration = config => config.OnEndpointSubscribed<Context>((s, ctx) =>
22-
{
23-
ctx.ExternalProcessorSubscribed = s.SubscriberReturnAddress.Contains(nameof(ExternalProcessor));
24-
});
25-
26-
var context = await Define<Context>()
27-
.WithEndpoint<ErrorSender>(b => b.When(session => Task.CompletedTask).DoNotFailOnErrorMessages())
28-
.WithEndpoint<ExternalProcessor>(b => b.When(async (bus, c) =>
21+
var context = await Define<EditMessageResolutionContext>()
22+
.WithEndpoint<EditMessageResolutionReceiver>(b => b.When(async (bus, c) =>
2923
{
3024
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+
}
3132

32-
if (c.HasNativePubSubSupport)
33+
if (!ctx.EditedMessage)
3334
{
34-
c.ExternalProcessorSubscribed = true;
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+
49+
ctx.EditedMessage = true;
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;
3565
}
36-
}))
37-
.Do("WaitUntilErrorsContainsFailedMessage",
38-
async ctx => await this.TryGet<FailedMessage>($"/api/errors/{ctx.FailedMessageId}") != null)
39-
.Do("WaitForExternalProcessorToSubscribe",
40-
ctx => Task.FromResult(ctx.ExternalProcessorSubscribed))
41-
.Do("EditAndRetry", async ctx =>
42-
{
43-
// First retrieve the original failed message to get all its headers
44-
var originalFailedMessageResult = await this.TryGet<FailedMessage>($"/api/errors/{ctx.FailedMessageId}");
45-
var originalFailedMessage = originalFailedMessageResult.Item;
4666

47-
// Convert the original headers to Dictionary<string, string> for the edit payload
48-
var originalHeaders = new Dictionary<string, string>();
49-
foreach (var header in originalFailedMessage.ProcessingAttempts[0].Headers)
67+
if (!ctx.EditedMessageHandled)
5068
{
51-
originalHeaders[header.Key] = header.Value;
69+
return false;
5270
}
5371

54-
// Prepare the edit payload with all original headers (locked headers unchanged, others can be modified)
55-
var editPayload = new
72+
if (!ctx.MessageResolved)
5673
{
57-
message_body = "{}", // Empty JSON body for AMessage (ICommand with no properties)
58-
message_headers = originalHeaders // Use all original headers to satisfy controller validation
59-
};
74+
return false;
75+
}
6076

61-
await this.Post($"/api/edit/{ctx.FailedMessageId}", editPayload);
62-
})
63-
.Do("EnsureRetried", async ctx =>
64-
{
65-
return await this.TryGet<FailedMessage>($"/api/errors/{ctx.FailedMessageId}",
66-
e => e.Status == FailedMessageStatus.Resolved);
67-
})
68-
.Done(ctx => ctx.EventDelivered) //Done when sequence is finished
69-
.Run();
70-
71-
var deserializedEvent = JsonSerializer.Deserialize<MessageFailureResolvedByRetry>(context.Event);
72-
Assert.That(deserializedEvent?.FailedMessageId, Is.EqualTo(context.FailedMessageId.ToString()));
77+
return true;
78+
}).Run();
79+
80+
Assert.That(context.ResolvedMessageId, Is.EqualTo(context.OriginalMessageFailureId));
7381
}
7482

75-
public class ExternalProcessor : EndpointConfigurationBuilder
83+
84+
class EditMessageResolutionContext : ScenarioContext
7685
{
77-
public ExternalProcessor() =>
78-
EndpointSetup<DefaultServerWithoutAudit>(c =>
79-
{
80-
var routing = c.ConfigureRouting();
81-
routing.RouteToEndpoint(typeof(MessageFailureResolvedByRetry).Assembly, Settings.DEFAULT_INSTANCE_NAME);
82-
}, publisherMetadata => { publisherMetadata.RegisterPublisherFor<MessageFailureResolvedByRetry>(Settings.DEFAULT_INSTANCE_NAME); });
86+
public bool OriginalMessageHandled { get; set; }
87+
public bool EditedMessage { get; set; }
88+
public string OriginalMessageFailureId { get; set; }
89+
public bool EditedMessageHandled { get; set; }
90+
public string ResolvedMessageId { get; set; }
91+
public bool MessageResolved { get; set; }
92+
}
8393

84-
public class FailureHandler(Context testContext) : IHandleMessages<MessageFailureResolvedByRetry>
94+
class EditMessageResolutionReceiver : EndpointConfigurationBuilder
95+
{
96+
public EditMessageResolutionReceiver() => EndpointSetup<DefaultServerWithoutAudit>(c => c.NoRetries());
97+
98+
class EditMessageResolutionHandler(EditMessageResolutionContext testContext)
99+
: IHandleMessages<EditResolutionMessage>, IHandleMessages<MessageFailureResolvedByRetry>
85100
{
101+
public Task Handle(EditResolutionMessage message, IMessageHandlerContext context)
102+
{
103+
if (message.HasBeenEdited)
104+
{
105+
testContext.EditedMessageHandled = true;
106+
return Task.CompletedTask;
107+
}
108+
109+
testContext.OriginalMessageHandled = true;
110+
throw new SimulatedException();
111+
}
112+
86113
public Task Handle(MessageFailureResolvedByRetry message, IMessageHandlerContext context)
87114
{
88-
var serializedMessage = JsonSerializer.Serialize(message);
89-
testContext.Event = serializedMessage;
90-
testContext.EventDelivered = true;
115+
testContext.ResolvedMessageId = message.FailedMessageId;
116+
testContext.MessageResolved = true;
91117
return Task.CompletedTask;
92118
}
93119
}
94120
}
121+
122+
class EditResolutionMessage : IMessage
123+
{
124+
public bool HasBeenEdited { get; init; }
125+
}
95126
}
96-
}
127+
}

0 commit comments

Comments
 (0)