Skip to content

Commit 00a072b

Browse files
authored
ci: fixes flaky tests (#677)
* ci: fixes flaky tests Signed-off-by: Vincent Biret <vibiret@microsoft.com> * chore: formatting --------- Signed-off-by: Vincent Biret <vibiret@microsoft.com>
1 parent 144edb4 commit 00a072b

1 file changed

Lines changed: 41 additions & 35 deletions

File tree

tests/http/httpClient/HttpClientRequestAdapterObservabilityTests.cs

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Concurrent;
12
using System.Diagnostics;
23
using System.Net;
34
using System.Net.Http;
@@ -66,8 +67,6 @@ namespace Microsoft.Kiota.Http.HttpClientLibrary.Tests;
6667
public sealed class HttpClientRequestAdapterObservabilityTests : IDisposable
6768
{
6869
private readonly HttpClientRequestAdapter _requestAdapter;
69-
private readonly ActivityListener _activityListener;
70-
private readonly List<Activity> _capturedActivities;
7170

7271
public HttpClientRequestAdapterObservabilityTests()
7372
{
@@ -77,30 +76,34 @@ public HttpClientRequestAdapterObservabilityTests()
7776

7877
var authProvider = new AnonymousAuthenticationProvider();
7978
var observabilityOptions = new ObservabilityOptions();
80-
_requestAdapter = new HttpClientRequestAdapter(authProvider, observabilityOptions: observabilityOptions);
81-
_requestAdapter.BaseUrl = "https://graph.microsoft.com/v1.0";
82-
83-
// Setup activity listener to capture activities from all Kiota sources
84-
_capturedActivities = new List<Activity>();
85-
_activityListener = new ActivityListener
79+
_requestAdapter = new HttpClientRequestAdapter(authProvider, observabilityOptions: observabilityOptions)
8680
{
87-
ShouldListenTo = source => source.Name.StartsWith("Microsoft.Kiota", StringComparison.OrdinalIgnoreCase),
88-
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllDataAndRecorded,
89-
ActivityStarted = activity => _capturedActivities.Add(activity),
90-
ActivityStopped = activity => { } // Capture stopped activities to ensure tags are set
81+
BaseUrl = "https://graph.microsoft.com/v1.0"
9182
};
92-
ActivitySource.AddActivityListener(_activityListener);
9383
}
9484

9585
public void Dispose()
9686
{
97-
_activityListener?.Dispose();
9887
_requestAdapter?.Dispose();
9988
}
10089

101-
private KeyValuePair<string, string?> GetTagFromActivities(string tagKey, ActivityTraceId traceId)
90+
private static (ActivityListener Listener, ConcurrentQueue<Activity> CapturedActivities) CreateActivityCapture()
10291
{
103-
return _capturedActivities
92+
var capturedActivities = new ConcurrentQueue<Activity>();
93+
var activityListener = new ActivityListener
94+
{
95+
ShouldListenTo = source => source.Name.StartsWith("Microsoft.Kiota", StringComparison.OrdinalIgnoreCase),
96+
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllDataAndRecorded,
97+
ActivityStarted = activity => capturedActivities.Enqueue(activity),
98+
ActivityStopped = activity => { } // Capture stopped activities to ensure tags are set
99+
};
100+
ActivitySource.AddActivityListener(activityListener);
101+
return (activityListener, capturedActivities);
102+
}
103+
104+
private static KeyValuePair<string, string?> GetTagFromActivities(IEnumerable<Activity> capturedActivities, string tagKey, ActivityTraceId traceId)
105+
{
106+
return capturedActivities
104107
.Where(a => a.TraceId == traceId)
105108
.SelectMany(a => a.Tags)
106109
.FirstOrDefault(t => t.Key == tagKey);
@@ -162,6 +165,8 @@ public void GetNormalizedHttpRoute_NormalizesCorrectly(string input, string expe
162165
public async Task SendAsync_CreatesActivityWithHttpRouteTag(string urlTemplate, string? pathParamKey, string? pathParamValue, string expectedRoute)
163166
{
164167
// Arrange
168+
var (activityListener, capturedActivities) = CreateActivityCapture();
169+
using var listener = activityListener;
165170
var mockHandler = new Mock<HttpMessageHandler>();
166171
mockHandler.Protected()
167172
.Setup<Task<HttpResponseMessage>>("SendAsync",
@@ -185,15 +190,13 @@ public async Task SendAsync_CreatesActivityWithHttpRouteTag(string urlTemplate,
185190
pathParameters.Add(pathParamKey, pathParamValue);
186191
}
187192
var requestInfo = new RequestInformation(Method.GET, urlTemplate, pathParameters);
188-
// Clear any previously captured activities
189-
_capturedActivities.Clear();
190193
using var testRoot = StartTestRootActivity();
191194

192195
// Act
193196
await adapter.SendAsync<MockEntity>(requestInfo, MockEntity.Factory, cancellationToken: TestContext.Current.CancellationToken);
194197

195198
// Assert
196-
var activity = _capturedActivities.FirstOrDefault(a => a.TraceId == testRoot.TraceId && a.OperationName.Contains("SendAsync"));
199+
var activity = capturedActivities.FirstOrDefault(a => a.TraceId == testRoot.TraceId && a.OperationName.Contains("SendAsync"));
197200
Assert.NotNull(activity);
198201

199202
var httpRouteTag = activity.Tags.FirstOrDefault(t => t.Key == "http.route");
@@ -216,6 +219,8 @@ public async Task SendAsync_CreatesActivityWithHttpRouteTag(string urlTemplate,
216219
public async Task SendAsync_CreatesActivityWithUriTemplateTag(string urlTemplate, string? pathParamKey, string? pathParamValue, string expectedSubstring)
217220
{
218221
// Arrange
222+
var (activityListener, capturedActivities) = CreateActivityCapture();
223+
using var listener = activityListener;
219224
var mockHandler = new Mock<HttpMessageHandler>();
220225
mockHandler.Protected()
221226
.Setup<Task<HttpResponseMessage>>("SendAsync",
@@ -239,16 +244,13 @@ public async Task SendAsync_CreatesActivityWithUriTemplateTag(string urlTemplate
239244
pathParameters.Add(pathParamKey, pathParamValue);
240245
}
241246
var requestInfo = new RequestInformation(Method.GET, urlTemplate, pathParameters);
242-
243-
// Clear any previously captured activities
244-
_capturedActivities.Clear();
245247
using var testRoot = StartTestRootActivity();
246248

247249
// Act
248250
await adapter.SendAsync<MockEntity>(requestInfo, MockEntity.Factory, cancellationToken: TestContext.Current.CancellationToken);
249251

250252
// Assert
251-
var activity = _capturedActivities.FirstOrDefault(a => a.TraceId == testRoot.TraceId && a.OperationName.Contains("SendAsync"));
253+
var activity = capturedActivities.FirstOrDefault(a => a.TraceId == testRoot.TraceId && a.OperationName.Contains("SendAsync"));
252254
Assert.NotNull(activity);
253255

254256
var uriTemplateTag = activity.Tags.FirstOrDefault(t => t.Key == "url.uri_template");
@@ -267,6 +269,8 @@ public async Task SendAsync_CreatesActivityWithUriTemplateTag(string urlTemplate
267269
public async Task SendAsync_SetsHttpRequestMethodTag(Method httpMethod, string expectedMethodTag)
268270
{
269271
// Arrange
272+
var (activityListener, capturedActivities) = CreateActivityCapture();
273+
using var listener = activityListener;
270274
var mockHandler = new Mock<HttpMessageHandler>();
271275
mockHandler.Protected()
272276
.Setup<Task<HttpResponseMessage>>("SendAsync",
@@ -290,17 +294,16 @@ public async Task SendAsync_SetsHttpRequestMethodTag(Method httpMethod, string e
290294
UrlTemplate = "{+baseurl}/users"
291295
};
292296

293-
_capturedActivities.Clear();
294297
using var testRoot = StartTestRootActivity();
295298

296299
// Act
297300
await adapter.SendAsync<MockEntity>(requestInfo, MockEntity.Factory, cancellationToken: TestContext.Current.CancellationToken);
298301

299302
// Assert
300-
var testActivities = _capturedActivities.Where(a => a.TraceId == testRoot.TraceId).ToList();
303+
var testActivities = capturedActivities.Where(a => a.TraceId == testRoot.TraceId).ToList();
301304
Assert.NotEmpty(testActivities);
302305

303-
var methodTag = GetTagFromActivities("http.request.method", testRoot.TraceId);
306+
var methodTag = GetTagFromActivities(capturedActivities, "http.request.method", testRoot.TraceId);
304307
Assert.NotNull(methodTag.Key);
305308
Assert.Equal(expectedMethodTag, methodTag.Value);
306309
}
@@ -309,6 +312,8 @@ public async Task SendAsync_SetsHttpRequestMethodTag(Method httpMethod, string e
309312
public async Task SendAsync_SetsUrlSchemeAndServerAddressTags()
310313
{
311314
// Arrange
315+
var (activityListener, capturedActivities) = CreateActivityCapture();
316+
using var listener = activityListener;
312317
var mockHandler = new Mock<HttpMessageHandler>();
313318
mockHandler.Protected()
314319
.Setup<Task<HttpResponseMessage>>("SendAsync",
@@ -332,27 +337,28 @@ public async Task SendAsync_SetsUrlSchemeAndServerAddressTags()
332337
UrlTemplate = "{+baseurl}/users"
333338
};
334339

335-
_capturedActivities.Clear();
336340
using var testRoot = StartTestRootActivity();
337341

338342
// Act
339343
await adapter.SendAsync<MockEntity>(requestInfo, MockEntity.Factory, cancellationToken: TestContext.Current.CancellationToken);
340344

341345
// Assert
342-
var testActivities = _capturedActivities.Where(a => a.TraceId == testRoot.TraceId).ToList();
346+
var testActivities = capturedActivities.Where(a => a.TraceId == testRoot.TraceId).ToList();
343347
Assert.NotEmpty(testActivities);
344348

345-
var schemeTag = GetTagFromActivities("url.scheme", testRoot.TraceId);
349+
var schemeTag = GetTagFromActivities(capturedActivities, "url.scheme", testRoot.TraceId);
346350
Assert.Equal("https", schemeTag.Value);
347351

348-
var serverTag = GetTagFromActivities("server.address", testRoot.TraceId);
352+
var serverTag = GetTagFromActivities(capturedActivities, "server.address", testRoot.TraceId);
349353
Assert.Equal("graph.microsoft.com", serverTag.Value);
350354
}
351355

352356
[Fact]
353357
public async Task SendAsync_WithoutIncludeEUIIAttributes_DoesNotSetUrlFullTag()
354358
{
355359
// Arrange
360+
var (activityListener, capturedActivities) = CreateActivityCapture();
361+
using var listener = activityListener;
356362
var mockHandler = new Mock<HttpMessageHandler>();
357363
mockHandler.Protected()
358364
.Setup<Task<HttpResponseMessage>>("SendAsync",
@@ -376,24 +382,25 @@ public async Task SendAsync_WithoutIncludeEUIIAttributes_DoesNotSetUrlFullTag()
376382
UrlTemplate = "{+baseurl}/users"
377383
};
378384

379-
_capturedActivities.Clear();
380385
using var testRoot = StartTestRootActivity();
381386

382387
// Act
383388
await adapter.SendAsync<MockEntity>(requestInfo, MockEntity.Factory, cancellationToken: TestContext.Current.CancellationToken);
384389

385390
// Assert
386-
var testActivities = _capturedActivities.Where(a => a.TraceId == testRoot.TraceId).ToList();
391+
var testActivities = capturedActivities.Where(a => a.TraceId == testRoot.TraceId).ToList();
387392
Assert.NotEmpty(testActivities);
388393

389-
var urlFullTag = GetTagFromActivities("url.full", testRoot.TraceId);
394+
var urlFullTag = GetTagFromActivities(capturedActivities, "url.full", testRoot.TraceId);
390395
Assert.Null(urlFullTag.Key);
391396
}
392397

393398
[Fact]
394399
public async Task SendAsync_CreatesNestedActivitySpans()
395400
{
396401
// Arrange
402+
var (activityListener, capturedActivities) = CreateActivityCapture();
403+
using var listener = activityListener;
397404
var mockHandler = new Mock<HttpMessageHandler>();
398405
mockHandler.Protected()
399406
.Setup<Task<HttpResponseMessage>>("SendAsync",
@@ -417,15 +424,14 @@ public async Task SendAsync_CreatesNestedActivitySpans()
417424
UrlTemplate = "{+baseurl}/users"
418425
};
419426

420-
_capturedActivities.Clear();
421427
using var testRoot = StartTestRootActivity();
422428

423429
// Act
424430
await adapter.SendAsync<MockEntity>(requestInfo, MockEntity.Factory, cancellationToken: TestContext.Current.CancellationToken);
425431

426432
// Assert - Verify various nested spans are created
427433
var resultingActivities = new HashSet<string>(
428-
_capturedActivities.ToList().Where(a => a.TraceId == testRoot.TraceId).Select(static a => a.OperationName),
434+
capturedActivities.Where(a => a.TraceId == testRoot.TraceId).Select(static a => a.OperationName),
429435
StringComparer.Ordinal);
430436
Assert.Contains("SendAsync - {+baseurl}/users", resultingActivities);
431437
Assert.Contains("GetHttpResponseMessageAsync", resultingActivities);

0 commit comments

Comments
 (0)