Skip to content

Commit 64d9de9

Browse files
authored
test(e2e): Port the astro-6-cf-workers E2E app to span streaming (#24080)
## What Removes the `traceLifecycle: 'static'` pin from the client and server configs, and rewrites the transaction specs as streamed span specs. Assertions on children of a segment now accumulate the trace with `collectStreamedSpans`. Two names change under span streaming, and the specs follow the value into the attributes instead: the outgoing `http.client` span is named after the domain (`GET localhost`), and a mysql span is named after its query summary (`SELECT`). ## Why Span streaming is the default, so the default E2E suite should exercise it. Static coverage lives in the new `astro-7-static` app (#24075). Ref: #23809
1 parent 98330de commit 64d9de9

5 files changed

Lines changed: 226 additions & 372 deletions

File tree

‎dev-packages/e2e-tests/test-applications/astro-6-cf-workers/sentry.client.config.js‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as Sentry from '@sentry/astro';
22

33
Sentry.init({
4-
traceLifecycle: 'static',
54
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
65
environment: 'qa',
76
tracesSampleRate: 1.0,

‎dev-packages/e2e-tests/test-applications/astro-6-cf-workers/sentry.server.config.js‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import handler from '@astrojs/cloudflare/entrypoints/server';
33

44
export default Sentry.withSentry(
55
env => ({
6-
traceLifecycle: 'static',
76
dsn: env.E2E_TEST_DSN,
87
environment: 'qa',
98
tracesSampleRate: 1.0,
Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,40 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForTransaction } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment, getSpanOp } from '@sentry-internal/test-utils';
3+
4+
const APP_NAME = 'astro-6-cf-workers';
35

46
test('a real mysql query emits a db span with orchestrion-channel attributes', async ({ request }) => {
5-
const transactionPromise = waitForTransaction('astro-6-cf-workers', transactionEvent => {
6-
return (
7-
transactionEvent.contexts?.trace?.op === 'http.server' &&
8-
(transactionEvent.spans?.some(span => span.op === 'db') ?? false)
9-
);
10-
});
7+
const spansPromise = collectStreamedSpansUntilSegment(APP_NAME, 'GET /db-mysql');
118

129
const res = await request.get('/db-mysql');
1310
expect(res.status()).toBe(200);
1411

15-
const transactionEvent = await transactionPromise;
16-
const dbSpans = transactionEvent.spans!.filter(span => span.op === 'db');
12+
const spans = await spansPromise;
13+
const dbSpans = spans.filter(span => getSpanOp(span) === 'db');
1714

18-
const firstQuery = dbSpans.find(span => span.description === 'SELECT 1 + 1 AS solution');
15+
const firstQuery = dbSpans.find(span => span.attributes['db.query.text']?.value === 'SELECT 1 + 1 AS solution');
1916
expect(firstQuery).toBeDefined();
20-
expect(firstQuery!.data?.['sentry.origin']).toBe('auto.db.mysql');
21-
expect(firstQuery!.data?.['db.system.name']).toBe('mysql');
22-
expect(firstQuery!.data?.['db.query.text']).toBe('SELECT 1 + 1 AS solution');
23-
expect(firstQuery!.data?.['server.address']).toBe('127.0.0.1');
24-
expect(firstQuery!.data?.['server.port']).toBe(3306);
25-
expect(firstQuery!.data?.['db.user']).toBe('root');
17+
// With span streaming the span name is the low-cardinality query summary; the statement stays in
18+
// `db.query.text`.
19+
expect(firstQuery!.name).toBe('SELECT');
20+
expect(firstQuery!.attributes['sentry.origin']?.value).toBe('auto.db.mysql');
21+
expect(firstQuery!.attributes['db.system.name']?.value).toBe('mysql');
22+
expect(firstQuery!.attributes['db.query.text']?.value).toBe('SELECT 1 + 1 AS solution');
23+
expect(firstQuery!.attributes['server.address']?.value).toBe('127.0.0.1');
24+
expect(firstQuery!.attributes['server.port']?.value).toBe(3306);
25+
expect(firstQuery!.attributes['db.user']?.value).toBe('root');
2626
});
2727

28-
test('a nested query lands on the same transaction (async context restored)', async ({ request }) => {
29-
const transactionPromise = waitForTransaction('astro-6-cf-workers', transactionEvent => {
30-
return (
31-
transactionEvent.contexts?.trace?.op === 'http.server' &&
32-
(transactionEvent.spans?.filter(span => span.op === 'db').length ?? 0) >= 2
33-
);
34-
});
28+
test('a nested query lands on the same trace (async context restored)', async ({ request }) => {
29+
const spansPromise = collectStreamedSpansUntilSegment(APP_NAME, 'GET /db-mysql');
3530

3631
const res = await request.get('/db-mysql');
3732
expect(res.status()).toBe(200);
3833

39-
const transactionEvent = await transactionPromise;
40-
const descriptions = transactionEvent.spans!.filter(span => span.op === 'db').map(span => span.description);
41-
expect(descriptions).toContain('SELECT 1 + 1 AS solution');
42-
expect(descriptions).toContain('SELECT NOW()');
34+
const spans = await spansPromise;
35+
const queryTexts = spans
36+
.filter(span => getSpanOp(span) === 'db')
37+
.map(span => span.attributes['db.query.text']?.value);
38+
expect(queryTexts).toContain('SELECT 1 + 1 AS solution');
39+
expect(queryTexts).toContain('SELECT NOW()');
4340
});

‎dev-packages/e2e-tests/test-applications/astro-6-cf-workers/tests/errors.server.test.ts‎

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
2+
import { getSpanOp, waitForError, waitForStreamedSpan } from '@sentry-internal/test-utils';
33

44
test.describe('server-side errors', () => {
55
test('captures SSR error', async ({ page }) => {
66
const errorEventPromise = waitForError('astro-6-cf-workers', errorEvent => {
77
return errorEvent?.exception?.values?.[0]?.value === "Cannot read properties of undefined (reading 'x')";
88
});
99

10-
const transactionEventPromise = waitForTransaction('astro-6-cf-workers', transactionEvent => {
11-
return transactionEvent.transaction === 'GET /ssr-error';
10+
const spanPromise = waitForStreamedSpan('astro-6-cf-workers', span => {
11+
return getSpanOp(span) === 'http.server' && span.is_segment && span.name === 'GET /ssr-error';
1212
});
1313

1414
// This page returns an error status code, so we need to catch the navigation error
@@ -17,19 +17,14 @@ test.describe('server-side errors', () => {
1717
});
1818

1919
const errorEvent = await errorEventPromise;
20-
const transactionEvent = await transactionEventPromise;
20+
const span = await spanPromise;
2121

22-
expect(transactionEvent).toMatchObject({
23-
transaction: 'GET /ssr-error',
24-
spans: [],
25-
});
26-
27-
const traceId = transactionEvent.contexts?.trace?.trace_id;
28-
const spanId = transactionEvent.contexts?.trace?.span_id;
22+
const traceId = span.trace_id;
23+
const spanId = span.span_id;
2924

3025
expect(traceId).toMatch(/[a-f0-9]{32}/);
3126
expect(spanId).toMatch(/[a-f0-9]{16}/);
32-
expect(transactionEvent.contexts?.trace?.parent_span_id).toBeUndefined();
27+
expect(span.parent_span_id).toBeUndefined();
3328

3429
expect(errorEvent).toMatchObject({
3530
contexts: {
@@ -80,38 +75,28 @@ test.describe('server-side errors', () => {
8075
const errorEventPromise = waitForError('astro-6-cf-workers', errorEvent => {
8176
return errorEvent?.exception?.values?.[0]?.value === 'Endpoint Error';
8277
});
83-
const transactionEventApiPromise = waitForTransaction('astro-6-cf-workers', transactionEvent => {
84-
return transactionEvent.transaction === 'GET /endpoint-error/api';
78+
const apiSpanPromise = waitForStreamedSpan('astro-6-cf-workers', span => {
79+
return getSpanOp(span) === 'http.server' && span.name === 'GET /endpoint-error/api';
8580
});
86-
const transactionEventEndpointPromise = waitForTransaction('astro-6-cf-workers', transactionEvent => {
87-
return transactionEvent.transaction === 'GET /endpoint-error';
81+
const endpointSpanPromise = waitForStreamedSpan('astro-6-cf-workers', span => {
82+
return getSpanOp(span) === 'http.server' && span.is_segment && span.name === 'GET /endpoint-error';
8883
});
8984

9085
await page.goto('/endpoint-error');
9186
await page.getByText('Get Data').click();
9287

9388
const errorEvent = await errorEventPromise;
94-
const transactionEventApi = await transactionEventApiPromise;
95-
const transactionEventEndpoint = await transactionEventEndpointPromise;
96-
97-
expect(transactionEventEndpoint).toMatchObject({
98-
transaction: 'GET /endpoint-error',
99-
spans: [],
100-
});
89+
const apiSpan = await apiSpanPromise;
90+
const endpointSpan = await endpointSpanPromise;
10191

102-
const traceId = transactionEventEndpoint.contexts?.trace?.trace_id;
103-
const endpointSpanId = transactionEventApi.contexts?.trace?.span_id;
92+
const traceId = endpointSpan.trace_id;
93+
const endpointSpanId = apiSpan.span_id;
10494

10595
expect(traceId).toMatch(/[a-f0-9]{32}/);
10696
expect(endpointSpanId).toMatch(/[a-f0-9]{16}/);
10797

108-
expect(transactionEventApi).toMatchObject({
109-
transaction: 'GET /endpoint-error/api',
110-
spans: [],
111-
});
112-
113-
const spanId = transactionEventApi.contexts?.trace?.span_id;
114-
const parentSpanId = transactionEventApi.contexts?.trace?.parent_span_id;
98+
const spanId = apiSpan.span_id;
99+
const parentSpanId = apiSpan.parent_span_id;
115100

116101
expect(spanId).toMatch(/[a-f0-9]{16}/);
117102
// TODO: This is incorrect, for whatever reason, it should be the endpointSpanId ideally

0 commit comments

Comments
 (0)