private static W3CTracestate TryGetTracestateFromHeaders <T>(T carrier, Func <T, string, IEnumerable <string> > getter, string trustedAccountKey, IList <IngestErrorType> errors)
        {
            var result = getter(carrier, "tracestate");

            if (result == null || result.Count() == 0)
            {
                return(null);
            }

            var tracestate = W3CTracestate.GetW3CTracestateFromHeaders(result, trustedAccountKey);

            if (tracestate.Error != IngestErrorType.None)
            {
                errors.Add(tracestate.Error);
            }

            return(tracestate);
        }
        public void GetW3CTracestateFromHeaders_DuplicateNRKeysInDifferentHeaders_AcceptLastOne_Test()
        {
            var testHeaders = new List <string>()
            {
                "33@nr=0-0-33-5043-27ddd2d8890283b4-5569065a5b1313bd-1-1.23456-1518469636025",
                "33@nr=0-0-55-5043-1238890283aasdfs-4569065a5b131bbg-1-1.23456-1518469636020",
            };

            var trustedAccountKey = "33";

            var tracestate = W3CTracestate.GetW3CTracestateFromHeaders(testHeaders, trustedAccountKey);

            Assert.AreEqual(tracestate.Version, 0);
            Assert.AreEqual((int)tracestate.ParentType, 0);
            Assert.AreEqual(tracestate.AccountId, "55");
            Assert.AreEqual(tracestate.AppId, "5043");
            Assert.AreEqual(tracestate.SpanId, "1238890283aasdfs");
            Assert.AreEqual(tracestate.TransactionId, "4569065a5b131bbg");
            Assert.AreEqual(tracestate.Sampled, 1);
            Assert.AreEqual(tracestate.Priority, 1.23456f);
            Assert.AreEqual(tracestate.Timestamp, 1518469636020);

            Assert.That(tracestate.VendorstateEntries.Count == 0, Is.True);
        }
        public void GetW3CTracestateFromHeaders_ValidTracestateString_Tests(string headerString, string trustedAccountKey)
        {
            var testHeaders = new List <string>()
            {
                headerString
            };

            var tracestate = W3CTracestate.GetW3CTracestateFromHeaders(testHeaders, trustedAccountKey);

            Assert.AreEqual(tracestate.Version, 0);
            Assert.AreEqual((int)tracestate.ParentType, 0);
            Assert.AreEqual(tracestate.AccountId, "33");
            Assert.AreEqual(tracestate.AppId, "5043");
            Assert.AreEqual(tracestate.SpanId, "27ddd2d8890283b4");
            Assert.AreEqual(tracestate.TransactionId, "5569065a5b1313bd");
            Assert.AreEqual(tracestate.Sampled, 1);
            Assert.AreEqual(tracestate.Priority, 1.23456f);
            Assert.AreEqual(tracestate.Timestamp, 1518469636025);

            Assert.That(tracestate.VendorstateEntries.Count == 2, Is.True);
            Assert.That(tracestate.VendorstateEntries.Contains("dd=YzRiMTIxODk1NmVmZTE4ZQ"), Is.True);
            Assert.That(tracestate.VendorstateEntries.Contains("44@nr=0-0-55-5043-1238890283aasdfs-4569065a5b131bbg-1-1.23456-1518469636020"), Is.True);
            Assert.That(tracestate.VendorstateEntries.Contains($"{trustedAccountKey}@nr"), Is.False);
        }
        public void GetW3CTracestateFromHeaders_NewRelicTracestateEntry_Tests(string headerString, string trustedAccountKey,
                                                                              string expectedOtherVendors, int?expectedSampled, float?expectedPriority, IngestErrorType expectedIngestError)
        {
            var testHeaders = new List <string>()
            {
                headerString
            };

            var tracestate = W3CTracestate.GetW3CTracestateFromHeaders(testHeaders, trustedAccountKey);

            if (expectedIngestError == IngestErrorType.None)
            {
                Assert.That(tracestate, Is.Not.Null);
            }
            else if (expectedOtherVendors != null)
            {
                Assert.That(tracestate.VendorstateEntries.Count > 0);
                Assert.AreEqual(expectedOtherVendors, string.Join(",", tracestate.VendorstateEntries));
            }

            Assert.That(tracestate.Priority == expectedPriority, $@"Expects Priority {expectedPriority} but gets Priority {tracestate.Priority } instead.");
            Assert.That(tracestate.Sampled == expectedSampled, $@"Expects Sampled {expectedSampled} but gets Sampled {tracestate.Sampled } instead.");
            Assert.That(expectedIngestError == tracestate.Error, $@"Expects Error {expectedIngestError} but gets Error {tracestate.Error } instead.");
        }
        private void ValidateOutboundHeaders(OutboundPayloadSettings payloadSettings, Dictionary <string, string> actualOutboundHeaders, string trustedAccountKey)
        {
            JObject newrelicHeaderValue = null;
            JObject newrelicJson        = null;
            JObject traceparentJson     = null;
            JObject tracestateJson      = null;

            if (actualOutboundHeaders.ContainsKey("newrelic"))
            {
                newrelicHeaderValue = JObject.Parse(Strings.Base64Decode(actualOutboundHeaders["newrelic"]));

                newrelicJson = new JObject
                {
                    { "newrelic", newrelicHeaderValue }
                };
            }
            if (actualOutboundHeaders.ContainsKey("traceparent"))
            {
                var fields = actualOutboundHeaders["traceparent"].Split('-');
                traceparentJson = new JObject
                {
                    { "traceparent", new JObject
                      {
                          { "version", fields[0] },
                          { "trace_id", fields[1] },
                          { "parent_id", fields[2] },
                          { "trace_flags", fields[3] }
                      } }
                };
            }
            if (actualOutboundHeaders.ContainsKey("tracestate"))
            {
                var tracestate  = W3CTracestate.GetW3CTracestateFromHeaders(new string[] { actualOutboundHeaders["tracestate"] }, trustedAccountKey);
                var headerValue = actualOutboundHeaders["tracestate"];
                var tenantId    = headerValue.Substring(0, headerValue.IndexOf('@'));
                tracestateJson = new JObject
                {
                    { "tracestate", new JObject
                      {
                          { "version", tracestate.Version },
                          { "parent_type", (int)tracestate.ParentType },
                          { "parent_account_id", tracestate.AccountId },
                          { "parent_application_id", tracestate.AppId },
                          { "span_id", tracestate.SpanId },
                          { "transaction_id", tracestate.TransactionId },
                          { "sampled", tracestate.Sampled },
                          { "priority", string.Format("{0:0.######}", tracestate.Priority) },  // cheating here: priority is stored as float, which may show in scientific notation; this formatting is performed when creating a new tracestate header in the agent so it will not be transmitted in scientific notation
                          { "timestamp", tracestate.Timestamp },
                          { "tenant_id", tenantId },
                          { "vendors", new JArray(tracestate.VendorstateEntries.Select(vse => vse.Split('=')[0]).ToList()) }
                      } }
                };
            }

            JToken actualValue = null;

            var exactFields = payloadSettings.Exact;

            if (exactFields != null)
            {
                foreach (var key in exactFields.Keys)
                {
                    var expectedValue = exactFields[key];

                    switch (key.Substring(0, key.IndexOf('.')))
                    {
                    case "newrelic":
                        actualValue = newrelicJson.SelectToken(key);
                        break;

                    case "traceparent":
                        actualValue = traceparentJson.SelectToken(key);
                        break;

                    case "tracestate":
                        actualValue = tracestateJson.SelectToken(key);
                        break;

                    default:
                        break;
                    }

                    Assert.That(actualValue.IsEqualTo(expectedValue), $"{key}, expected: {expectedValue}, actual: {actualValue}");
                }
            }

            payloadSettings.Expected?.ForEach(expected =>
            {
                switch (expected.Substring(0, expected.IndexOf('.')))
                {
                case "newrelic":
                    Assert.That(newrelicJson.SelectToken(expected), Is.Not.Null, $"Missing expected: {expected}");
                    break;

                case "traceparent":
                    Assert.That(traceparentJson.SelectToken(expected), Is.Not.Null, $"Missing expected: {expected}");
                    break;

                case "tracestate":
                    Assert.That(tracestateJson.SelectToken(expected), Is.Not.Null, $"Missing expected: {expected}");
                    break;

                default:
                    break;
                }
            });

            payloadSettings.Unexpected?.ForEach(unexpected =>
            {
                switch (unexpected.Substring(0, unexpected.IndexOf('.')))
                {
                case "newrelic":
                    Assert.That(newrelicJson.SelectToken(unexpected), Is.Null, $"Unexpected exists: {unexpected}");
                    break;

                case "traceparent":
                    Assert.That(traceparentJson.SelectToken(unexpected), Is.Empty, $"Unexpected exists: {unexpected}");
                    break;

                case "tracestate":
                    Assert.That(tracestateJson.SelectToken(unexpected), Is.Empty, $"Unexpected exists: {unexpected}");
                    break;

                default:
                    break;
                }
            });

            var notequalFields = payloadSettings.Notequal;

            if (notequalFields != null)
            {
                foreach (var key in notequalFields.Keys)
                {
                    var notValue = notequalFields[key];

                    switch (key.Substring(0, key.IndexOf('.')))
                    {
                    case "newrelic":
                        actualValue = newrelicJson.SelectToken(key);
                        break;

                    case "traceparent":
                        actualValue = traceparentJson.SelectToken(key);
                        break;

                    case "tracestate":
                        actualValue = tracestateJson.SelectToken(key);
                        break;

                    default:
                        break;
                    }

                    Assert.That(actualValue.IsNotEqualTo(notValue), $"Expected not equal {key}, but was equal {notValue}");
                }
            }

            if (payloadSettings.Vendors != null)
            {
                JArray actualVendors = (JArray)tracestateJson["tracestate"]["vendors"];

                Assert.That(JToken.DeepEquals(actualVendors, payloadSettings.Vendors), $"Expected vendors {payloadSettings.Vendors}, actual: {actualVendors}");
            }
        }