Skip to content

Commit 0f33366

Browse files
nicohrubeccodex
andauthored
test(node): Port basic and nested startSpan tests to streaming (#25214)
Exercise basic and nested span creation with the default streaming lifecycle. Keep static counterparts for every suite, including basic usage with children, to preserve transaction coverage. Part of #24140 --------- Co-authored-by: GPT-6 <codex@openai.com>
1 parent 37f012b commit 0f33366

12 files changed

Lines changed: 295 additions & 62 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as Sentry from '@sentry/node';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
4+
Sentry.init({
5+
traceLifecycle: 'static',
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
release: '1.0',
8+
tracesSampleRate: 1.0,
9+
transport: loggingTransport,
10+
});
11+
12+
Sentry.startSpan({ name: 'test_span' }, () => undefined);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { SENTRY_SEGMENT_NAME_SOURCE } from '@sentry/conventions/attributes';
2+
import { afterAll, expect, test } from 'vitest';
3+
import { cleanupChildProcesses, createRunner } from '../../../../utils/runner';
4+
5+
afterAll(() => {
6+
cleanupChildProcesses();
7+
});
8+
9+
test('sends a manually started root span with source custom', async () => {
10+
await createRunner(__dirname, 'scenario.ts')
11+
.expect({
12+
transaction: {
13+
transaction: 'test_span',
14+
transaction_info: { source: 'custom' },
15+
contexts: {
16+
trace: {
17+
span_id: expect.any(String),
18+
trace_id: expect.any(String),
19+
data: { [SENTRY_SEGMENT_NAME_SOURCE]: 'custom' },
20+
},
21+
},
22+
},
23+
})
24+
.start()
25+
.completed();
26+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as Sentry from '@sentry/node';
2+
import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes';
3+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
4+
5+
Sentry.init({
6+
traceLifecycle: 'static',
7+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
8+
tracesSampleRate: 1.0,
9+
transport: loggingTransport,
10+
release: '1.0.0',
11+
});
12+
13+
Sentry.startSpan({ name: 'test-span', op: 'test' }, segmentSpan => {
14+
Sentry.startSpan({ name: 'test-child-span', op: 'test-child' }, () => {
15+
// noop
16+
});
17+
18+
const inactiveSpan = Sentry.startInactiveSpan({
19+
name: 'test-inactive-span',
20+
links: [{ context: segmentSpan.spanContext(), attributes: { [SENTRY_LINK_TYPE]: 'some_relation' } }],
21+
});
22+
inactiveSpan.end();
23+
24+
Sentry.startSpanManual({ name: 'test-manual-span' }, span => {
25+
span.end();
26+
});
27+
});
28+
29+
void Sentry.flush();
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core';
2+
import { SENTRY_LINK_TYPE, SENTRY_OP, SENTRY_ORIGIN, SENTRY_SEGMENT_NAME_SOURCE } from '@sentry/conventions/attributes';
3+
import { expect, test } from 'vitest';
4+
import { EXPECTED_SDK_NAME } from '../../../../../utils';
5+
import { assertSentryTransaction } from '../../../../../utils/assertions';
6+
import { createRunner } from '../../../../../utils/runner';
7+
8+
test('sends a transaction envelope with correct envelope header', async () => {
9+
await createRunner(__dirname, 'scenario.ts')
10+
.expectHeader({
11+
transaction: {
12+
sent_at: expect.any(String),
13+
sdk: {
14+
name: EXPECTED_SDK_NAME,
15+
version: SDK_VERSION,
16+
},
17+
trace: expect.objectContaining({
18+
public_key: 'public',
19+
sample_rate: '1',
20+
sampled: 'true',
21+
trace_id: expect.stringMatching(/^[\da-f]{32}$/),
22+
transaction: 'test-span',
23+
}),
24+
},
25+
})
26+
.start()
27+
.completed();
28+
});
29+
30+
test('sends a transaction with correct spans for a manually started span with children', async () => {
31+
await createRunner(__dirname, 'scenario.ts')
32+
.expect({
33+
transaction: transaction => {
34+
const rootSpanId = transaction.contexts?.trace?.span_id;
35+
const traceId = transaction.contexts?.trace?.trace_id;
36+
37+
assertSentryTransaction(transaction, {
38+
transaction: 'test-span',
39+
transaction_info: { source: 'custom' },
40+
release: '1.0.0',
41+
environment: 'production',
42+
sdk: { name: EXPECTED_SDK_NAME, version: SDK_VERSION },
43+
contexts: {
44+
trace: {
45+
span_id: expect.stringMatching(/^[\da-f]{16}$/),
46+
trace_id: expect.stringMatching(/^[\da-f]{32}$/),
47+
op: 'test',
48+
origin: 'manual',
49+
status: 'ok',
50+
data: {
51+
[SENTRY_OP]: 'test',
52+
[SENTRY_ORIGIN]: 'manual',
53+
[SENTRY_SEGMENT_NAME_SOURCE]: 'custom',
54+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
55+
},
56+
},
57+
},
58+
});
59+
60+
expect(transaction.spans).toEqual([
61+
{
62+
description: 'test-child-span',
63+
op: 'test-child',
64+
origin: 'manual',
65+
data: { [SENTRY_OP]: 'test-child', [SENTRY_ORIGIN]: 'manual' },
66+
parent_span_id: rootSpanId,
67+
trace_id: traceId,
68+
span_id: expect.stringMatching(/^[\da-f]{16}$/),
69+
start_timestamp: expect.any(Number),
70+
timestamp: expect.any(Number),
71+
status: 'ok',
72+
},
73+
{
74+
description: 'test-inactive-span',
75+
origin: 'manual',
76+
data: { [SENTRY_ORIGIN]: 'manual' },
77+
parent_span_id: rootSpanId,
78+
trace_id: traceId,
79+
span_id: expect.stringMatching(/^[\da-f]{16}$/),
80+
start_timestamp: expect.any(Number),
81+
timestamp: expect.any(Number),
82+
status: 'ok',
83+
links: [
84+
{
85+
attributes: { [SENTRY_LINK_TYPE]: 'some_relation' },
86+
sampled: true,
87+
span_id: rootSpanId,
88+
trace_id: traceId,
89+
},
90+
],
91+
},
92+
{
93+
description: 'test-manual-span',
94+
origin: 'manual',
95+
data: { [SENTRY_ORIGIN]: 'manual' },
96+
parent_span_id: rootSpanId,
97+
trace_id: traceId,
98+
span_id: expect.stringMatching(/^[\da-f]{16}$/),
99+
start_timestamp: expect.any(Number),
100+
timestamp: expect.any(Number),
101+
status: 'ok',
102+
},
103+
]);
104+
},
105+
})
106+
.start()
107+
.completed();
108+
});

‎dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/scenario.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as Sentry from '@sentry/node';
22
import { loggingTransport } from '@sentry-internal/node-integration-tests';
33

44
Sentry.init({
5-
traceLifecycle: 'static',
65
dsn: 'https://public@dsn.ingest.sentry.io/1337',
76
release: '1.0',
87
tracesSampleRate: 1.0,

‎dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/test.ts‎

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,14 @@ afterAll(() => {
99
test('sends a manually started root span with source custom', async () => {
1010
await createRunner(__dirname, 'scenario.ts')
1111
.expect({
12-
transaction: {
13-
transaction: 'test_span',
14-
transaction_info: { source: 'custom' },
15-
contexts: {
16-
trace: {
17-
span_id: expect.any(String),
18-
trace_id: expect.any(String),
19-
data: { [SENTRY_SEGMENT_NAME_SOURCE]: 'custom' },
20-
},
21-
},
22-
},
23-
})
24-
.start()
25-
.completed();
26-
});
27-
28-
test("doesn't change the name for manually started spans even if attributes triggering inference are set", async () => {
29-
await createRunner(__dirname, 'scenario.ts')
30-
.expect({
31-
transaction: {
32-
transaction: 'test_span',
33-
transaction_info: { source: 'custom' },
34-
contexts: {
35-
trace: {
36-
span_id: expect.any(String),
37-
trace_id: expect.any(String),
38-
data: { [SENTRY_SEGMENT_NAME_SOURCE]: 'custom' },
39-
},
40-
},
12+
span: container => {
13+
const segment = container.items.find(span => span.is_segment);
14+
expect(segment?.name).toBe('test_span');
15+
expect(segment?.span_id).toEqual(expect.any(String));
16+
expect(segment?.trace_id).toEqual(expect.any(String));
17+
expect(segment?.start_timestamp).toEqual(expect.any(Number));
18+
expect(segment?.end_timestamp).toEqual(expect.any(Number));
19+
expect(segment?.attributes[SENTRY_SEGMENT_NAME_SOURCE]).toEqual({ type: 'string', value: 'custom' });
4120
},
4221
})
4322
.start()

dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage-streamed/scenario.ts renamed to dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/with-children/scenario.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { loggingTransport } from '@sentry-internal/node-integration-tests';
44
Sentry.init({
55
dsn: 'https://public@dsn.ingest.sentry.io/1337',
66
tracesSampleRate: 1.0,
7-
traceLifecycle: 'stream',
87
integrations: [Sentry.spanStreamingIntegration()],
98
transport: loggingTransport,
109
release: '1.0.0',

dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage-streamed/test.ts renamed to dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/with-children/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
SENTRY_ORIGIN,
1414
} from '@sentry/conventions/attributes';
1515
import { expect, test } from 'vitest';
16-
import { createRunner } from '../../../../utils/runner';
17-
import { EXPECTED_SDK_NAME } from '../../../../utils';
16+
import { createRunner } from '../../../../../utils/runner';
17+
import { EXPECTED_SDK_NAME } from '../../../../../utils';
1818

1919
test('sends a streamed span envelope with correct envelope header', async () => {
2020
await createRunner(__dirname, 'scenario.ts')
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as Sentry from '@sentry/node';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
4+
Sentry.init({
5+
traceLifecycle: 'static',
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
release: '1.0',
8+
tracesSampleRate: 1.0,
9+
transport: loggingTransport,
10+
});
11+
12+
Sentry.startSpan({ name: 'root_span' }, () => {
13+
Sentry.startSpan(
14+
{
15+
name: 'span_1',
16+
attributes: {
17+
foo: 'bar',
18+
baz: [1, 2, 3],
19+
},
20+
},
21+
() => undefined,
22+
);
23+
24+
// span_2 doesn't finish
25+
Sentry.startInactiveSpan({ name: 'span_2' });
26+
27+
Sentry.startSpan({ name: 'span_3' }, () => {
28+
// span_4 is the child of span_3 but doesn't finish.
29+
Sentry.startInactiveSpan({ name: 'span_4', attributes: { qux: 'quux' } });
30+
31+
// span_5 is another child of span_3 but finishes.
32+
Sentry.startSpan({ name: 'span_5' }, () => undefined);
33+
});
34+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { SpanJSON } from '@sentry/core';
2+
import { afterAll, expect, test } from 'vitest';
3+
import { assertSentryTransaction } from '../../../../utils/assertions';
4+
import { cleanupChildProcesses, createRunner } from '../../../../utils/runner';
5+
6+
afterAll(() => {
7+
cleanupChildProcesses();
8+
});
9+
10+
test('should report finished spans as children of the root transaction.', async () => {
11+
await createRunner(__dirname, 'scenario.ts')
12+
.expect({
13+
transaction: transaction => {
14+
const rootSpanId = transaction.contexts?.trace?.span_id;
15+
const span3Id = transaction.spans?.[1]?.span_id;
16+
17+
expect(rootSpanId).toEqual(expect.any(String));
18+
expect(span3Id).toEqual(expect.any(String));
19+
20+
assertSentryTransaction(transaction, {
21+
transaction: 'root_span',
22+
spans: [
23+
{
24+
description: 'span_1',
25+
data: {
26+
foo: 'bar',
27+
baz: [1, 2, 3],
28+
},
29+
parent_span_id: rootSpanId,
30+
},
31+
{
32+
description: 'span_3',
33+
parent_span_id: rootSpanId,
34+
data: {},
35+
},
36+
{
37+
description: 'span_5',
38+
parent_span_id: span3Id,
39+
data: {},
40+
},
41+
] as SpanJSON[],
42+
});
43+
},
44+
})
45+
.start()
46+
.completed();
47+
});

0 commit comments

Comments
 (0)