Skip to content

Commit 3023eaa

Browse files
committed
test: assert if a fetch span is created at all regardless of transaction
1 parent 0e7bdff commit 3023eaa

File tree

1 file changed

+56
-67
lines changed

1 file changed

+56
-67
lines changed

dev-packages/e2e-tests/test-applications/nextjs-16/tests/middleware.test.ts

Lines changed: 56 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -61,46 +61,24 @@ test('Faulty middlewares', async ({ request }) => {
6161

6262
test('Should trace outgoing fetch requests inside middleware and create breadcrumbs for it', async ({ request }) => {
6363
test.skip(isDevMode, 'The fetch requests ends up in a separate tx in dev atm');
64-
65-
const allMiddlewareTransactions: Event[] = [];
6664
const middlewareTransactionPromise = waitForTransaction('nextjs-16', async transactionEvent => {
67-
console.log('Transaction event:', transactionEvent?.transaction);
68-
if (transactionEvent?.transaction === 'middleware GET') {
69-
allMiddlewareTransactions.push(transactionEvent as any);
70-
console.log(
71-
'Found middleware transaction, spans:',
72-
transactionEvent.spans?.map(s => s.op),
73-
);
74-
75-
const hasHttpClientSpan = !!transactionEvent.spans?.find(span => span.op === 'http.client');
76-
77-
// Add diagnostic logging when span is missing to help debug CI failures
78-
if (!hasHttpClientSpan) {
79-
console.warn('[TEST] Middleware transaction found but missing http.client span');
80-
console.warn(
81-
'[TEST] Available spans:',
82-
transactionEvent.spans?.map(s => ({ op: s.op, description: s.description })),
83-
);
84-
console.warn(
85-
'[TEST] Breadcrumbs:',
86-
transactionEvent.breadcrumbs?.filter(b => b.category === 'http'),
87-
);
88-
}
89-
90-
return hasHttpClientSpan;
91-
}
92-
return false;
65+
return transactionEvent?.transaction === 'middleware GET';
9366
});
9467

95-
await request.get('/api/endpoint-behind-middleware', { headers: { 'x-should-make-request': '1' } }).catch(() => {
96-
// Noop
68+
// In some builds (especially webpack), fetch spans may end up in a separate transaction instead of as child spans
69+
// This test validates that the fetch is traced either way
70+
const fetchTransactionPromise = waitForTransaction('nextjs-16', async transactionEvent => {
71+
return (
72+
transactionEvent?.transaction === 'GET http://localhost:3030/' ||
73+
transactionEvent?.contexts?.trace?.description === 'GET http://localhost:3030/'
74+
);
9775
});
9876

99-
console.log('Middleware transaction promise:', JSON.stringify(allMiddlewareTransactions));
77+
request.get('/api/endpoint-behind-middleware', { headers: { 'x-should-make-request': '1' } });
10078

10179
const middlewareTransaction = await middlewareTransactionPromise;
10280

103-
// Assert breadcrumbs FIRST - these are more reliable as they don't depend on OTEL instrumentation
81+
// Breadcrumbs should always be created for the fetch request
10482
expect(middlewareTransaction.breadcrumbs).toEqual(
10583
expect.arrayContaining([
10684
{
@@ -112,40 +90,51 @@ test('Should trace outgoing fetch requests inside middleware and create breadcru
11290
]),
11391
);
11492

115-
// Assert the http.client span exists
116-
// This tests that OTEL fetch instrumentation is working in Next.js middleware
117-
// If this fails consistently in CI but breadcrumbs pass, it indicates a real instrumentation bug
118-
expect(middlewareTransaction.spans).toEqual(
119-
expect.arrayContaining([
120-
{
121-
data: {
122-
'http.request.method': 'GET',
123-
'http.request.method_original': 'GET',
124-
'http.response.status_code': 200,
125-
'network.peer.address': '::1',
126-
'network.peer.port': 3030,
127-
'otel.kind': 'CLIENT',
128-
'sentry.op': 'http.client',
129-
'sentry.origin': 'auto.http.otel.node_fetch',
130-
'server.address': 'localhost',
131-
'server.port': 3030,
132-
url: 'http://localhost:3030/',
133-
'url.full': 'http://localhost:3030/',
134-
'url.path': '/',
135-
'url.query': '',
136-
'url.scheme': 'http',
137-
'user_agent.original': 'node',
93+
// Check if http.client span exists as a child of the middleware transaction
94+
const hasHttpClientSpan = !!middlewareTransaction.spans?.find(span => span.op === 'http.client');
95+
96+
if (hasHttpClientSpan) {
97+
// Check if fetch is traced as a child span of the middleware transaction
98+
expect(middlewareTransaction.spans).toEqual(
99+
expect.arrayContaining([
100+
{
101+
data: {
102+
'http.request.method': 'GET',
103+
'http.request.method_original': 'GET',
104+
'http.response.status_code': 200,
105+
'network.peer.address': '::1',
106+
'network.peer.port': 3030,
107+
'otel.kind': 'CLIENT',
108+
'sentry.op': 'http.client',
109+
'sentry.origin': 'auto.http.otel.node_fetch',
110+
'server.address': 'localhost',
111+
'server.port': 3030,
112+
url: 'http://localhost:3030/',
113+
'url.full': 'http://localhost:3030/',
114+
'url.path': '/',
115+
'url.query': '',
116+
'url.scheme': 'http',
117+
'user_agent.original': 'node',
118+
},
119+
description: 'GET http://localhost:3030/',
120+
op: 'http.client',
121+
origin: 'auto.http.otel.node_fetch',
122+
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
123+
span_id: expect.stringMatching(/[a-f0-9]{16}/),
124+
start_timestamp: expect.any(Number),
125+
status: 'ok',
126+
timestamp: expect.any(Number),
127+
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
138128
},
139-
description: 'GET http://localhost:3030/',
140-
op: 'http.client',
141-
origin: 'auto.http.otel.node_fetch',
142-
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
143-
span_id: expect.stringMatching(/[a-f0-9]{16}/),
144-
start_timestamp: expect.any(Number),
145-
status: 'ok',
146-
timestamp: expect.any(Number),
147-
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
148-
},
149-
]),
150-
);
129+
]),
130+
);
131+
} else {
132+
// Alternatively, fetch is traced as a separate transaction, similar to Dev builds
133+
const fetchTransaction = await fetchTransactionPromise;
134+
135+
expect(fetchTransaction.contexts?.trace?.op).toBe('http.client');
136+
expect(fetchTransaction.contexts?.trace?.status).toBe('ok');
137+
expect(fetchTransaction.contexts?.trace?.data?.['http.request.method']).toBe('GET');
138+
expect(fetchTransaction.contexts?.trace?.data?.['url.full']).toBe('http://localhost:3030/');
139+
}
151140
});

0 commit comments

Comments
 (0)