From 34828ce0489d56565e603435b13cd02839fc8346 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Apr 2024 10:58:44 +0200 Subject: [PATCH 1/3] test(browser-integration-tests): Add trace lifetime tests for tag pageload transactions --- .../pageload-meta/template.html | 13 ++ .../trace-lifetime/pageload-meta/test.ts | 159 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/template.html b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/template.html new file mode 100644 index 000000000000..61372c8605e5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/template.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts new file mode 100644 index 000000000000..daf2f84dac95 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts @@ -0,0 +1,159 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; +import { sentryTest } from '../../../../utils/fixtures'; +import { + getFirstSentryEnvelopeRequest, + getMultipleSentryEnvelopeRequests, + shouldSkipTracingTest, +} from '../../../../utils/helpers'; + +const META_TAG_TRACE_ID = '12345678901234567890123456789012'; +const META_TAG_PARENT_SPAN_ID = '1234567890123456'; +const META_TAG_BAGGAGE = + 'sentry-trace_id=12345678901234567890123456789012,sentry-sample_rate=0.2,sentry-transaction=my-transaction,sentry-public_key=public,sentry-release=1.0.0,sentry-environment=prod'; + +sentryTest( + 'create a new trace for a navigation after the tag pageload trace', + async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + + const pageloadEvent = await getFirstSentryEnvelopeRequest(page, url); + const navigationEvent = await getFirstSentryEnvelopeRequest(page, `${url}#foo`); + + const pageloadTraceContext = pageloadEvent.contexts?.trace; + const navigationTraceContext = navigationEvent.contexts?.trace; + + expect(pageloadTraceContext).toMatchObject({ + op: 'pageload', + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + expect(navigationTraceContext).toMatchObject({ + op: 'navigation', + trace_id: expect.stringMatching(/^[0-9a-f]{32}$/), + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + // navigation span is head of trace, so there's no parent span: + expect(navigationTraceContext?.trace_id).not.toHaveProperty('parent_span_id'); + + expect(pageloadTraceContext?.trace_id).not.toEqual(navigationTraceContext?.trace_id); + }, +); + +sentryTest('error after tag pageload has pageload traceId', async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + + const pageloadEvent = await getFirstSentryEnvelopeRequest(page, url); + expect(pageloadEvent.contexts?.trace).toMatchObject({ + op: 'pageload', + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + + const errorEventPromise = getFirstSentryEnvelopeRequest(page); + await page.locator('#errorBtn').click(); + const errorEvent = await errorEventPromise; + + expect(errorEvent.contexts?.trace).toMatchObject({ + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); +}); + +sentryTest('error during tag pageload has pageload traceId', async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + + const envelopeRequestsPromise = getMultipleSentryEnvelopeRequests(page, 2); + await page.goto(url); + await page.locator('#errorBtn').click(); + const events = await envelopeRequestsPromise; + + const pageloadEvent = events.find(event => event.type === 'transaction'); + const errorEvent = events.find(event => !event.type); + + expect(pageloadEvent?.contexts?.trace).toMatchObject({ + op: 'pageload', + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + + expect(errorEvent?.contexts?.trace).toMatchObject({ + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); +}); + +sentryTest( + 'outgoing fetch request after tag pageload has pageload traceId in headers', + async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + + const pageloadEvent = await getFirstSentryEnvelopeRequest(page, url); + expect(pageloadEvent?.contexts?.trace).toMatchObject({ + op: 'pageload', + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + + const requestPromise = page.waitForRequest('http://example.com/*'); + await page.locator('#fetchBtn').click(); + const request = await requestPromise; + const headers = request.headers(); + + // sampling decision is deferred b/c of no active span at the time of request + expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}-1$`)); + expect(headers['baggage']).toBe(META_TAG_BAGGAGE); + }, +); + +sentryTest( + 'outgoing fetch request during tag pageload has pageload traceId in headers', + async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + + const pageloadEventPromise = getFirstSentryEnvelopeRequest(page); + const requestPromise = page.waitForRequest('http://example.com/*'); + await page.goto(url); + await page.locator('#fetchBtn').click(); + const [pageloadEvent, request] = await Promise.all([pageloadEventPromise, requestPromise]); + + expect(pageloadEvent?.contexts?.trace).toMatchObject({ + op: 'pageload', + trace_id: META_TAG_TRACE_ID, + parent_span_id: META_TAG_PARENT_SPAN_ID, + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + }); + + const headers = request.headers(); + + // sampling decision is propagated from active span sampling decision + expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}-1$`)); + expect(headers['baggage']).toBe(META_TAG_BAGGAGE); + }, +); From e585a7804937d46736e8b825e714d17e471efb21 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Apr 2024 11:02:29 +0200 Subject: [PATCH 2/3] fix invalid comments --- .../suites/tracing/trace-lifetime/pageload-meta/test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts index daf2f84dac95..2f872b398d87 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-meta/test.ts @@ -122,7 +122,7 @@ sentryTest( const request = await requestPromise; const headers = request.headers(); - // sampling decision is deferred b/c of no active span at the time of request + // sampling decision is propagated from meta tag's sentry-trace sampled flag expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}-1$`)); expect(headers['baggage']).toBe(META_TAG_BAGGAGE); }, @@ -152,7 +152,7 @@ sentryTest( const headers = request.headers(); - // sampling decision is propagated from active span sampling decision + // sampling decision is propagated from meta tag's sentry-trace sampled flag expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}-1$`)); expect(headers['baggage']).toBe(META_TAG_BAGGAGE); }, From 87bdf9f384b0a373bbf3198513701d166b27edc5 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Apr 2024 11:10:39 +0200 Subject: [PATCH 3/3] add small readme to test suite --- .../suites/tracing/trace-lifetime/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/README.md diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/README.md b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/README.md new file mode 100644 index 000000000000..d7330f233425 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/README.md @@ -0,0 +1,9 @@ +Tests in this suite are meant to test the lifetime of a trace in the browser SDK and how different events sent are +connected to a trace. This suite distinguishes the following cases: + +1. `pageload` - Traces started on the initial pageload as head of trace +2. `pageload-meta` - Traces started on the initial pageload as a continuation of the trace on the server (via `` + tags) +3. `navigation` - Traces started during navigations on a page + +Tests scenarios should be fairly similar for all three cases but it's important we test all of them.