private TransactionDto FindAndVerifyTransaction(ReceivedData receivedData, SampleAppUrlPathData txData)
        {
            var txUrlPath   = Consts.SampleApp.RootUrlPath + "/" + txData.RelativeUrlPath;
            var transaction = receivedData.Transactions.Single(tx => tx.Name == $"GET {txUrlPath}");

            transaction.Context.Request.Method.ToUpperInvariant().Should().Be("GET");
            transaction.Context.Request.Url.Full.Should().Be("http://" + Consts.SampleApp.Host + txUrlPath);
            transaction.Context.Request.Url.PathName.Should().Be(txUrlPath);
            transaction.Context.Request.Url.Search.Should().BeNull();

            transaction.Context.Response.Finished.Should().BeTrue();
            transaction.Context.Response.StatusCode.Should().Be(txData.StatusCode);
            if (txData.StatusCode == (int)HttpStatusCode.OK)
            {
                var caseInsensitiveResponseHeaders =
                    new Dictionary <string, string>(transaction.Context.Response.Headers, StringComparer.OrdinalIgnoreCase);
                caseInsensitiveResponseHeaders["Content-Type"].Should().Be("text/html; charset=utf-8");
            }

            transaction.Context.Tags.Should().BeNull();
            transaction.Context.User.Should().BeNull();

            transaction.IsSampled.Should().BeTrue();
            transaction.Name.Should().Be($"GET {txUrlPath}");
            transaction.SpanCount.Started.Should().Be(txData.SpansCount);
            transaction.SpanCount.Dropped.Should().Be(0);

            return(transaction);
        }
예제 #2
0
        private static void VerifyRootChildTransactions(
            ReceivedData receivedData,
            SampleAppUrlPathData rootTxData,
            SampleAppUrlPathData childTxData,
            out TransactionDto rootTxOut,
            out TransactionDto childTxOut
            )
        {
            var rootTx  = FindAndVerifyTransaction(receivedData, rootTxData);
            var childTx = FindAndVerifyTransaction(receivedData, childTxData);

            var spanCallToChildTx = receivedData.Spans.Single(sp => sp.Context.Http.Url == childTx.Context.Request.Url.Full);

            VerifyHttpCallSpan(spanCallToChildTx, childTx.Context.Request.Url.Full, childTxData.StatusCode);

            childTx.TraceId.Should().Be(rootTx.TraceId);
            spanCallToChildTx.TraceId.Should().Be(rootTx.TraceId);

            spanCallToChildTx.ParentId.Should().Be(rootTx.Id);
            childTx.ParentId.Should().Be(spanCallToChildTx.Id);

            spanCallToChildTx.TransactionId.Should().Be(rootTx.Id);

            spanCallToChildTx.ShouldOccurBetween(rootTx);
            childTx.ShouldOccurBetween(spanCallToChildTx);

            rootTxOut  = rootTx;
            childTxOut = childTx;
        }
예제 #3
0
        public async Task DbOperationOutsideTransactionTest()
        {
            var pageData = new SampleAppUrlPathData(HomeController.DbOperationOutsideTransactionTestPageRelativePath
                                                    , HomeController.DbOperationOutsideTransactionTestStatusCode);

            await SendGetRequestToSampleAppAndVerifyResponse(pageData.Uri, pageData.StatusCode);
            await WaitAndVerifyReceivedDataSharedConstraints(pageData);
        }
예제 #4
0
        public async Task SimpleDbTest()
        {
            // 4 DB spans:
            //      1) CREATE TABLE
            //                      the first "new SampleDataDbContext()"
            //      2) INSERT for
            //                      dbCtx.Set<SampleData>().Add(new SampleData { Name = simpleDbTestSampleDataName });
            //      3) SELECT for
            //						```if (dbCtx.Set<SampleData>().Count() != 1)
            //      4) SELECT for
            //                      if (dbCtx.Set<SampleData>().First().Name != simpleDbTestSampleDataName)
            var pageData = new SampleAppUrlPathData(HomeController.SimpleDbTestPageRelativePath, 200, spansCount: 4);

            await SendGetRequestToSampleAppAndVerifyResponse(pageData.Uri, pageData.StatusCode);

            await WaitAndCustomVerifyReceivedData(receivedData =>
            {
                VerifyReceivedDataSharedConstraints(pageData, receivedData);

                // See comment for TestsBase.SampleAppUrlPaths.SimpleDbTestPage
                var dbStatements = new[] { "CREATE TABLE", "INSERT", "SELECT", "SELECT" };

                var transaction = receivedData.Transactions.First();

                receivedData.Spans.ForEachIndexed((span, i) =>
                {
                    var signatureParser = new SignatureParser(new Scanner());
                    var name            = new StringBuilder();
                    signatureParser.QuerySignature(dbStatements[i], name, preparedStatement: false);

                    span.Name.Should().StartWith(name.ToString());
                    span.Type.Should().Be(ApiConstants.TypeDb);
                    span.Subtype.Should().Be(ApiConstants.SubtypeSqLite);
                    span.Context.Db.Type.Should().Be(Database.TypeSql);
                    span.Outcome.Should().Be(Outcome.Success);

                    span.Context.Db.Instance.Should().NotBeNull();
                    span.Context.Db.Instance.Should().Be(receivedData.Spans.First().Context.Db.Instance);

                    span.Context.Db.Statement.Should().StartWith(dbStatements[i]);

                    span.Context.Destination.Should().NotBeNull();

                    span.TraceId.Should().Be(transaction.TraceId);
                    span.TransactionId.Should().Be(transaction.Id);
                    span.ParentId.Should().Be(transaction.Id);
                    span.ShouldOccurBetween(transaction);

                    if (i != 0)
                    {
                        receivedData.Spans[i - 1].ShouldOccurBefore(span);
                    }
                });

                ShouldBeMonotonicInTime(receivedData.Spans);
            });
        }
예제 #5
0
        public async Task Test(SampleAppUrlPathData sampleAppUrlPathDataForSampled)
        {
            var sampleAppUrlPathData = sampleAppUrlPathDataForSampled.Clone(spansCount: 0);

            await SendGetRequestToSampleAppAndVerifyResponse(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode);

            await WaitAndCustomVerifyReceivedData(receivedData =>
            {
                VerifyReceivedDataSharedConstraints(sampleAppUrlPathData, receivedData);

                AssertReceivedDataSampledStatus(receivedData, /* isSampled */ false);
            });
        }
예제 #6
0
        public async Task FailingDbCallTest()
        {
            var pageData = new SampleAppUrlPathData(HomeController.FailingDbCallTestPageRelativePath
                                                    , HomeController.FailingDbCallTestStatusCode);

            await SendGetRequestToSampleAppAndVerifyResponse(pageData.Uri, pageData.StatusCode);

            await WaitAndCustomVerifyReceivedData(receivedData =>
            {
                receivedData.Transactions.Should().NotBeNullOrEmpty();
                receivedData.Spans.Should().NotBeNullOrEmpty();

                var selectSpan = receivedData.Spans
                                 .First(n => n.Context.Db != null && n.Context.Db.Statement.ToLower().Contains("select * from nonexistingtable"));
                selectSpan.Should().NotBeNull();
                selectSpan.Outcome.Should().Be(Outcome.Failure);
            });
        }
예제 #7
0
        protected void VerifyReceivedDataSharedConstraints(SampleAppUrlPathData sampleAppUrlPathData, ReceivedData receivedData)
        {
            FullFwAssertValid(receivedData);

            receivedData.Transactions.Count.Should().Be(sampleAppUrlPathData.TransactionsCount);
            receivedData.Spans.Count.Should().Be(sampleAppUrlPathData.SpansCount);
            receivedData.Errors.Count.Should().Be(sampleAppUrlPathData.ErrorsCount);

            if (receivedData.Transactions.Count != 1)
            {
                return;
            }

            var transaction = receivedData.Transactions.First();

            if (transaction.Context != null)
            {
                transaction.Context.Request.Url.Full.Should().Be(sampleAppUrlPathData.Uri.AbsoluteUri);
                transaction.Context.Request.Url.PathName.Should().Be(sampleAppUrlPathData.Uri.AbsolutePath);

                if (string.IsNullOrEmpty(sampleAppUrlPathData.Uri.Query))
                {
                    transaction.Context.Request.Url.Search.Should().BeNull();
                }
                else
                {
                    // Uri.Query always unescapes the querystring so don't use it, and instead get the escaped querystring.
                    var queryString = sampleAppUrlPathData.Uri.GetComponents(UriComponents.Query, UriFormat.UriEscaped);
                    transaction.Context.Request.Url.Search.Should().Be(queryString);
                }

                transaction.Context.Response.StatusCode.Should().Be(sampleAppUrlPathData.StatusCode);
                transaction.Outcome.Should().Be(sampleAppUrlPathData.Outcome);
            }

            var httpStatusFirstDigit = sampleAppUrlPathData.StatusCode / 100;

            transaction.Result.Should().Be($"HTTP {httpStatusFirstDigit}xx");
            transaction.SpanCount.Started.Should().Be(sampleAppUrlPathData.SpansCount);
        }
예제 #8
0
        protected void VerifyReceivedDataSharedConstraints(SampleAppUrlPathData sampleAppUrlPathData, ReceivedData receivedData)
        {
            FullFwAssertValid(receivedData);

            receivedData.Transactions.Count.Should().Be(sampleAppUrlPathData.TransactionsCount);
            receivedData.Spans.Count.Should().Be(sampleAppUrlPathData.SpansCount);
            receivedData.Errors.Count.Should().Be(sampleAppUrlPathData.ErrorsCount);

            // ReSharper disable once InvertIf
            if (receivedData.Transactions.Count == 1)
            {
                var transaction = receivedData.Transactions.First();

                if (transaction.Context != null)
                {
                    transaction.Context.Request.Url.Full.Should().Be(Consts.SampleApp.RootUrl + "/" + sampleAppUrlPathData.RelativeUrlPath);

                    var questionMarkIndex = sampleAppUrlPathData.RelativeUrlPath.IndexOf('?');
                    if (questionMarkIndex == -1)
                    {
                        transaction.Context.Request.Url.PathName.Should()
                        .Be(Consts.SampleApp.RootUrlPath + "/" + sampleAppUrlPathData.RelativeUrlPath);
                        transaction.Context.Request.Url.Search.Should().BeNull();
                    }
                    else
                    {
                        transaction.Context.Request.Url.PathName.Should()
                        .Be(Consts.SampleApp.RootUrlPath + "/" + sampleAppUrlPathData.RelativeUrlPath.Substring(0, questionMarkIndex));
                        transaction.Context.Request.Url.Search.Should().Be(sampleAppUrlPathData.RelativeUrlPath.Substring(questionMarkIndex + 1));
                    }

                    transaction.Context.Response.StatusCode.Should().Be(sampleAppUrlPathData.StatusCode);
                    transaction.Outcome.Should().Be(sampleAppUrlPathData.Outcome);
                }

                var httpStatusFirstDigit = sampleAppUrlPathData.StatusCode / 100;
                transaction.Result.Should().Be($"HTTP {httpStatusFirstDigit}xx");
                transaction.SpanCount.Started.Should().Be(sampleAppUrlPathData.SpansCount);
            }
        }
        public async Task Test(SampleAppUrlPathData sampleAppUrlPathDataForSampled)
        {
            var sampleAppUrlPathData = sampleAppUrlPathDataForSampled.Clone(spansCount: 0);

            await SendGetRequestToSampleAppAndVerifyResponseStatusCode(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode);

            VerifyDataReceivedFromAgent(receivedData =>
            {
                TryVerifyDataReceivedFromAgent(sampleAppUrlPathData, receivedData);

                foreach (var transaction in receivedData.Transactions)
                {
                    transaction.Context.Should().BeNull();
                    transaction.SpanCount.Started.Should().Be(0);
                    transaction.IsSampled.Should().BeFalse();
                }

                foreach (var error in receivedData.Errors)
                {
                    error.Context.Should().BeNull();
                    error.Transaction.IsSampled.Should().BeFalse();
                }
            });
        }
예제 #10
0
        public async Task ConcurrentDbTest()
        {
            const int numberOfConcurrentIterations = HomeController.ConcurrentDbTestNumberOfIterations;

            (numberOfConcurrentIterations % 2).Should()
            .Be(0
                , $"because numberOfConcurrentIterations should be even. numberOfConcurrentIterations: {numberOfConcurrentIterations}");
            // numberOfConcurrentIterations *3 + 5 DB spans:
            //      1) CREATE TABLE
            //      2) SELECT for
            //                      if (dbCtx.Set<SampleData>().Count() != 0)
            //      3) 2 concurrent spans
            //          3.1) numberOfConcurrentIterations * 3 INSERT-s
            //      4) SELECT for
            //                      var sampleDataList = dbCtx.Set<SampleData>().ToList();
            //
            var pageData = new SampleAppUrlPathData(HomeController.ConcurrentDbTestPageRelativePath, 200
                                                    , spansCount: numberOfConcurrentIterations * 3 + 5);

            await SendGetRequestToSampleAppAndVerifyResponse(pageData.Uri, pageData.StatusCode);

            await WaitAndCustomVerifyReceivedData(receivedData =>
            {
                VerifyReceivedDataSharedConstraints(pageData, receivedData);

                var transaction = receivedData.Transactions.First();

                // ReSharper disable PossibleMultipleEnumeration

                var topLevelSpans = receivedData.Spans.Where(span => span.ParentId == transaction.Id);
                topLevelSpans.Should().HaveCount(5);

                var topLevelDbSpans = topLevelSpans.Where(span => span.Type == ApiConstants.TypeDb);
                topLevelDbSpans.Should().HaveCount(3);
                ShouldBeMonotonicInTime(topLevelDbSpans);

                var topLevelConcurrentSpans = topLevelSpans.Where(span => span.Type == HomeController.ConcurrentDbTestSpanType).ToList();
                topLevelConcurrentSpans.Should().HaveCount(2);

                var allChildDbSpans = new List <SpanDto> [2];
                // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
                foreach (var topLevelConcurrentSpan in topLevelConcurrentSpans)
                {
                    var childDbSpans = receivedData.Spans.Where(span => span.ParentId == topLevelConcurrentSpan.Id);
                    childDbSpans.Should().HaveCount(numberOfConcurrentIterations * 3 / 2);
                    ShouldBeMonotonicInTime(childDbSpans);

                    childDbSpans.ForEachIndexed((childDbSpan, i) =>
                    {
                        childDbSpan.ParentId.Should().Be(topLevelConcurrentSpan.Id);
                        childDbSpan.ShouldOccurBetween(topLevelConcurrentSpan);
                    });

                    (topLevelConcurrentSpan.Name == "A" || topLevelConcurrentSpan.Name == "B").Should().BeTrue();
                    var index = topLevelConcurrentSpan.Name == "A" ? 0 : 1;
                    allChildDbSpans[index].Should().BeNull();
                    allChildDbSpans[index] = childDbSpans.ToList();
                }

                var indexInBranch = new int[2];
                numberOfConcurrentIterations.Repeat(i =>
                {
                    var containingSpanBranchIndex = i % 2 == 0 ? 0 : 1;
                    var containedSpansBranchIndex = (containingSpanBranchIndex + 1) % 2;
                    var containingSpan            = allChildDbSpans[containingSpanBranchIndex][indexInBranch[containingSpanBranchIndex]];
                    var containedSpanBefore       = allChildDbSpans[containedSpansBranchIndex][indexInBranch[containedSpansBranchIndex]];
                    var containedSpanAfter        = allChildDbSpans[containedSpansBranchIndex][indexInBranch[containedSpansBranchIndex] + 1];
                    (OccursBetween(containedSpanBefore, containingSpan) || OccursBetween(containedSpanAfter, containingSpan)).Should()
                    .BeTrue(
                        $"containingSpan: {containingSpan}, containedSpanBefore: {containedSpanBefore}, containedSpanAfter: {containedSpanAfter}");
                    indexInBranch[containingSpanBranchIndex] += 1;
                    indexInBranch[containedSpansBranchIndex] += 2;
                });

                var dbStatements = new List <string> {
                    "CREATE TABLE", "SELECT", "SELECT"
                };
                var allChildDbSpansFlattened = allChildDbSpans.SelectMany(x => x);
                dbStatements.AddRange(Enumerable.Repeat("INSERT", allChildDbSpansFlattened.Count()));
                topLevelDbSpans.Concat(allChildDbSpansFlattened)
                .ForEachIndexed((dbSpan, i) =>
                {
                    var signatureParser = new SignatureParser(new Scanner());
                    var name            = new StringBuilder();
                    signatureParser.QuerySignature(dbStatements[i], name, preparedStatement: false);

                    dbSpan.Name.Should().StartWith(name.ToString());
                    dbSpan.Type.Should().Be(ApiConstants.TypeDb);
                    dbSpan.Subtype.Should().Be(ApiConstants.SubtypeSqLite);
                    dbSpan.Context.Db.Type.Should().Be(Database.TypeSql);

                    dbSpan.Context.Db.Instance.Should().NotBeNull();
                    dbSpan.Context.Db.Instance.Should().Be(topLevelDbSpans.First().Context.Db.Instance);

                    dbSpan.Context.Db.Statement.Should().StartWith(dbStatements[i]);

                    dbSpan.Context.Destination.Should().NotBeNull();

                    dbSpan.TraceId.Should().Be(transaction.TraceId);
                    dbSpan.TransactionId.Should().Be(transaction.Id);
                    dbSpan.ShouldOccurBetween(transaction);
                });

                // ReSharper restore PossibleMultipleEnumeration
            });
        }
예제 #11
0
 public async Task Test(SampleAppUrlPathData sampleAppUrlPathData)
 {
     await SendGetRequestToSampleAppAndVerifyResponse(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode);
     await VerifyDataReceivedFromAgent(sampleAppUrlPathData);
 }
 public async Task SampleAppShouldBeAvailableEvenWhenApmServerStopped(SampleAppUrlPathData sampleAppUrlPathData) =>
 await SendGetRequestToSampleAppAndVerifyResponseStatusCode(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode);
예제 #13
0
 protected async Task WaitAndVerifyReceivedDataSharedConstraints(SampleAppUrlPathData sampleAppUrlPathData) =>
 await WaitAndCustomVerifyReceivedData(receivedData => { VerifyReceivedDataSharedConstraints(sampleAppUrlPathData, receivedData); });
예제 #14
0
 protected void VerifyDataReceivedFromAgent(SampleAppUrlPathData sampleAppUrlPathData) =>
 VerifyDataReceivedFromAgent(receivedData => { TryVerifyDataReceivedFromAgent(sampleAppUrlPathData, receivedData); });
예제 #15
0
 protected async Task VerifyDataReceivedFromAgent(SampleAppUrlPathData sampleAppUrlPathData) =>
 await VerifyDataReceivedFromAgent(receivedData => { TryVerifyDataReceivedFromAgent(sampleAppUrlPathData, receivedData); });
예제 #16
0
        private static TransactionDto FindAndVerifyTransaction(ReceivedData receivedData, SampleAppUrlPathData txData)
        {
            var txUrlPath = Consts.SampleApp.RootUrlPath + "/" + txData.RelativeUrlPath;
            var expectedFullUrlAsString = "http://" + Consts.SampleApp.Host + txUrlPath;
            var expectedFullUrl         = new Uri(expectedFullUrlAsString);
            var queryString             = expectedFullUrl.Query;

            if (queryString.IsEmpty())
            {
                // Uri.Query returns empty string both when query string is empty ("http://host/path?") and
                // when there's no query string at all ("http://host/path") so we need a way to distinguish between these cases
                if (expectedFullUrlAsString.IndexOf('?') == -1)
                {
                    queryString = null;
                }
            }
            else if (queryString[0] == '?')
            {
                queryString = queryString.Substring(1, queryString.Length - 1);
            }

            var transaction = receivedData.Transactions.Single(tx => tx.Context.Request.Url.PathName == expectedFullUrl.AbsolutePath);

            transaction.Context.Request.Method.ToUpperInvariant().Should().Be("GET");
            transaction.Context.Request.Url.Full.Should().Be(expectedFullUrlAsString);
            transaction.Context.Request.Url.PathName.Should().Be(expectedFullUrl.AbsolutePath);
            transaction.Context.Request.Url.Search.Should().Be(queryString);

            transaction.Context.Response.Finished.Should().BeTrue();
            transaction.Context.Response.StatusCode.Should().Be(txData.StatusCode);
            if (txData.StatusCode == (int)HttpStatusCode.OK)
            {
                var caseInsensitiveResponseHeaders =
                    new Dictionary <string, string>(transaction.Context.Response.Headers, StringComparer.OrdinalIgnoreCase);
                caseInsensitiveResponseHeaders["Content-Type"].Should().Be("text/html; charset=utf-8");
            }

            transaction.Context.Labels.Should().BeNull();
            transaction.Context.User.Should().BeNull();

            transaction.IsSampled.Should().BeTrue();
            transaction.Name.Should().Be($"GET {expectedFullUrl.AbsolutePath}");
            transaction.SpanCount.Started.Should().Be(txData.SpansCount);
            transaction.SpanCount.Dropped.Should().Be(0);

            return(transaction);
        }
예제 #17
0
 protected async Task WaitAndVerifyReceivedDataSharedConstraints(
     SampleAppUrlPathData sampleAppUrlPathData
     , bool shouldGatherDiagnostics = true
     ) =>
 await WaitAndCustomVerifyReceivedData(receivedData => { VerifyReceivedDataSharedConstraints(sampleAppUrlPathData, receivedData); },
                                       shouldGatherDiagnostics);
예제 #18
0
 public async Task Test(SampleAppUrlPathData sampleAppUrlPathData)
 {
     await SendGetRequestToSampleAppAndVerifyResponse(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode);
     await WaitAndVerifyReceivedDataSharedConstraints(sampleAppUrlPathData);
 }
예제 #19
0
        public async Task WithDefaultSettings(SampleAppUrlPathData sampleAppUrlPathData)
        {
            await SendGetRequestToSampleAppAndVerifyResponseStatusCode(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode);

            VerifyDataReceivedFromAgent(sampleAppUrlPathData);
        }