330 lines
13 KiB
C#
330 lines
13 KiB
C#
// <copyright file="TraceContextPropagatorTest.cs" company="OpenTelemetry Authors">
|
|
// Copyright The OpenTelemetry Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// </copyright>
|
|
using System.Diagnostics;
|
|
using Xunit;
|
|
|
|
namespace OpenTelemetry.Context.Propagation.Tests
|
|
{
|
|
public class TraceContextPropagatorTest
|
|
{
|
|
private const string TraceParent = "traceparent";
|
|
private const string TraceState = "tracestate";
|
|
private const string TraceId = "0af7651916cd43dd8448eb211c80319c";
|
|
private const string SpanId = "b9c7c989f97918e1";
|
|
|
|
private static readonly string[] Empty = Array.Empty<string>();
|
|
private static readonly Func<IDictionary<string, string>, string, IEnumerable<string>> Getter = (headers, name) =>
|
|
{
|
|
if (headers.TryGetValue(name, out var value))
|
|
{
|
|
return new[] { value };
|
|
}
|
|
|
|
return Empty;
|
|
};
|
|
|
|
private static readonly Func<IDictionary<string, string[]>, string, IEnumerable<string>> ArrayGetter = (headers, name) =>
|
|
{
|
|
if (headers.TryGetValue(name, out var value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
return Array.Empty<string>();
|
|
};
|
|
|
|
private static readonly Action<IDictionary<string, string>, string, string> Setter = (carrier, name, value) =>
|
|
{
|
|
carrier[name] = value;
|
|
};
|
|
|
|
[Fact]
|
|
public void CanParseExampleFromSpec()
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{TraceId}-{SpanId}-01" },
|
|
{ TraceState, $"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01" },
|
|
};
|
|
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
|
|
Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId);
|
|
Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId);
|
|
|
|
Assert.True(ctx.ActivityContext.IsRemote);
|
|
Assert.True(ctx.ActivityContext.IsValid());
|
|
Assert.True((ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0);
|
|
|
|
Assert.Equal($"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01", ctx.ActivityContext.TraceState);
|
|
}
|
|
|
|
[Fact]
|
|
public void NotSampled()
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{TraceId}-{SpanId}-00" },
|
|
};
|
|
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
|
|
Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId);
|
|
Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId);
|
|
Assert.True((ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) == 0);
|
|
|
|
Assert.True(ctx.ActivityContext.IsRemote);
|
|
Assert.True(ctx.ActivityContext.IsValid());
|
|
}
|
|
|
|
[Fact]
|
|
public void IsBlankIfNoHeader()
|
|
{
|
|
var headers = new Dictionary<string, string>();
|
|
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
|
|
Assert.False(ctx.ActivityContext.IsValid());
|
|
}
|
|
|
|
[Fact]
|
|
public void IsBlankIfInvalid()
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-xyz7651916cd43dd8448eb211c80319c-{SpanId}-01" },
|
|
};
|
|
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
|
|
Assert.False(ctx.ActivityContext.IsValid());
|
|
}
|
|
|
|
[Fact]
|
|
public void TracestateToStringEmpty()
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{TraceId}-{SpanId}-01" },
|
|
};
|
|
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
|
|
Assert.Null(ctx.ActivityContext.TraceState);
|
|
}
|
|
|
|
[Fact]
|
|
public void TracestateToString()
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{TraceId}-{SpanId}-01" },
|
|
{ TraceState, "k1=v1,k2=v2,k3=v3" },
|
|
};
|
|
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
|
|
Assert.Equal("k1=v1,k2=v2,k3=v3", ctx.ActivityContext.TraceState);
|
|
}
|
|
|
|
[Fact]
|
|
public void Inject_NoTracestate()
|
|
{
|
|
var traceId = ActivityTraceId.CreateRandom();
|
|
var spanId = ActivitySpanId.CreateRandom();
|
|
var expectedHeaders = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{traceId}-{spanId}-01" },
|
|
};
|
|
|
|
var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, traceState: null);
|
|
PropagationContext propagationContext = new PropagationContext(activityContext, default);
|
|
var carrier = new Dictionary<string, string>();
|
|
var f = new TraceContextPropagator();
|
|
f.Inject(propagationContext, carrier, Setter);
|
|
|
|
Assert.Equal(expectedHeaders, carrier);
|
|
}
|
|
|
|
[Fact]
|
|
public void Inject_WithTracestate()
|
|
{
|
|
var traceId = ActivityTraceId.CreateRandom();
|
|
var spanId = ActivitySpanId.CreateRandom();
|
|
var expectedHeaders = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{traceId}-{spanId}-01" },
|
|
{ TraceState, $"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{traceId}-00f067aa0ba902b7-01" },
|
|
};
|
|
|
|
var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, expectedHeaders[TraceState]);
|
|
PropagationContext propagationContext = new PropagationContext(activityContext, default);
|
|
var carrier = new Dictionary<string, string>();
|
|
var f = new TraceContextPropagator();
|
|
f.Inject(propagationContext, carrier, Setter);
|
|
|
|
Assert.Equal(expectedHeaders, carrier);
|
|
}
|
|
|
|
[Fact]
|
|
public void DuplicateKeys()
|
|
{
|
|
// test_tracestate_duplicated_keys
|
|
Assert.Empty(CallTraceContextPropagator("foo=1,foo=1"));
|
|
Assert.Empty(CallTraceContextPropagator("foo=1,foo=2"));
|
|
Assert.Empty(CallTraceContextPropagator(new string[] { "foo=1", "foo=1" }));
|
|
Assert.Empty(CallTraceContextPropagator(new string[] { "foo=1", "foo=2" }));
|
|
}
|
|
|
|
[Fact]
|
|
public void Key_IllegalCharacters()
|
|
{
|
|
// test_tracestate_key_illegal_characters
|
|
Assert.Empty(CallTraceContextPropagator("foo =1"));
|
|
Assert.Empty(CallTraceContextPropagator("FOO =1"));
|
|
Assert.Empty(CallTraceContextPropagator("foo.bar=1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Key_IllegalVendorFormat()
|
|
{
|
|
// test_tracestate_key_illegal_vendor_format
|
|
Assert.Empty(CallTraceContextPropagator("foo@=1,bar=2"));
|
|
Assert.Empty(CallTraceContextPropagator("@foo=1,bar=2"));
|
|
Assert.Empty(CallTraceContextPropagator("foo@@bar=1,bar=2"));
|
|
Assert.Empty(CallTraceContextPropagator("foo@bar@baz=1,bar=2"));
|
|
}
|
|
|
|
[Fact]
|
|
public void MemberCountLimit()
|
|
{
|
|
// test_tracestate_member_count_limit
|
|
var output1 = CallTraceContextPropagator(new string[]
|
|
{
|
|
"bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10",
|
|
"bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20",
|
|
"bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30",
|
|
"bar31=31,bar32=32",
|
|
});
|
|
var expected =
|
|
"bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10" + "," +
|
|
"bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20" + "," +
|
|
"bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30" + "," +
|
|
"bar31=31,bar32=32";
|
|
Assert.Equal(expected, output1);
|
|
|
|
var output2 = CallTraceContextPropagator(new string[]
|
|
{
|
|
"bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10",
|
|
"bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20",
|
|
"bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30",
|
|
"bar31=31,bar32=32,bar33=33",
|
|
});
|
|
Assert.Empty(output2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Key_KeyLengthLimit()
|
|
{
|
|
// test_tracestate_key_length_limit
|
|
string input1 = new string('z', 256) + "=1";
|
|
Assert.Equal(input1, CallTraceContextPropagator(input1));
|
|
Assert.Empty(CallTraceContextPropagator(new string('z', 257) + "=1"));
|
|
string input2 = new string('t', 241) + "@" + new string('v', 14) + "=1";
|
|
Assert.Equal(input2, CallTraceContextPropagator(input2));
|
|
Assert.Empty(CallTraceContextPropagator(new string('t', 242) + "@v=1"));
|
|
Assert.Empty(CallTraceContextPropagator("t@" + new string('v', 15) + "=1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Value_IllegalCharacters()
|
|
{
|
|
// test_tracestate_value_illegal_characters
|
|
Assert.Empty(CallTraceContextPropagator("foo=bar=baz"));
|
|
Assert.Empty(CallTraceContextPropagator("foo=,bar=3"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Traceparent_Version()
|
|
{
|
|
// test_traceparent_version_0x00
|
|
Assert.NotEqual(
|
|
"12345678901234567890123456789012",
|
|
CallTraceContextPropagatorWithTraceParent("00-12345678901234567890123456789012-1234567890123456-01."));
|
|
Assert.NotEqual(
|
|
"12345678901234567890123456789012",
|
|
CallTraceContextPropagatorWithTraceParent("00-12345678901234567890123456789012-1234567890123456-01-what-the-future-will-be-like"));
|
|
|
|
// test_traceparent_version_0xcc
|
|
Assert.Equal(
|
|
"12345678901234567890123456789012",
|
|
CallTraceContextPropagatorWithTraceParent("cc-12345678901234567890123456789012-1234567890123456-01"));
|
|
Assert.Equal(
|
|
"12345678901234567890123456789012",
|
|
CallTraceContextPropagatorWithTraceParent("cc-12345678901234567890123456789012-1234567890123456-01-what-the-future-will-be-like"));
|
|
Assert.NotEqual(
|
|
"12345678901234567890123456789012",
|
|
CallTraceContextPropagatorWithTraceParent("cc-12345678901234567890123456789012-1234567890123456-01.what-the-future-will-be-like"));
|
|
|
|
// test_traceparent_version_0xff
|
|
Assert.NotEqual(
|
|
"12345678901234567890123456789012",
|
|
CallTraceContextPropagatorWithTraceParent("ff-12345678901234567890123456789012-1234567890123456-01"));
|
|
}
|
|
|
|
private static string CallTraceContextPropagatorWithTraceParent(string traceparent)
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, traceparent },
|
|
};
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
return ctx.ActivityContext.TraceId.ToString();
|
|
}
|
|
|
|
private static string CallTraceContextPropagator(string tracestate)
|
|
{
|
|
var headers = new Dictionary<string, string>
|
|
{
|
|
{ TraceParent, $"00-{TraceId}-{SpanId}-01" },
|
|
{ TraceState, tracestate },
|
|
};
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, Getter);
|
|
return ctx.ActivityContext.TraceState;
|
|
}
|
|
|
|
private static string CallTraceContextPropagator(string[] tracestate)
|
|
{
|
|
var headers = new Dictionary<string, string[]>
|
|
{
|
|
{ TraceParent, new string[] { $"00-{TraceId}-{SpanId}-01" } },
|
|
{ TraceState, tracestate },
|
|
};
|
|
var f = new TraceContextPropagator();
|
|
var ctx = f.Extract(default, headers, ArrayGetter);
|
|
return ctx.ActivityContext.TraceState;
|
|
}
|
|
}
|
|
}
|