Skip to content

Commit 3a6d50d

Browse files
[OpenTelemetry] Extend test coverage
Extend test coverage for `TraceContextPropagator`.
1 parent 496807c commit 3a6d50d

1 file changed

Lines changed: 142 additions & 20 deletions

File tree

test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs

Lines changed: 142 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
using System.Collections;
45
using System.Diagnostics;
56
using Xunit;
67

@@ -14,25 +15,11 @@ public class TraceContextPropagatorTests
1415
private const string SpanId = "b9c7c989f97918e1";
1516

1617
private static readonly string[] Empty = [];
17-
private static readonly Func<IDictionary<string, string>, string, IEnumerable<string>> Getter = (headers, name) =>
18-
{
19-
if (headers.TryGetValue(name, out var value))
20-
{
21-
return [value];
22-
}
23-
24-
return Empty;
25-
};
18+
private static readonly Func<IDictionary<string, string>, string, IEnumerable<string>> Getter =
19+
static (headers, name) => headers.TryGetValue(name, out var value) ? [value] : (IEnumerable<string>)Empty;
2620

27-
private static readonly Func<IDictionary<string, string[]>, string, IEnumerable<string>> ArrayGetter = (headers, name) =>
28-
{
29-
if (headers.TryGetValue(name, out var value))
30-
{
31-
return value;
32-
}
33-
34-
return [];
35-
};
21+
private static readonly Func<IDictionary<string, string[]>, string, IEnumerable<string>> ArrayGetter =
22+
static (headers, name) => headers.TryGetValue(name, out var value) ? value : (IEnumerable<string>)[];
3623

3724
private static readonly Action<IDictionary<string, string>, string, string> Setter = (carrier, name, value) =>
3825
{
@@ -56,7 +43,7 @@ public void CanParseExampleFromSpec()
5643

5744
Assert.True(ctx.ActivityContext.IsRemote);
5845
Assert.True(ctx.ActivityContext.IsValid());
59-
Assert.True((ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0);
46+
Assert.NotEqual(0, (int)(ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded));
6047

6148
Assert.Equal($"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01", ctx.ActivityContext.TraceState);
6249
}
@@ -74,7 +61,7 @@ public void NotSampled()
7461

7562
Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId);
7663
Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId);
77-
Assert.True((ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) == 0);
64+
Assert.Equal(0, (int)(ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded));
7865

7966
Assert.True(ctx.ActivityContext.IsRemote);
8067
Assert.True(ctx.ActivityContext.IsValid());
@@ -138,6 +125,111 @@ public void TracestateToString()
138125
Assert.Equal("k1=v1,k2=v2,k3=v3", ctx.ActivityContext.TraceState);
139126
}
140127

128+
[Fact]
129+
public void Extract_SupportsReadOnlyListCarrierValues()
130+
{
131+
var headers = new Dictionary<string, ReadOnlyCarrierValues>
132+
{
133+
[TraceParent] = new([$"00-{TraceId}-{SpanId}-01"]),
134+
[TraceState] = new(["k1=v1"]),
135+
};
136+
137+
var target = new TraceContextPropagator();
138+
var actual = target.Extract(default, headers, static (carrier, name) =>
139+
carrier.TryGetValue(name, out var value) ? value : new ReadOnlyCarrierValues([]));
140+
141+
Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), actual.ActivityContext.TraceId);
142+
Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), actual.ActivityContext.SpanId);
143+
Assert.Equal("k1=v1", actual.ActivityContext.TraceState);
144+
}
145+
146+
[Fact]
147+
public void Extract_SupportsEnumerableCarrierValues()
148+
{
149+
var headers = new Dictionary<string, EnumerableCarrierValues>
150+
{
151+
[TraceParent] = new([$"00-{TraceId}-{SpanId}-01"]),
152+
[TraceState] = new([" k1=v1 , k2=v2 "]),
153+
};
154+
155+
var target = new TraceContextPropagator();
156+
var actual = target.Extract(default, headers, static (carrier, name) =>
157+
carrier.TryGetValue(name, out var value) ? value : new EnumerableCarrierValues([]));
158+
159+
Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), actual.ActivityContext.TraceId);
160+
Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), actual.ActivityContext.SpanId);
161+
Assert.Equal("k1=v1,k2=v2", actual.ActivityContext.TraceState);
162+
}
163+
164+
[Fact]
165+
public void Extract_IgnoresMultipleEnumerableTraceparentValues()
166+
{
167+
var headers = new Dictionary<string, EnumerableCarrierValues>
168+
{
169+
[TraceParent] = new([$"00-{TraceId}-{SpanId}-01", $"00-{TraceId}-{SpanId}-00"]),
170+
};
171+
172+
var target = new TraceContextPropagator();
173+
var context = target.Extract(default, headers, static (carrier, name) =>
174+
carrier.TryGetValue(name, out var value) ? value : new EnumerableCarrierValues([]));
175+
176+
Assert.False(context.ActivityContext.IsValid());
177+
}
178+
179+
[Fact]
180+
public void Extract_IgnoresEmptyEnumerableTracestateValues()
181+
{
182+
var headers = new Dictionary<string, EnumerableCarrierValues>
183+
{
184+
[TraceParent] = new([$"00-{TraceId}-{SpanId}-01"]),
185+
[TraceState] = new([]),
186+
};
187+
188+
var target = new TraceContextPropagator();
189+
var context = target.Extract(default, headers, static (carrier, name) =>
190+
carrier.TryGetValue(name, out var value) ? value : new EnumerableCarrierValues([]));
191+
192+
Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId);
193+
Assert.Null(context.ActivityContext.TraceState);
194+
}
195+
196+
[Fact]
197+
public void TryExtractTracestate_SingleHeaderReturnsOriginalString()
198+
{
199+
Assert.True(TraceContextPropagator.TryExtractTracestate(["k1=v1,k2=v2"], out var actual));
200+
Assert.Equal("k1=v1,k2=v2", actual);
201+
}
202+
203+
[Fact]
204+
public void TryExtractTracestate_SingleHeaderReturnsEmptyForWhitespaceOnly()
205+
{
206+
Assert.True(TraceContextPropagator.TryExtractTracestate([" , "], out var actual));
207+
Assert.Empty(actual);
208+
}
209+
210+
[Fact]
211+
public void TryExtractTracestate_SingleHeaderRejectsTooManyMembers()
212+
{
213+
var tracestate = string.Join(",", Enumerable.Range(1, 33).Select(static i => $"k{i:D2}=v{i:D2}"));
214+
215+
Assert.False(TraceContextPropagator.TryExtractTracestate([tracestate], out _));
216+
}
217+
218+
[Fact]
219+
public void TryExtractTracestate_SingleHeaderRejectsDuplicateLongKeys()
220+
{
221+
var key = new string('a', 33);
222+
223+
Assert.False(TraceContextPropagator.TryExtractTracestate([$"{key}=1,{key}=2"], out _));
224+
}
225+
226+
[Fact]
227+
public void TryExtractTracestate_NullCollectionReturnsEmpty()
228+
{
229+
Assert.True(TraceContextPropagator.TryExtractTracestate((IEnumerable<string>)null!, out var actual));
230+
Assert.Empty(actual);
231+
}
232+
141233
[Fact]
142234
public void Inject_NoTracestate()
143235
{
@@ -333,4 +425,34 @@ private static string CallTraceContextPropagator(string[] tracestate)
333425
Assert.NotNull(traceState);
334426
return traceState;
335427
}
428+
429+
private sealed class ReadOnlyCarrierValues(params string[] values) : IReadOnlyList<string>
430+
{
431+
public int Count => values.Length;
432+
433+
public string this[int index] => values[index];
434+
435+
public IEnumerator<string> GetEnumerator()
436+
{
437+
foreach (var value in values)
438+
{
439+
yield return value;
440+
}
441+
}
442+
443+
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
444+
}
445+
446+
private sealed class EnumerableCarrierValues(params string[] values) : IEnumerable<string>
447+
{
448+
public IEnumerator<string> GetEnumerator()
449+
{
450+
foreach (var value in values)
451+
{
452+
yield return value;
453+
}
454+
}
455+
456+
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
457+
}
336458
}

0 commit comments

Comments
 (0)