Skip to content

Commit b9afed0

Browse files
committed
test(remix): Add server-side instrumentation integration tests.
1 parent 524fb6e commit b9afed0

File tree

9 files changed

+198
-11
lines changed

9 files changed

+198
-11
lines changed

packages/node-integration-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@types/mysql": "^2.15.21",
2424
"@types/pg": "^8.6.5",
2525
"apollo-server": "^3.6.7",
26+
"axios": "^0.27.2",
2627
"cors": "^2.8.5",
2728
"express": "^4.17.3",
2829
"graphql": "^16.3.0",

packages/node-integration-tests/utils/index.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
2-
import { parseSemver } from '@sentry/utils';
2+
import { logger, parseSemver } from '@sentry/utils';
3+
import axios from 'axios';
34
import { Express } from 'express';
45
import * as http from 'http';
56
import { RequestOptions } from 'https';
67
import nock from 'nock';
78
import * as path from 'path';
89
import { getPortPromise } from 'portfinder';
9-
1010
/**
1111
* Returns`describe` or `describe.skip` depending on allowed major versions of Node.
1212
*
@@ -71,12 +71,18 @@ export const parseEnvelope = (body: string): Array<Record<string, unknown>> => {
7171
* @param url The url the intercepted requests will be directed to.
7272
* @param count The expected amount of requests to the envelope endpoint. If
7373
* the amount of sentrequests is lower than`count`, this function will not resolve.
74+
* @param method The method of the request. Defaults to `GET`.
7475
* @returns The intercepted envelopes.
7576
*/
76-
export const getMultipleEnvelopeRequest = async (url: string, count: number): Promise<Record<string, unknown>[][]> => {
77+
export const getMultipleEnvelopeRequest = async (
78+
url: string,
79+
count: number,
80+
method: 'get' | 'post' = 'get',
81+
): Promise<Record<string, unknown>[][]> => {
7782
const envelopes: Record<string, unknown>[][] = [];
7883

79-
return new Promise(resolve => {
84+
// eslint-disable-next-line no-async-promise-executor
85+
return new Promise(async resolve => {
8086
nock('https://dsn.ingest.sentry.io')
8187
.post('/api/1337/envelope/', body => {
8288
const envelope = parseEnvelope(body);
@@ -92,7 +98,17 @@ export const getMultipleEnvelopeRequest = async (url: string, count: number): Pr
9298
.query(true) // accept any query params - used for sentry_key param
9399
.reply(200);
94100

95-
http.get(url);
101+
try {
102+
if (method === 'get') {
103+
await axios.get(url);
104+
} else {
105+
await axios.post(url);
106+
}
107+
} catch (e) {
108+
// We sometimes expect the request to fail, but not the test.
109+
// So, we do nothing.
110+
logger.warn(e);
111+
}
96112
});
97113
};
98114

@@ -133,10 +149,14 @@ export const getAPIResponse = async (url: URL, headers?: Record<string, string>)
133149
* Intercepts and extracts a single request containing a Sentry envelope
134150
*
135151
* @param url The url the intercepted request will be directed to.
152+
* @param method The method of the request. Defaults to `GET`.
136153
* @returns The extracted envelope.
137154
*/
138-
export const getEnvelopeRequest = async (url: string): Promise<Array<Record<string, unknown>>> => {
139-
return (await getMultipleEnvelopeRequest(url, 1))[0];
155+
export const getEnvelopeRequest = async (
156+
url: string,
157+
method: 'get' | 'post' = 'get',
158+
): Promise<Array<Record<string, unknown>>> => {
159+
return (await getMultipleEnvelopeRequest(url, 1, method))[0];
140160
};
141161

142162
/**

packages/remix/test/integration/app/root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const meta: MetaFunction = ({ data }) => ({
1010
baggage: data.sentryBaggage,
1111
});
1212

13-
function App() {
13+
export function App() {
1414
return (
1515
<html lang="en">
1616
<head>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ActionFunction, json } from '@remix-run/node';
2+
import { useActionData } from '@remix-run/react';
3+
4+
export const action: ActionFunction = async ({ params: { id } }) => {
5+
if (id === '-1') {
6+
throw new Error('Error');
7+
}
8+
9+
return json({ test: 'test' });
10+
};
11+
12+
export default function ActionJSONResponse() {
13+
const { test } = useActionData();
14+
return (
15+
<div>
16+
<h1>{test}</h1>
17+
</div>
18+
);
19+
}

packages/remix/test/integration/app/routes/loader-json-response/$id.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { useLoaderData } from '@remix-run/react';
44
type LoaderData = { id: string };
55

66
export const loader: LoaderFunction = async ({ params: { id } }) => {
7+
if (id === '-1') {
8+
throw new Error('Error');
9+
}
10+
711
return json({
812
id,
913
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
assertSentryTransaction,
3+
getEnvelopeRequest,
4+
runServer,
5+
getMultipleEnvelopeRequest,
6+
assertSentryEvent,
7+
} from './utils/helpers';
8+
9+
jest.spyOn(console, 'error').mockImplementation();
10+
11+
describe('Remix API Actions', () => {
12+
it('correctly instruments a parameterized Remix API action', async () => {
13+
const baseURL = await runServer();
14+
const url = `${baseURL}/action-json-response/123123`;
15+
const envelope = await getEnvelopeRequest(url, 'post');
16+
const transaction = envelope[2];
17+
18+
assertSentryTransaction(transaction, {
19+
spans: [
20+
{
21+
description: 'routes/action-json-response/$id',
22+
op: 'remix.server.action',
23+
},
24+
{
25+
description: 'routes/action-json-response/$id',
26+
op: 'remix.server.documentRequest',
27+
},
28+
],
29+
});
30+
});
31+
32+
it('reports an error thrown from the action', async () => {
33+
const baseURL = await runServer();
34+
const url = `${baseURL}/action-json-response/-1`;
35+
36+
const [transaction, event] = await getMultipleEnvelopeRequest(url, 2, 'post');
37+
38+
assertSentryTransaction(transaction[2], {
39+
contexts: {
40+
trace: {
41+
status: 'internal_error',
42+
tags: {
43+
'http.status_code': '500',
44+
},
45+
},
46+
},
47+
});
48+
49+
assertSentryEvent(event[2], {
50+
exception: {
51+
values: [
52+
{
53+
type: 'Error',
54+
value: 'Error',
55+
stacktrace: expect.any(Object),
56+
mechanism: {
57+
data: {
58+
function: 'action',
59+
},
60+
handled: true,
61+
type: 'instrument',
62+
},
63+
},
64+
],
65+
},
66+
});
67+
});
68+
});

packages/remix/test/integration/test/server/loader.test.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,68 @@
1-
import { assertSentryTransaction, getEnvelopeRequest, runServer } from './utils/helpers';
1+
import {
2+
assertSentryTransaction,
3+
getEnvelopeRequest,
4+
runServer,
5+
getMultipleEnvelopeRequest,
6+
assertSentryEvent,
7+
} from './utils/helpers';
8+
9+
jest.spyOn(console, 'error').mockImplementation();
210

311
describe('Remix API Loaders', () => {
4-
it('correctly instruments a Remix API loader', async () => {
12+
it('does not add a loader if there is not one defined.', async () => {
13+
const baseURL = await runServer();
14+
const url = `${baseURL}/`;
15+
const envelope = await getEnvelopeRequest(url);
16+
const transaction = envelope[2];
17+
18+
assertSentryTransaction(transaction, {
19+
spans: [
20+
{
21+
description: 'root',
22+
op: 'remix.server.documentRequest',
23+
},
24+
],
25+
});
26+
});
27+
28+
it('reports an error thrown from the loader', async () => {
29+
const baseURL = await runServer();
30+
const url = `${baseURL}/loader-json-response/-1`;
31+
32+
const [transaction, event] = await getMultipleEnvelopeRequest(url, 2);
33+
34+
assertSentryTransaction(transaction[2], {
35+
contexts: {
36+
trace: {
37+
status: 'internal_error',
38+
tags: {
39+
'http.status_code': '500',
40+
},
41+
},
42+
},
43+
});
44+
45+
assertSentryEvent(event[2], {
46+
exception: {
47+
values: [
48+
{
49+
type: 'Error',
50+
value: 'Error',
51+
stacktrace: expect.any(Object),
52+
mechanism: {
53+
data: {
54+
function: 'loader',
55+
},
56+
handled: true,
57+
type: 'instrument',
58+
},
59+
},
60+
],
61+
},
62+
});
63+
});
64+
65+
it('correctly instruments a parameterized Remix API loader', async () => {
566
const baseURL = await runServer();
667
const url = `${baseURL}/loader-json-response/123123`;
768
const envelope = await getEnvelopeRequest(url);

packages/remix/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"include": ["src/**/*"],
55

66
"compilerOptions": {
7-
"jsx": "react"
7+
"jsx": "react",
8+
"module": "es2020"
89
}
910
}

yarn.lock

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7146,6 +7146,14 @@ aws4@^1.8.0:
71467146
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
71477147
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
71487148

7149+
axios@^0.27.2:
7150+
version "0.27.2"
7151+
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
7152+
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
7153+
dependencies:
7154+
follow-redirects "^1.14.9"
7155+
form-data "^4.0.0"
7156+
71497157
babel-code-frame@^6.26.0:
71507158
version "6.26.0"
71517159
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -13706,6 +13714,11 @@ follow-redirects@^1.0.0:
1370613714
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
1370713715
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
1370813716

13717+
follow-redirects@^1.14.9:
13718+
version "1.15.1"
13719+
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
13720+
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
13721+
1370913722
for-each@^0.3.3:
1371013723
version "0.3.3"
1371113724
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"

0 commit comments

Comments
 (0)