|
1 | 1 | import { expect, test } from '@playwright/test'; |
2 | | -import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils'; |
| 2 | +import { collectStreamedSpans, collectStreamedSpansUntilSegment, getSpanOp } from '@sentry-internal/test-utils'; |
3 | 3 | import { isDevMode } from './isDevMode'; |
4 | 4 |
|
5 | | -// TODO: Skipped until the Cloudflare Workers edge middleware setup emits middleware spans reliably. |
6 | | -test.skip('tracesSampler receives normalizedRequest for edge middleware', async ({ request }) => { |
7 | | - const middlewareSpanPromise = waitForStreamedSpan('nextjs-16-cf-workers', span => { |
8 | | - return span.name === 'middleware GET' && span.is_segment; |
9 | | - }); |
10 | | - |
11 | | - await request.get('/api/endpoint-behind-middleware'); |
12 | | - |
13 | | - const middlewareSpan = await middlewareSpanPromise; |
14 | | - |
15 | | - expect(String(middlewareSpan.attributes['http.target']?.value)).toContain('/api/endpoint-behind-middleware'); |
16 | | - expect(middlewareSpan.attributes['http.request.method']?.value).toBe('GET'); |
17 | | -}); |
18 | | - |
19 | | -// TODO: Middleware tests need SDK adjustments for Cloudflare Workers edge runtime |
20 | | -test.skip('Should create a span for middleware', async ({ request }) => { |
21 | | - const middlewareSpanPromise = waitForStreamedSpan('nextjs-16-cf-workers', span => { |
22 | | - return span.name === 'middleware GET' && span.is_segment; |
23 | | - }); |
| 5 | +// `withSentry` from `@sentry/nextjs/cloudflare` wraps the Worker entry, so middleware runs inside the request's |
| 6 | +// `http.server` segment and its span is a child of it rather than a segment of its own. |
| 7 | +test('Should create a span for middleware inside the request segment', async ({ request }) => { |
| 8 | + const spansPromise = collectStreamedSpansUntilSegment('nextjs-16-cf-workers', 'GET /api/endpoint-behind-middleware'); |
24 | 9 |
|
25 | 10 | const response = await request.get('/api/endpoint-behind-middleware'); |
26 | 11 | expect(await response.json()).toStrictEqual({ name: 'John Doe' }); |
27 | 12 |
|
28 | | - const middlewareSpan = await middlewareSpanPromise; |
29 | | - |
| 13 | + const spans = await spansPromise; |
| 14 | + const segmentSpan = spans.find(span => span.is_segment)!; |
| 15 | + const middlewareSpan = spans.find(span => span.name === 'middleware GET')!; |
| 16 | + |
| 17 | + expect(spans.filter(span => getSpanOp(span) === 'middleware')).toEqual([middlewareSpan]); |
| 18 | + expect(segmentSpan.attributes['url.path']?.value).toBe('/api/endpoint-behind-middleware'); |
| 19 | + expect(segmentSpan.attributes['sentry.segment.name.source']?.value).toBe('route'); |
| 20 | + expect(segmentSpan.attributes['isolation_scope.is_default']).toEqual({ value: false, type: 'boolean' }); |
| 21 | + expect(middlewareSpan.is_segment).toBe(false); |
| 22 | + expect(middlewareSpan.parent_span_id).toBe(segmentSpan.span_id); |
30 | 23 | expect(middlewareSpan.status).toBe('ok'); |
31 | 24 | expect(getSpanOp(middlewareSpan)).toBe('middleware'); |
32 | | - expect(middlewareSpan.attributes['sentry.segment.name.source']?.value).toBe('route'); |
33 | | - |
34 | | - // Assert that isolation scope works properly |
35 | | - // expect(middlewareSpan.attributes['isolation_scope.is_default']).toEqual({ value: false, type: 'boolean' }); |
36 | 25 | }); |
37 | 26 |
|
38 | | -// TODO: Middleware tests need SDK adjustments for Cloudflare Workers edge runtime |
39 | | -test.skip('Faulty middlewares', async ({ request }) => { |
| 27 | +test('Faulty middlewares', async ({ request }) => { |
40 | 28 | test.skip(isDevMode, 'Throwing crashes the dev server atm'); // https://github.com/vercel/next.js/issues/85261 |
41 | | - const middlewareSpanPromise = waitForStreamedSpan('nextjs-16-cf-workers', span => { |
42 | | - return span.name === 'middleware GET' && span.is_segment; |
43 | | - }); |
| 29 | + // The middleware throws, so the request span of `withSentry` keeps the middleware name, like the middleware segment |
| 30 | + // of Node.js. |
| 31 | + const spansPromise = collectStreamedSpans( |
| 32 | + 'nextjs-16-cf-workers', |
| 33 | + spans => |
| 34 | + spans.some(span => span.is_segment && span.name === 'middleware GET') && |
| 35 | + spans.some(span => !span.is_segment && span.name === 'middleware GET'), |
| 36 | + ); |
44 | 37 |
|
45 | 38 | request.get('/api/endpoint-behind-middleware', { headers: { 'x-should-throw': '1' } }).catch(() => { |
46 | 39 | // Noop |
47 | 40 | }); |
48 | 41 |
|
49 | | - await test.step('should record spans', async () => { |
50 | | - const middlewareSpan = await middlewareSpanPromise; |
51 | | - expect(middlewareSpan.status).toBe('error'); |
52 | | - expect(getSpanOp(middlewareSpan)).toBe('middleware'); |
53 | | - expect(middlewareSpan.attributes['sentry.segment.name.source']?.value).toBe('route'); |
54 | | - }); |
| 42 | + const spans = await spansPromise; |
| 43 | + const segmentSpan = spans.find(span => span.is_segment)!; |
| 44 | + const middlewareSpan = spans.find(span => !span.is_segment && span.name === 'middleware GET')!; |
| 45 | + |
| 46 | + expect(segmentSpan.status).toBe('error'); |
| 47 | + expect(segmentSpan.attributes['sentry.segment.name.source']?.value).toBe('route'); |
| 48 | + expect(middlewareSpan.parent_span_id).toBe(segmentSpan.span_id); |
| 49 | + expect(middlewareSpan.status).toBe('error'); |
| 50 | + expect(getSpanOp(middlewareSpan)).toBe('middleware'); |
55 | 51 | }); |
56 | 52 |
|
57 | | -// TODO: Middleware tests need SDK adjustments for Cloudflare Workers edge runtime |
58 | | -test.skip('Should trace outgoing fetch requests inside middleware', async ({ request }) => { |
| 53 | +test('Should trace outgoing fetch requests inside middleware', async ({ request }) => { |
59 | 54 | test.skip(isDevMode, 'The fetch requests ends up in a separate tx in dev atm'); |
60 | 55 |
|
61 | 56 | // `http.client` span names are low cardinality under span streaming, hence `GET localhost` rather |
|
0 commit comments