Skip to content

Commit 1379591

Browse files
authored
test(node): Add Node SDK integration test structure. (#4694)
1 parent 29fb2a8 commit 1379591

File tree

17 files changed

+382
-5
lines changed

17 files changed

+382
-5
lines changed

.github/workflows/build.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,36 @@ jobs:
421421
run: |
422422
cd packages/utils
423423
yarn test:package
424+
425+
job_node_integration_tests:
426+
name: Node SDK Integration Tests (${{ matrix.node }})
427+
needs: job_build
428+
runs-on: ubuntu-latest
429+
timeout-minutes: 10
430+
continue-on-error: true
431+
strategy:
432+
matrix:
433+
node: [10, 12, 14, 16]
434+
steps:
435+
- name: Check out current commit (${{ github.sha }})
436+
uses: actions/checkout@v2
437+
- name: Set up Node
438+
uses: actions/setup-node@v1
439+
with:
440+
node-version: ${{ matrix.node }}
441+
- name: Check dependency cache
442+
uses: actions/cache@v2
443+
with:
444+
path: ${{ env.CACHED_DEPENDENCY_PATHS }}
445+
key: ${{ needs.job_build.outputs.dependency_cache_key }}
446+
- name: Check build cache
447+
uses: actions/cache@v2
448+
with:
449+
path: ${{ env.CACHED_BUILD_PATHS }}
450+
key: ${{ env.BUILD_CACHE_KEY }}
451+
- name: Run integration tests
452+
env:
453+
NODE_VERSION: ${{ matrix.node }}
454+
run: |
455+
cd packages/node-integration-tests
456+
yarn test

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"lint:eslint": "lerna run --parallel lint:eslint",
2323
"prepublishOnly": "lerna run --stream --concurrency 1 prepublishOnly",
2424
"postpublish": "make publish-docs && lerna run --stream --concurrency 1 postpublish",
25-
"test": "lerna run --ignore @sentry-internal/browser-integration-tests --stream --concurrency 1 --sort test",
25+
"test": "lerna run --ignore @sentry-internal/browser-integration-tests @sentry-internal/node-integration-tests --stream --concurrency 1 --sort test",
2626
"test-ci": "ts-node ./scripts/test.ts"
2727
},
2828
"volta": {
@@ -43,6 +43,7 @@
4343
"packages/minimal",
4444
"packages/nextjs",
4545
"packages/node",
46+
"packages/node-integration-tests",
4647
"packages/react",
4748
"packages/serverless",
4849
"packages/tracing",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = {
2+
env: {
3+
node: true,
4+
jest: true,
5+
},
6+
extends: ['../../.eslintrc.js'],
7+
parserOptions: {
8+
sourceType: 'module',
9+
},
10+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const config = {
2+
transform: {
3+
'^.+\\.ts$': 'ts-jest',
4+
},
5+
testEnvironment: 'node',
6+
testMatch: ['**/test.ts'],
7+
moduleFileExtensions: ['js', 'ts'],
8+
};
9+
10+
module.exports = config;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@sentry-internal/node-integration-tests",
3+
"version": "6.18.2",
4+
"license": "MIT",
5+
"engines": {
6+
"node": ">=10"
7+
},
8+
"private": true,
9+
"scripts": {
10+
"lint": "run-s lint:prettier lint:eslint",
11+
"lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish",
12+
"lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"",
13+
"type-check": "tsc",
14+
"test": "jest --detectOpenHandles --runInBand --forceExit"
15+
},
16+
"dependencies": {
17+
"express": "^4.17.3",
18+
"nock": "^13.1.0",
19+
"portfinder": "^1.0.28"
20+
}
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/node';
2+
3+
Sentry.init({
4+
dsn: 'https://[email protected]/1337',
5+
release: '1.0',
6+
});
7+
8+
Sentry.captureException(new Error('Captured Error'));
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { assertSentryEvent, getEventRequest, runServer } from '../../../utils';
2+
3+
test('should send captureException', async () => {
4+
const url = await runServer(__dirname);
5+
const requestBody = await getEventRequest(url);
6+
7+
assertSentryEvent(requestBody, {
8+
exception: {
9+
values: [
10+
{
11+
type: 'Error',
12+
value: 'Captured Error',
13+
},
14+
],
15+
},
16+
});
17+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/node';
2+
3+
Sentry.init({
4+
dsn: 'https://[email protected]/1337',
5+
release: '1.0',
6+
});
7+
8+
Sentry.captureMessage('Message');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { assertSentryEvent, getEventRequest, runServer } from '../../../utils';
2+
3+
test('should send captureMessage', async () => {
4+
const url = await runServer(__dirname);
5+
const requestBody = await getEventRequest(url);
6+
7+
assertSentryEvent(requestBody, {
8+
message: 'Message',
9+
level: 'info',
10+
});
11+
});
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 * as _ from '@sentry/tracing';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
});
9+
10+
const transaction = Sentry.startTransaction({ name: 'test_transaction_1' });
11+
12+
transaction.finish();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { assertSentryTransaction, getEnvelopeRequest, runServer } from '../../../utils';
2+
3+
test.skip('should send a manually started transaction when @sentry/tracing is imported using namespace import.', async () => {
4+
const url = await runServer(__dirname);
5+
const envelope = await getEnvelopeRequest(url);
6+
7+
expect(envelope).toHaveLength(3);
8+
9+
assertSentryTransaction(envelope[2], {
10+
transaction: 'test_transaction_1',
11+
});
12+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2+
import '@sentry/tracing';
3+
4+
import * as Sentry from '@sentry/node';
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
release: '1.0',
9+
tracesSampleRate: 1.0,
10+
});
11+
12+
const transaction = Sentry.startTransaction({ name: 'test_transaction_1' });
13+
14+
transaction.finish();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { assertSentryTransaction, getEnvelopeRequest, runServer } from '../../../utils';
2+
3+
test('should send a manually started transaction when @sentry/tracing is imported using unnamed import.', async () => {
4+
const url = await runServer(__dirname);
5+
const envelope = await getEnvelopeRequest(url);
6+
7+
expect(envelope).toHaveLength(3);
8+
9+
assertSentryTransaction(envelope[2], {
10+
transaction: 'test_transaction_1',
11+
});
12+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"esModuleInterop": true,
5+
"types": ["jest", "node"]
6+
},
7+
"include": ["**/*.ts", "jest.config.js"],
8+
"exclude": ["node_modules"]
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import express from 'express';
2+
3+
const app = express();
4+
5+
export default app;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
2+
import { Express } from 'express';
3+
import * as http from 'http';
4+
import nock from 'nock';
5+
import * as path from 'path';
6+
import { getPortPromise } from 'portfinder';
7+
8+
const assertSentryEvent = (actual: Record<string, unknown>, expected: Record<string, unknown>): void => {
9+
expect(actual).toMatchObject({
10+
event_id: expect.any(String),
11+
timestamp: expect.any(Number),
12+
...expected,
13+
});
14+
};
15+
16+
const assertSentryTransaction = (actual: Record<string, unknown>, expected: Record<string, unknown>): void => {
17+
expect(actual).toMatchObject({
18+
event_id: expect.any(String),
19+
timestamp: expect.any(Number),
20+
start_timestamp: expect.any(Number),
21+
spans: expect.any(Array),
22+
type: 'transaction',
23+
...expected,
24+
});
25+
};
26+
27+
const parseEnvelope = (body: string): Array<Record<string, unknown>> => {
28+
return body.split('\n').map(e => JSON.parse(e));
29+
};
30+
31+
const getEventRequest = async (url: string): Promise<Record<string, unknown>> => {
32+
return new Promise(resolve => {
33+
nock('https://dsn.ingest.sentry.io')
34+
.post('/api/1337/store/', body => {
35+
resolve(body);
36+
return true;
37+
})
38+
.reply(200);
39+
40+
http.get(url);
41+
});
42+
};
43+
44+
const getEnvelopeRequest = async (url: string): Promise<Array<Record<string, unknown>>> => {
45+
return new Promise(resolve => {
46+
nock('https://dsn.ingest.sentry.io')
47+
.post('/api/1337/envelope/', body => {
48+
const envelope = parseEnvelope(body);
49+
resolve(envelope);
50+
return true;
51+
})
52+
.reply(200);
53+
54+
http.get(url);
55+
});
56+
};
57+
58+
async function runServer(testDir: string, serverPath?: string, scenarioPath?: string): Promise<string> {
59+
const port = await getPortPromise();
60+
const url = `http://localhost:${port}/test`;
61+
const defaultServerPath = path.resolve(process.cwd(), 'utils', 'defaults', 'server');
62+
63+
await new Promise(resolve => {
64+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
65+
const app = require(serverPath || defaultServerPath).default as Express;
66+
67+
app.get('/test', async () => {
68+
require(scenarioPath || `${testDir}/scenario`);
69+
70+
setTimeout(() => server.close(), 500);
71+
});
72+
73+
const server = app.listen(port, () => {
74+
resolve();
75+
});
76+
});
77+
78+
return url;
79+
}
80+
81+
export { assertSentryEvent, assertSentryTransaction, parseEnvelope, getEventRequest, getEnvelopeRequest, runServer };

0 commit comments

Comments
 (0)