Skip to content

Commit d9cd3a0

Browse files
JPeer264claude
andauthored
test(cloudflare): Port the request handler suites to span streaming (#24196)
Ports the remaining request handler suites off the `traceLifecycle: 'static'` pin: `hono-sdk`, `double-instrumentation` and `public-api/metrics/server-address`. `double-instrumentation` and the metrics suite assert on an error event and on a metric envelope, so only the pin goes. `double-instrumentation` swaps its `ignore('transaction')` for `ignore('span')`, which is the envelope the `http.server` span now races the error event with. The Hono routes are parametrized patterns, so their segment names survive streaming unchanged. What does change is the status: span v2 carries the coarse `error` on the span and the specific `internal_error` in the `sentry.status.message` attribute. ## Static trace lifecycle guard `suites/basic` keeps the static pin as the package's guard. It is the smallest suite that still covers a plain worker fetch, so it is the cheapest one to keep on the old lifecycle. ## Suite left behind `suites/request-handler/subpath` also stays pinned, but not by choice. With the pin removed, `wrapRequestHandler` from `@sentry/cloudflare/request` still sends a transaction envelope. That holds with `traceLifecycle: 'stream'` passed explicitly and with `cacheClient: false`, and the transaction's own `sentry.sdk.integrations` lists `SpanStreaming` — so the client the event is processed by does have streaming on, while `getClient()` inside `_onSpanEnded` does not see a streaming client and falls through to `_convertSpanToTransaction`. Every other entry point in the package streams, and they differ only in which default integration set they inject, so this looks like a gap in the `/request` entry rather than a test problem. It is tracked separately and the suite is left as it was. Fixes #24148 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 3740892 commit d9cd3a0

5 files changed

Lines changed: 32 additions & 65 deletions

File tree

‎dev-packages/cloudflare-integration-tests/suites/double-instrumentation/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ const handler = {
1717
// This simulates scenarios where the module is re-evaluated or the handler
1818
// is wrapped multiple times. The SDK should handle this gracefully
1919
// without double-wrapping (which would cause duplicate error reports).
20-
const once = Sentry.withSentry((env: Env) => ({ dsn: env.SENTRY_DSN, traceLifecycle: 'static' }), handler);
21-
export default Sentry.withSentry((env: Env) => ({ dsn: env.SENTRY_DSN, traceLifecycle: 'static' }), once);
20+
const once = Sentry.withSentry((env: Env) => ({ dsn: env.SENTRY_DSN }), handler);
21+
export default Sentry.withSentry((env: Env) => ({ dsn: env.SENTRY_DSN }), once);

‎dev-packages/cloudflare-integration-tests/suites/double-instrumentation/test.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,9 @@ it('Only sends one error event when withSentry is called twice', async ({ signal
3131
{ includeTransaction: false },
3232
),
3333
)
34-
// The http.server span produces a transaction envelope that is sent in parallel with the
35-
// error event. Either can arrive first at the mock server, so ignore it here to keep the
36-
// assertion focused on the error event.
37-
.ignore('transaction')
34+
// The http.server span is streamed in parallel with the error event. Either can arrive first
35+
// at the mock server, so ignore it here to keep the assertion focused on the error event.
36+
.ignore('span')
3837
.start(signal);
3938
await runner.makeRequest('get', '/error', { expectError: true });
4039
await runner.completed();

‎dev-packages/cloudflare-integration-tests/suites/hono-sdk/index.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const app = new Hono<{ Bindings: Env }>();
1010
app.use(
1111
sentry(app, {
1212
dsn: process.env.SENTRY_DSN,
13-
traceLifecycle: 'static',
1413
tracesSampleRate: 1.0,
1514
}),
1615
);

‎dev-packages/cloudflare-integration-tests/suites/hono-sdk/test.ts‎

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { expect, it } from 'vitest';
2-
import { eventEnvelope, SHORT_UUID_MATCHER, UUID_MATCHER } from '../../expect';
2+
import { eventEnvelope } from '../../expect';
33
import { createRunner } from '../../runner';
4+
import { getSpanOp, getSpansFromEnvelope } from '../../spanUtils';
45

56
it('Hono app captures parametrized errors (Hono SDK)', async ({ signal }) => {
67
const runner = createRunner(__dirname)
@@ -43,43 +44,23 @@ it('Hono app captures parametrized errors (Hono SDK)', async ({ signal }) => {
4344
{ includeSamplingFields: true, includeSampleRand: true, sdk: 'hono' },
4445
),
4546
)
46-
4747
.expect(envelope => {
48-
const [, envelopeItems] = envelope;
49-
const [itemHeader, itemPayload] = envelopeItems[0];
50-
51-
expect(itemHeader.type).toBe('transaction');
48+
const segmentSpan = getSpansFromEnvelope(envelope).find(span => span.is_segment);
5249

53-
expect(itemPayload).toMatchObject({
54-
type: 'transaction',
55-
platform: 'javascript',
56-
transaction: 'GET /error/:param',
57-
contexts: {
58-
trace: {
59-
span_id: expect.any(String),
60-
trace_id: expect.any(String),
61-
op: 'http.server',
62-
status: 'internal_error',
63-
origin: 'auto.http.cloudflare',
64-
},
65-
},
66-
request: expect.objectContaining({
67-
method: 'GET',
68-
url: expect.stringContaining('/error/param-123'),
50+
// The Hono route is a parametrized pattern, so the streamed segment keeps the full name.
51+
expect(segmentSpan?.name).toBe('GET /error/:param');
52+
// Span v2 keeps the coarse `error` status on the span and the specific one as an attribute.
53+
expect(segmentSpan?.status).toBe('error');
54+
expect(getSpanOp(segmentSpan!)).toBe('http.server');
55+
expect(segmentSpan?.attributes).toEqual(
56+
expect.objectContaining({
57+
'sentry.status.message': { type: 'string', value: 'internal_error' },
58+
'sentry.origin': { type: 'string', value: 'auto.http.cloudflare' },
59+
'sentry.segment.name.source': { type: 'string', value: 'route' },
60+
'http.request.method': { type: 'string', value: 'GET' },
61+
'url.path': { type: 'string', value: '/error/param-123' },
6962
}),
70-
breadcrumbs: [
71-
{
72-
timestamp: expect.any(Number),
73-
category: 'console',
74-
level: 'error',
75-
message: 'Error: Test error from Hono app',
76-
data: expect.objectContaining({
77-
logger: 'console',
78-
arguments: [{ message: 'Test error from Hono app', name: 'Error', stack: expect.any(String) }],
79-
}),
80-
},
81-
],
82-
});
63+
);
8364
})
8465
.unordered()
8566
.start(signal);
@@ -91,31 +72,20 @@ it('Hono app captures parametrized errors (Hono SDK)', async ({ signal }) => {
9172
it('Hono app captures parametrized names', async ({ signal }) => {
9273
const runner = createRunner(__dirname)
9374
.expect(envelope => {
94-
const [, envelopeItems] = envelope;
95-
const [itemHeader, itemPayload] = envelopeItems[0];
75+
const segmentSpan = getSpansFromEnvelope(envelope).find(span => span.is_segment);
9676

97-
expect(itemHeader.type).toBe('transaction');
98-
99-
expect(itemPayload).toMatchObject({
100-
type: 'transaction',
101-
platform: 'javascript',
102-
transaction: 'GET /hello/:name',
103-
contexts: {
104-
trace: {
105-
span_id: SHORT_UUID_MATCHER,
106-
trace_id: UUID_MATCHER,
107-
op: 'http.server',
108-
status: 'ok',
109-
origin: 'auto.http.cloudflare',
110-
},
111-
},
112-
request: expect.objectContaining({
113-
method: 'GET',
114-
url: expect.stringContaining('/hello/:name'),
77+
expect(segmentSpan?.name).toBe('GET /hello/:name');
78+
expect(segmentSpan?.status).toBe('ok');
79+
expect(getSpanOp(segmentSpan!)).toBe('http.server');
80+
expect(segmentSpan?.attributes).toEqual(
81+
expect.objectContaining({
82+
'sentry.origin': { type: 'string', value: 'auto.http.cloudflare' },
83+
'sentry.segment.name.source': { type: 'string', value: 'route' },
84+
'http.request.method': { type: 'string', value: 'GET' },
85+
'url.path': { type: 'string', value: '/hello/:name' },
11586
}),
116-
});
87+
);
11788
})
118-
11989
.unordered()
12090
.start(signal);
12191

‎dev-packages/cloudflare-integration-tests/suites/public-api/metrics/server-address/index.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ interface Env {
77
export default Sentry.withSentry(
88
(env: Env) => ({
99
dsn: env.SENTRY_DSN,
10-
traceLifecycle: 'static',
1110
release: '1.0.0',
1211
environment: 'test',
1312
serverName: 'mi-servidor.com',

0 commit comments

Comments
 (0)