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); }
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; }
public async Task DbOperationOutsideTransactionTest() { var pageData = new SampleAppUrlPathData(HomeController.DbOperationOutsideTransactionTestPageRelativePath , HomeController.DbOperationOutsideTransactionTestStatusCode); await SendGetRequestToSampleAppAndVerifyResponse(pageData.Uri, pageData.StatusCode); await WaitAndVerifyReceivedDataSharedConstraints(pageData); }
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); }); }
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); }); }
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); }); }
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); }
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(); } }); }
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 }); }
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);
protected async Task WaitAndVerifyReceivedDataSharedConstraints(SampleAppUrlPathData sampleAppUrlPathData) => await WaitAndCustomVerifyReceivedData(receivedData => { VerifyReceivedDataSharedConstraints(sampleAppUrlPathData, receivedData); });
protected void VerifyDataReceivedFromAgent(SampleAppUrlPathData sampleAppUrlPathData) => VerifyDataReceivedFromAgent(receivedData => { TryVerifyDataReceivedFromAgent(sampleAppUrlPathData, receivedData); });
protected async Task VerifyDataReceivedFromAgent(SampleAppUrlPathData sampleAppUrlPathData) => await VerifyDataReceivedFromAgent(receivedData => { TryVerifyDataReceivedFromAgent(sampleAppUrlPathData, receivedData); });
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); }
protected async Task WaitAndVerifyReceivedDataSharedConstraints( SampleAppUrlPathData sampleAppUrlPathData , bool shouldGatherDiagnostics = true ) => await WaitAndCustomVerifyReceivedData(receivedData => { VerifyReceivedDataSharedConstraints(sampleAppUrlPathData, receivedData); }, shouldGatherDiagnostics);
public async Task Test(SampleAppUrlPathData sampleAppUrlPathData) { await SendGetRequestToSampleAppAndVerifyResponse(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode); await WaitAndVerifyReceivedDataSharedConstraints(sampleAppUrlPathData); }
public async Task WithDefaultSettings(SampleAppUrlPathData sampleAppUrlPathData) { await SendGetRequestToSampleAppAndVerifyResponseStatusCode(sampleAppUrlPathData.RelativeUrlPath, sampleAppUrlPathData.StatusCode); VerifyDataReceivedFromAgent(sampleAppUrlPathData); }