diff --git a/src/ServiceControl.AcceptanceTests/EventLogs/When_the_event_log_is_polled_with_an_etag.cs b/src/ServiceControl.AcceptanceTests/EventLogs/When_the_event_log_is_polled_with_an_etag.cs index aef1816f6f..1e98c47e1d 100644 --- a/src/ServiceControl.AcceptanceTests/EventLogs/When_the_event_log_is_polled_with_an_etag.cs +++ b/src/ServiceControl.AcceptanceTests/EventLogs/When_the_event_log_is_polled_with_an_etag.cs @@ -45,21 +45,22 @@ await Define() return false; } - // Raw header: an unquoted value fails EntityTagHeaderValue parsing, and this test - // has to observe that rather than throw on it. - if (!first.Headers.TryGetValues("ETag", out var values)) + var currentEtag = ReadEtag(first); + + if (string.IsNullOrEmpty(currentEtag)) { return false; } - etag = values.FirstOrDefault(); + var current = await PollUntilTheLogGoesQuiet(currentEtag); - if (string.IsNullOrEmpty(etag)) + if (current == null) { return false; } - currentEtagStatus = (await Poll(etag)).StatusCode; + etag = currentEtag; + currentEtagStatus = current.StatusCode; var unknown = await Poll("\"not-an-etag-this-instance-ever-issued\""); unknownEtagStatus = unknown.StatusCode; @@ -87,6 +88,40 @@ await Define() } } + // Raw header: an unquoted value fails EntityTagHeaderValue parsing, and this test + // has to observe that rather than throw on it. + static string ReadEtag(HttpResponseMessage response) => + response.Headers.TryGetValues("ETag", out var values) ? values.FirstOrDefault() : null; + + // The endpoint keeps writing startup events, so a 200 carrying a *different* ETag only + // means the log moved on between the two requests, and says nothing about conditional + // GET support. Poll again with the ETag that response handed back, until either the log + // stops changing (304, or a 200 repeating the validator we sent, which is the real + // failure) or the attempts run out and the caller retries from a fresh read. + async Task PollUntilTheLogGoesQuiet(string etag) + { + for (var attempt = 0; attempt < MaxPollAttempts; attempt++) + { + var response = await Poll(etag); + + if (response.StatusCode != HttpStatusCode.OK) + { + return response; + } + + var newEtag = ReadEtag(response); + + if (string.IsNullOrEmpty(newEtag) || newEtag == etag) + { + return response; + } + + etag = newEtag; + } + + return null; + } + Task Poll(string ifNoneMatch) { var request = new HttpRequestMessage(HttpMethod.Get, "/api/eventlogitems/"); @@ -95,6 +130,8 @@ Task Poll(string ifNoneMatch) return HttpClient.SendAsync(request); } + const int MaxPollAttempts = 5; + public class StartingEndpoint : EndpointConfigurationBuilder { public StartingEndpoint() =>