|
1 | | -namespace ServiceControl.AcceptanceTests.Recoverability.MessageFailures |
| 1 | +namespace ServiceControl.AcceptanceTests.Recoverability.MessageFailures; |
| 2 | + |
| 3 | +using System; |
| 4 | +using System.Collections.Generic; |
| 5 | +using System.Threading.Tasks; |
| 6 | +using AcceptanceTesting; |
| 7 | +using AcceptanceTesting.EndpointTemplates; |
| 8 | +using Infrastructure; |
| 9 | +using NServiceBus; |
| 10 | +using NServiceBus.AcceptanceTesting; |
| 11 | +using NServiceBus.Routing; |
| 12 | +using NServiceBus.Transport; |
| 13 | +using NUnit.Framework; |
| 14 | +using ServiceControl.MessageFailures.Api; |
| 15 | + |
| 16 | +class When_ingesting_failed_message_with_missing_headers : AcceptanceTest |
2 | 17 | { |
3 | | - using System; |
4 | | - using System.Collections.Generic; |
5 | | - using System.Threading.Tasks; |
6 | | - using AcceptanceTesting; |
7 | | - using AcceptanceTesting.EndpointTemplates; |
8 | | - using Infrastructure; |
9 | | - using NServiceBus; |
10 | | - using NServiceBus.AcceptanceTesting; |
11 | | - using NServiceBus.Routing; |
12 | | - using NServiceBus.Transport; |
13 | | - using NUnit.Framework; |
14 | | - using ServiceControl.MessageFailures.Api; |
15 | | - |
16 | | - class When_ingesting_failed_message_with_missing_headers : AcceptanceTest |
| 18 | + [Test] |
| 19 | + public async Task Should_be_ingested_when_minimal_required_headers_is_present() |
17 | 20 | { |
18 | | - [Test] |
19 | | - public async Task Should_be_ingested_when_minimal_required_headers_is_present() |
20 | | - { |
21 | | - FailedMessageView failure = null; |
22 | | - |
23 | | - var testStartTime = DateTime.UtcNow; |
24 | | - |
25 | | - await Define<MyContext>(c => |
26 | | - { |
27 | | - c.AddMinimalRequiredHeaders(); |
28 | | - }) |
29 | | - .WithEndpoint<FailingEndpoint>() |
30 | | - .Done(async c => |
31 | | - { |
32 | | - var result = await this.TryGet<FailedMessageView>($"/api/errors/last/{c.UniqueMessageId}"); |
33 | | - failure = result; |
34 | | - return (c.UniqueMessageId != null) & result; |
35 | | - }) |
36 | | - .Run(); |
37 | | - |
38 | | - Assert.That(failure, Is.Not.Null); |
39 | | - Assert.That(failure.TimeSent, Is.Null); |
40 | | - |
41 | | - //No failure time will result in utc now being used |
42 | | - Assert.That(failure.TimeOfFailure, Is.GreaterThan(testStartTime)); |
43 | | - |
44 | | - // Both host and endpoint name is currently needed so this will be null since no host can be detected from the failed q header |
45 | | - Assert.That(failure.ReceivingEndpoint, Is.Null); |
46 | | - } |
| 21 | + var testStartTime = DateTime.UtcNow; |
47 | 22 |
|
48 | | - [Test] |
49 | | - public async Task Should_include_headers_required_by_ServicePulse() |
50 | | - { |
51 | | - FailedMessageView failure = null; |
52 | | - |
53 | | - var context = await Define<MyContext>(c => |
54 | | - { |
55 | | - c.AddMinimalRequiredHeaders(); |
56 | | - |
57 | | - // This is needed for ServiceControl to be able to detect both endpoint (via failed q header) and host via the processing machine header |
58 | | - // Missing endpoint or host will cause a null ref in ServicePulse |
59 | | - c.Headers[Headers.ProcessingMachine] = "MyMachine"; |
60 | | - |
61 | | - c.Headers["NServiceBus.ExceptionInfo.ExceptionType"] = "SomeExceptionType"; |
62 | | - c.Headers["NServiceBus.ExceptionInfo.Message"] = "Some message"; |
63 | | - }) |
64 | | - .WithEndpoint<FailingEndpoint>() |
65 | | - .Done(async c => |
66 | | - { |
67 | | - var result = await this.TryGet<FailedMessageView>($"/api/errors/last/{c.UniqueMessageId}"); |
68 | | - failure = result; |
69 | | - return (c.UniqueMessageId != null) & result; |
70 | | - }) |
71 | | - .Run(); |
72 | | - |
73 | | - Assert.That(failure, Is.Not.Null); |
74 | | - |
75 | | - // ServicePulse assumes that the receiving endpoint name is present |
76 | | - Assert.That(failure.ReceivingEndpoint, Is.Not.Null); |
77 | | - Assert.That(failure.ReceivingEndpoint.Name, Is.EqualTo(context.EndpointNameOfReceivingEndpoint)); |
78 | | - Assert.That(failure.ReceivingEndpoint.Host, Is.EqualTo("MyMachine")); |
79 | | - |
80 | | - // ServicePulse needs both an exception type and description to render the UI in a resonable way |
81 | | - Assert.That(failure.Exception.ExceptionType, Is.EqualTo("SomeExceptionType")); |
82 | | - Assert.That(failure.Exception.Message, Is.EqualTo("Some message")); |
83 | | - } |
| 23 | + var context = await Define<TestContext>(c => c.AddMinimalRequiredHeaders()) |
| 24 | + .WithEndpoint<FailingEndpoint>() |
| 25 | + .Done(async c => await TryGetFailureFromApi(c)) |
| 26 | + .Run(); |
84 | 27 |
|
85 | | - [Test] |
86 | | - public async Task TimeSent_should_not_be_casted() |
87 | | - { |
88 | | - FailedMessageView failure = null; |
89 | | - |
90 | | - var sentTime = DateTime.Parse("2014-11-11T02:26:58.000462Z"); |
91 | | - |
92 | | - await Define<MyContext>(c => |
93 | | - { |
94 | | - c.AddMinimalRequiredHeaders(); |
95 | | - c.Headers.Add("NServiceBus.TimeSent", DateTimeOffsetHelper.ToWireFormattedString(sentTime)); |
96 | | - }) |
97 | | - .WithEndpoint<FailingEndpoint>() |
98 | | - .Done(async c => |
99 | | - { |
100 | | - var result = await this.TryGet<FailedMessageView>($"/api/errors/last/{c.UniqueMessageId}"); |
101 | | - failure = result; |
102 | | - return (c.UniqueMessageId != null) & result; |
103 | | - }) |
104 | | - .Run(); |
105 | | - |
106 | | - Assert.That(failure, Is.Not.Null); |
107 | | - Assert.That(failure.TimeSent, Is.EqualTo(sentTime)); |
108 | | - } |
| 28 | + var failure = context.Failure; |
109 | 29 |
|
110 | | - class MyContext : ScenarioContext |
111 | | - { |
112 | | - public string MessageId { get; } = Guid.NewGuid().ToString(); |
| 30 | + Assert.That(failure, Is.Not.Null); |
| 31 | + Assert.That(failure.TimeSent, Is.Null); |
113 | 32 |
|
114 | | - public string EndpointNameOfReceivingEndpoint => "MyEndpoint"; |
| 33 | + //No failure time will result in utc now being used |
| 34 | + Assert.That(failure.TimeOfFailure, Is.GreaterThan(testStartTime)); |
115 | 35 |
|
116 | | - public string UniqueMessageId => DeterministicGuid.MakeId(MessageId, EndpointNameOfReceivingEndpoint).ToString(); |
| 36 | + // Both host and endpoint name is currently needed so this will be null since no host can be detected from the failed q header |
| 37 | + Assert.That(failure.ReceivingEndpoint, Is.Null); |
| 38 | + } |
117 | 39 |
|
118 | | - public Dictionary<string, string> Headers { get; } = []; |
| 40 | + [Test] |
| 41 | + public async Task Should_include_headers_required_by_ServicePulse() |
| 42 | + { |
| 43 | + var context = await Define<TestContext>(c => |
| 44 | + { |
| 45 | + c.AddMinimalRequiredHeaders(); |
| 46 | + |
| 47 | + // This is needed for ServiceControl to be able to detect both endpoint (via failed q header) and host via the processing machine header |
| 48 | + // Missing endpoint or host will cause a null ref in ServicePulse |
| 49 | + c.Headers[Headers.ProcessingMachine] = "MyMachine"; |
| 50 | + |
| 51 | + c.Headers["NServiceBus.ExceptionInfo.ExceptionType"] = "SomeExceptionType"; |
| 52 | + c.Headers["NServiceBus.ExceptionInfo.Message"] = "Some message"; |
| 53 | + }) |
| 54 | + .WithEndpoint<FailingEndpoint>() |
| 55 | + .Done(async c => await TryGetFailureFromApi(c)) |
| 56 | + .Run(); |
| 57 | + |
| 58 | + var failure = context.Failure; |
| 59 | + |
| 60 | + Assert.That(failure, Is.Not.Null); |
| 61 | + |
| 62 | + // ServicePulse assumes that the receiving endpoint name is present |
| 63 | + Assert.That(failure.ReceivingEndpoint, Is.Not.Null); |
| 64 | + Assert.That(failure.ReceivingEndpoint.Name, Is.EqualTo(context.EndpointNameOfReceivingEndpoint)); |
| 65 | + Assert.That(failure.ReceivingEndpoint.Host, Is.EqualTo("MyMachine")); |
| 66 | + |
| 67 | + // ServicePulse needs both an exception type and description to render the UI in a resonable way |
| 68 | + Assert.That(failure.Exception.ExceptionType, Is.EqualTo("SomeExceptionType")); |
| 69 | + Assert.That(failure.Exception.Message, Is.EqualTo("Some message")); |
| 70 | + } |
| 71 | + |
| 72 | + [Test] |
| 73 | + public async Task TimeSent_should_not_be_casted() |
| 74 | + { |
| 75 | + var sentTime = DateTime.Parse("2014-11-11T02:26:58.000462Z"); |
119 | 76 |
|
120 | | - public void AddMinimalRequiredHeaders() |
| 77 | + var context = await Define<TestContext>(c => |
121 | 78 | { |
122 | | - Headers["NServiceBus.FailedQ"] = EndpointNameOfReceivingEndpoint; |
123 | | - Headers[NServiceBus.Headers.MessageId] = MessageId; |
124 | | - } |
125 | | - } |
| 79 | + c.AddMinimalRequiredHeaders(); |
| 80 | + c.Headers.Add("NServiceBus.TimeSent", DateTimeOffsetHelper.ToWireFormattedString(sentTime)); |
| 81 | + }) |
| 82 | + .WithEndpoint<FailingEndpoint>() |
| 83 | + .Done(async c => await TryGetFailureFromApi(c)) |
| 84 | + .Run(); |
| 85 | + |
| 86 | + var failure = context.Failure; |
| 87 | + |
| 88 | + Assert.That(failure, Is.Not.Null); |
| 89 | + Assert.That(failure.TimeSent, Is.EqualTo(sentTime)); |
| 90 | + } |
| 91 | + |
| 92 | + async Task<bool> TryGetFailureFromApi(TestContext context) |
| 93 | + { |
| 94 | + context.Failure = await this.TryGet<FailedMessageView>($"/api/errors/last/{context.UniqueMessageId}"); |
| 95 | + return context.Failure != null; |
| 96 | + } |
| 97 | + |
| 98 | + class TestContext : ScenarioContext |
| 99 | + { |
| 100 | + public string MessageId { get; } = Guid.NewGuid().ToString(); |
126 | 101 |
|
127 | | - class FailingEndpoint : EndpointConfigurationBuilder |
| 102 | + public string EndpointNameOfReceivingEndpoint => "MyEndpoint"; |
| 103 | + |
| 104 | + public string UniqueMessageId => DeterministicGuid.MakeId(MessageId, EndpointNameOfReceivingEndpoint).ToString(); |
| 105 | + |
| 106 | + public Dictionary<string, string> Headers { get; } = []; |
| 107 | + |
| 108 | + public FailedMessageView Failure { get; set; } |
| 109 | + |
| 110 | + public void AddMinimalRequiredHeaders() |
128 | 111 | { |
129 | | - public FailingEndpoint() => EndpointSetup<DefaultServerWithoutAudit>(c => c.Recoverability().Delayed(x => x.NumberOfRetries(0))); |
| 112 | + Headers["NServiceBus.FailedQ"] = EndpointNameOfReceivingEndpoint; |
| 113 | + Headers[NServiceBus.Headers.MessageId] = MessageId; |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + class FailingEndpoint : EndpointConfigurationBuilder |
| 118 | + { |
| 119 | + public FailingEndpoint() => EndpointSetup<DefaultServerWithoutAudit>(); |
130 | 120 |
|
131 | | - class SendFailedMessage : DispatchRawMessages<MyContext> |
| 121 | + class SendFailedMessage : DispatchRawMessages<TestContext> |
| 122 | + { |
| 123 | + protected override TransportOperations CreateMessage(TestContext context) |
132 | 124 | { |
133 | | - protected override TransportOperations CreateMessage(MyContext context) |
134 | | - { |
135 | | - var outgoingMessage = new OutgoingMessage(context.MessageId, context.Headers, Array.Empty<byte>()); |
| 125 | + var outgoingMessage = new OutgoingMessage(context.MessageId, context.Headers, Array.Empty<byte>()); |
136 | 126 |
|
137 | | - return new TransportOperations(new TransportOperation(outgoingMessage, new UnicastAddressTag("error"))); |
138 | | - } |
| 127 | + return new TransportOperations(new TransportOperation(outgoingMessage, new UnicastAddressTag("error"))); |
139 | 128 | } |
140 | 129 | } |
141 | 130 | } |
|
0 commit comments