Skip to content

Commit 120cc5b

Browse files
committed
support server in scenario and making requests to it
1 parent 48587cd commit 120cc5b

File tree

5 files changed

+185
-102
lines changed

5 files changed

+185
-102
lines changed

dev-packages/node-integration-tests/src/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import type { AddressInfo } from 'net';
12
import type { BaseTransportOptions, Envelope, Transport, TransportMakeRequestResponse } from '@sentry/types';
3+
import type { Express } from 'express';
24

35
/**
46
* Debug logging transport
@@ -15,3 +17,15 @@ export function loggingTransport(_options: BaseTransportOptions): Transport {
1517
},
1618
};
1719
}
20+
21+
/**
22+
* Starts an express server and sends the port to the runner
23+
*/
24+
export function startExpressServerAndSendPortToRunner(app: Express): void {
25+
const server = app.listen(0, () => {
26+
const address = server.address() as AddressInfo;
27+
28+
// eslint-disable-next-line no-console
29+
console.log(`{"port":${address.port}}`);
30+
});
31+
}

dev-packages/node-integration-tests/suites/express/tracing/server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { loggingTransport, startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests';
12
import * as Sentry from '@sentry/node';
23
import cors from 'cors';
34
import express from 'express';
@@ -11,6 +12,7 @@ Sentry.init({
1112
tracePropagationTargets: [/^(?!.*test).*$/],
1213
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Express({ app })],
1314
tracesSampleRate: 1.0,
15+
transport: loggingTransport,
1416
});
1517

1618
app.use(Sentry.Handlers.requestHandler());
@@ -36,4 +38,4 @@ app.get(['/test/arr/:id', /\/test\/arr[0-9]*\/required(path)?(\/optionalPath)?\/
3638

3739
app.use(Sentry.Handlers.errorHandler());
3840

39-
export default app;
41+
startExpressServerAndSendPortToRunner(app);
Lines changed: 117 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,101 @@
1-
import { TestEnv, assertSentryTransaction } from '../../../utils/index';
1+
import { assertSentryTransaction, createRunner } from '../../../utils/runner';
22

3-
test('should create and send transactions for Express routes and spans for middlewares.', async () => {
4-
const env = await TestEnv.init(__dirname, `${__dirname}/server.ts`);
5-
const envelope = await env.getEnvelopeRequest({ url: `${env.url}/express`, envelopeType: 'transaction' });
6-
7-
expect(envelope).toHaveLength(3);
8-
9-
assertSentryTransaction(envelope[2], {
10-
contexts: {
11-
trace: {
12-
data: {
13-
url: '/test/express',
14-
'http.response.status_code': 200,
15-
},
16-
op: 'http.server',
17-
status: 'ok',
18-
tags: {
19-
'http.status_code': '200',
20-
},
21-
},
22-
},
23-
spans: [
24-
{
25-
description: 'corsMiddleware',
26-
op: 'middleware.express.use',
3+
test('should create and send transactions for Express routes and spans for middlewares.', done => {
4+
createRunner(__dirname, 'server.ts')
5+
.expect({
6+
transaction: transaction => {
7+
assertSentryTransaction(transaction, {
8+
contexts: {
9+
trace: {
10+
span_id: expect.any(String),
11+
trace_id: expect.any(String),
12+
data: {
13+
url: '/test/express',
14+
'http.response.status_code': 200,
15+
},
16+
op: 'http.server',
17+
status: 'ok',
18+
tags: {
19+
'http.status_code': '200',
20+
},
21+
},
22+
},
23+
spans: [
24+
expect.objectContaining({
25+
description: 'corsMiddleware',
26+
op: 'middleware.express.use',
27+
}),
28+
],
29+
});
2730
},
28-
],
29-
});
31+
})
32+
.start(done)
33+
.makeRequest('get', '/test/express');
3034
});
3135

32-
test('should set a correct transaction name for routes specified in RegEx', async () => {
33-
const env = await TestEnv.init(__dirname, `${__dirname}/server.ts`);
34-
const envelope = await env.getEnvelopeRequest({ url: `${env.url}/regex`, envelopeType: 'transaction' });
35-
36-
expect(envelope).toHaveLength(3);
37-
38-
assertSentryTransaction(envelope[2], {
39-
transaction: 'GET /\\/test\\/regex/',
40-
transaction_info: {
41-
source: 'route',
42-
},
43-
contexts: {
44-
trace: {
45-
data: {
46-
url: '/test/regex',
47-
'http.response.status_code': 200,
48-
},
49-
op: 'http.server',
50-
status: 'ok',
51-
tags: {
52-
'http.status_code': '200',
53-
},
36+
test('should set a correct transaction name for routes specified in RegEx', done => {
37+
createRunner(__dirname, 'server.ts')
38+
.expect({
39+
transaction: transaction => {
40+
assertSentryTransaction(transaction, {
41+
transaction: 'GET /\\/test\\/regex/',
42+
transaction_info: {
43+
source: 'route',
44+
},
45+
contexts: {
46+
trace: {
47+
trace_id: expect.any(String),
48+
span_id: expect.any(String),
49+
data: {
50+
url: '/test/regex',
51+
'http.response.status_code': 200,
52+
},
53+
op: 'http.server',
54+
status: 'ok',
55+
tags: {
56+
'http.status_code': '200',
57+
},
58+
},
59+
},
60+
});
5461
},
55-
},
56-
});
62+
})
63+
.start(done)
64+
.makeRequest('get', '/test/regex');
5765
});
5866

5967
test.each([['array1'], ['array5']])(
6068
'should set a correct transaction name for routes consisting of arrays of routes',
61-
async segment => {
62-
const env = await TestEnv.init(__dirname, `${__dirname}/server.ts`);
63-
const envelope = await env.getEnvelopeRequest({ url: `${env.url}/${segment}`, envelopeType: 'transaction' });
64-
65-
expect(envelope).toHaveLength(3);
66-
67-
assertSentryTransaction(envelope[2], {
68-
transaction: 'GET /test/array1,/\\/test\\/array[2-9]',
69-
transaction_info: {
70-
source: 'route',
71-
},
72-
contexts: {
73-
trace: {
74-
data: {
75-
url: `/test/${segment}`,
76-
'http.response.status_code': 200,
77-
},
78-
op: 'http.server',
79-
status: 'ok',
80-
tags: {
81-
'http.status_code': '200',
82-
},
69+
((segment: string, done: () => void) => {
70+
createRunner(__dirname, 'server.ts')
71+
.expect({
72+
transaction: transaction => {
73+
assertSentryTransaction(transaction, {
74+
transaction: 'GET /test/array1,/\\/test\\/array[2-9]',
75+
transaction_info: {
76+
source: 'route',
77+
},
78+
contexts: {
79+
trace: {
80+
trace_id: expect.any(String),
81+
span_id: expect.any(String),
82+
data: {
83+
url: `/test/${segment}`,
84+
'http.response.status_code': 200,
85+
},
86+
op: 'http.server',
87+
status: 'ok',
88+
tags: {
89+
'http.status_code': '200',
90+
},
91+
},
92+
},
93+
});
8394
},
84-
},
85-
});
86-
},
95+
})
96+
.start(done)
97+
.makeRequest('get', `/test/${segment}`);
98+
}) as any,
8799
);
88100

89101
test.each([
@@ -95,29 +107,33 @@ test.each([
95107
['arr55/required/lastParam'],
96108
['arr/requiredPath/optionalPath/'],
97109
['arr/requiredPath/optionalPath/lastParam'],
98-
])('should handle more complex regexes in route arrays correctly', async segment => {
99-
const env = await TestEnv.init(__dirname, `${__dirname}/server.ts`);
100-
const envelope = await env.getEnvelopeRequest({ url: `${env.url}/${segment}`, envelopeType: 'transaction' });
101-
102-
expect(envelope).toHaveLength(3);
103-
104-
assertSentryTransaction(envelope[2], {
105-
transaction: 'GET /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?',
106-
transaction_info: {
107-
source: 'route',
108-
},
109-
contexts: {
110-
trace: {
111-
data: {
112-
url: `/test/${segment}`,
113-
'http.response.status_code': 200,
114-
},
115-
op: 'http.server',
116-
status: 'ok',
117-
tags: {
118-
'http.status_code': '200',
119-
},
110+
])('should handle more complex regexes in route arrays correctly', ((segment: string, done: () => void) => {
111+
createRunner(__dirname, 'server.ts')
112+
.expect({
113+
transaction: transaction => {
114+
assertSentryTransaction(transaction, {
115+
transaction: 'GET /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?',
116+
transaction_info: {
117+
source: 'route',
118+
},
119+
contexts: {
120+
trace: {
121+
trace_id: expect.any(String),
122+
span_id: expect.any(String),
123+
data: {
124+
url: `/test/${segment}`,
125+
'http.response.status_code': 200,
126+
},
127+
op: 'http.server',
128+
status: 'ok',
129+
tags: {
130+
'http.status_code': '200',
131+
},
132+
},
133+
},
134+
});
120135
},
121-
},
122-
});
123-
});
136+
})
137+
.start(done)
138+
.makeRequest('get', `/test/${segment}`);
139+
}) as any);

dev-packages/node-integration-tests/utils/runner.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { spawn } from 'child_process';
22
import { join } from 'path';
33
import type { Envelope, EnvelopeItemType, Event, SerializedSession } from '@sentry/types';
4+
import axios from 'axios';
45

56
export function assertSentryEvent(actual: Event, expected: Event): void {
67
expect(actual).toMatchObject({
@@ -16,6 +17,17 @@ export function assertSentrySession(actual: SerializedSession, expected: Partial
1617
});
1718
}
1819

20+
export function assertSentryTransaction(actual: Event, expected: Partial<Event>): void {
21+
expect(actual).toMatchObject({
22+
event_id: expect.any(String),
23+
timestamp: expect.anything(),
24+
start_timestamp: expect.anything(),
25+
spans: expect.any(Array),
26+
type: 'transaction',
27+
...expected,
28+
});
29+
}
30+
1931
type Expected =
2032
| {
2133
event: (event: Event) => void;
@@ -57,6 +69,7 @@ export function createRunner(...paths: string[]) {
5769
start: function (done?: (e?: unknown) => void) {
5870
const expectedEnvelopeCount = expectedEnvelopes.length;
5971
let envelopeCount = 0;
72+
let serverPort: number | undefined;
6073

6174
const child = spawn('node', [...flags, testPath]);
6275

@@ -69,6 +82,17 @@ export function createRunner(...paths: string[]) {
6982
done?.(e);
7083
});
7184

85+
async function waitForServerPort(timeout = 10_000): Promise<void> {
86+
let remaining = timeout;
87+
while (serverPort === undefined) {
88+
await new Promise<void>(resolve => setTimeout(resolve, 100));
89+
remaining -= 100;
90+
if (remaining < 0) {
91+
throw new Error('Timed out waiting for server port');
92+
}
93+
}
94+
}
95+
7296
function checkDone(): void {
7397
envelopeCount++;
7498
if (envelopeCount === expectedEnvelopeCount) {
@@ -81,6 +105,12 @@ export function createRunner(...paths: string[]) {
81105
// Lines can have leading '[something] [{' which we need to remove
82106
const cleanedLine = line.replace(/^.*?] \[{"/, '[{"');
83107

108+
if (cleanedLine.startsWith('{"port":')) {
109+
const { port } = JSON.parse(cleanedLine) as { port: number };
110+
serverPort = port;
111+
return;
112+
}
113+
84114
if (!cleanedLine.startsWith('[{')) {
85115
return;
86116
}
@@ -156,6 +186,25 @@ export function createRunner(...paths: string[]) {
156186
childHasExited: function (): boolean {
157187
return hasExited;
158188
},
189+
makeRequest: async function <T>(
190+
method: 'get' | 'post',
191+
path: string,
192+
headers: Record<string, string> = {},
193+
): Promise<T | undefined> {
194+
try {
195+
await waitForServerPort();
196+
197+
const url = `http://localhost:${serverPort}${path}`;
198+
if (method === 'get') {
199+
return (await axios.get(url, { headers })).data;
200+
} else {
201+
return (await axios.post(url, { headers })).data;
202+
}
203+
} catch (e) {
204+
done?.(e);
205+
return undefined;
206+
}
207+
},
159208
};
160209
},
161210
};

packages/node/src/integrations/anr/worker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ async function sendAbnormalSession(): Promise<void> {
4747
updateSession(session, { status: 'abnormal', abnormal_mechanism: 'anr_foreground' });
4848

4949
const envelope = createSessionEnvelope(session, options.dsn, options.sdkMetadata);
50+
// Log the envelope so to aid in testing
5051
log(JSON.stringify(envelope));
5152

5253
await transport.send(envelope);
@@ -118,6 +119,7 @@ async function sendAnrEvent(frames?: StackFrame[], traceContext?: TraceContext):
118119
};
119120

120121
const envelope = createEventEnvelope(event, options.dsn, options.sdkMetadata);
122+
// Log the envelope so to aid in testing
121123
log(JSON.stringify(envelope));
122124

123125
await transport.send(envelope);

0 commit comments

Comments
 (0)