Skip to content

Commit b1d6a60

Browse files
author
Luca Forstner
committed
Add first few test cases
1 parent 6019f60 commit b1d6a60

File tree

2 files changed

+236
-1
lines changed

2 files changed

+236
-1
lines changed

packages/node/src/transports/new.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as fs from 'fs';
1111
import * as http from 'http';
1212
import * as https from 'https';
1313

14-
import { HTTPModule, HTTPModuleClientRequest } from './base/http-module';
14+
import { HTTPModule } from './base/http-module';
1515

1616
interface HttpTransportOptions extends BaseTransportOptions {
1717
// Todo: doc
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import { createTransport } from '@sentry/core';
2+
import { EventEnvelope, EventItem } from '@sentry/types';
3+
import { createEnvelope, serializeEnvelope } from '@sentry/utils';
4+
import * as http from 'http';
5+
6+
import { makeNewHttpTransport } from '../../../src/transports/new';
7+
8+
jest.mock('@sentry/core', () => {
9+
const actualCore = jest.requireActual('@sentry/core');
10+
return {
11+
...actualCore,
12+
createTransport: jest.fn().mockImplementation(actualCore.createTransport),
13+
};
14+
});
15+
16+
const SUCCESS = 200;
17+
const RATE_LIMIT = 429;
18+
const INVALID = 400;
19+
const FAILED = 500;
20+
21+
interface TestServerOptions {
22+
statusCode: number;
23+
responseHeaders: Record<string, string | string[] | undefined>;
24+
}
25+
26+
let testServer: http.Server | undefined;
27+
28+
function setupTestServer(
29+
options: TestServerOptions,
30+
requestInspector?: (req: http.IncomingMessage, body: string) => void,
31+
) {
32+
testServer = http.createServer((req, res) => {
33+
let body = '';
34+
35+
req.on('data', data => {
36+
body += data;
37+
});
38+
39+
req.on('end', () => {
40+
requestInspector?.(req, body);
41+
});
42+
43+
res.writeHead(options.statusCode, options.responseHeaders);
44+
res.end();
45+
});
46+
47+
testServer.listen(12345);
48+
49+
return new Promise(resolve => {
50+
testServer?.on('listening', resolve);
51+
});
52+
}
53+
54+
const testServerUrl = 'http://localhost:12345';
55+
56+
const EVENT_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [
57+
[{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem,
58+
]);
59+
60+
const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE);
61+
62+
describe('makeNewHttpTransport()', () => {
63+
afterEach(() => {
64+
jest.clearAllMocks();
65+
66+
if (testServer) {
67+
testServer.close();
68+
}
69+
});
70+
71+
describe('.send()', () => {
72+
it('should correctly return successful server response', async () => {
73+
await setupTestServer({ statusCode: SUCCESS, responseHeaders: {} });
74+
75+
const transport = makeNewHttpTransport({ url: testServerUrl });
76+
const transportResponse = await transport.send(EVENT_ENVELOPE);
77+
78+
expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' }));
79+
});
80+
81+
it('should correctly send envelope to server', async () => {
82+
await setupTestServer({ statusCode: SUCCESS, responseHeaders: {} }, (req, body) => {
83+
expect(req.method).toBe('POST');
84+
expect(body).toBe(SERIALIZED_EVENT_ENVELOPE);
85+
});
86+
87+
const transport = makeNewHttpTransport({ url: testServerUrl });
88+
await transport.send(EVENT_ENVELOPE);
89+
});
90+
91+
it('should correctly send user-provided headers to server', async () => {
92+
await setupTestServer({ statusCode: SUCCESS, responseHeaders: {} }, req => {
93+
expect(req.headers).toEqual(
94+
expect.objectContaining({
95+
// node http module lower-cases incoming headers
96+
'x-some-custom-header-1': 'value1',
97+
'x-some-custom-header-2': 'value2',
98+
}),
99+
);
100+
});
101+
102+
const transport = makeNewHttpTransport({
103+
url: testServerUrl,
104+
headers: {
105+
'X-Some-Custom-Header-1': 'value1',
106+
'X-Some-Custom-Header-2': 'value2',
107+
},
108+
});
109+
110+
await transport.send(EVENT_ENVELOPE);
111+
});
112+
113+
it.each([
114+
[RATE_LIMIT, 'rate_limit'],
115+
[INVALID, 'invalid'],
116+
[FAILED, 'failed'],
117+
])('should correctly reject bad server response (status %i)', async (serverStatusCode, expectedStatus) => {
118+
await setupTestServer({ statusCode: serverStatusCode, responseHeaders: {} });
119+
120+
const transport = makeNewHttpTransport({ url: testServerUrl });
121+
await expect(transport.send(EVENT_ENVELOPE)).rejects.toEqual(expect.objectContaining({ status: expectedStatus }));
122+
});
123+
124+
it('should resolve when server responds with rate limit header and status code 200', async () => {
125+
await setupTestServer({
126+
statusCode: SUCCESS,
127+
responseHeaders: {
128+
'Retry-After': '2700',
129+
'X-Sentry-Rate-Limits': '60::organization, 2700::organization',
130+
},
131+
});
132+
133+
const transport = makeNewHttpTransport({ url: testServerUrl });
134+
const transportResponse = await transport.send(EVENT_ENVELOPE);
135+
136+
expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' }));
137+
});
138+
139+
it('should resolve when server responds with rate limit header and status code 200', async () => {
140+
await setupTestServer({
141+
statusCode: SUCCESS,
142+
responseHeaders: {
143+
'Retry-After': '2700',
144+
'X-Sentry-Rate-Limits': '60::organization, 2700::organization',
145+
},
146+
});
147+
148+
const transport = makeNewHttpTransport({ url: testServerUrl });
149+
const transportResponse = await transport.send(EVENT_ENVELOPE);
150+
151+
expect(transportResponse).toEqual(expect.objectContaining({ status: 'success' }));
152+
});
153+
});
154+
155+
it('should register TransportRequestExecutor that returns the correct object from server response (rate limit)', async () => {
156+
await setupTestServer({
157+
statusCode: RATE_LIMIT,
158+
responseHeaders: {
159+
'Retry-After': '2700',
160+
'X-Sentry-Rate-Limits': '60::organization, 2700::organization',
161+
},
162+
});
163+
164+
makeNewHttpTransport({ url: testServerUrl });
165+
const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1];
166+
167+
const executorResult = registeredRequestExecutor({
168+
body: serializeEnvelope(EVENT_ENVELOPE),
169+
category: 'error',
170+
});
171+
172+
await expect(executorResult).resolves.toEqual(
173+
expect.objectContaining({
174+
headers: {
175+
'retry-after': '2700',
176+
'x-sentry-rate-limits': '60::organization, 2700::organization',
177+
},
178+
statusCode: RATE_LIMIT,
179+
}),
180+
);
181+
});
182+
183+
it('should register TransportRequestExecutor that returns the correct object from server response (success)', async () => {
184+
await setupTestServer({
185+
statusCode: SUCCESS,
186+
responseHeaders: {},
187+
});
188+
189+
makeNewHttpTransport({ url: testServerUrl });
190+
const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1];
191+
192+
const executorResult = registeredRequestExecutor({
193+
body: serializeEnvelope(EVENT_ENVELOPE),
194+
category: 'error',
195+
});
196+
197+
await expect(executorResult).resolves.toEqual(
198+
expect.objectContaining({
199+
headers: {
200+
'retry-after': null,
201+
'x-sentry-rate-limits': null,
202+
},
203+
statusCode: SUCCESS,
204+
}),
205+
);
206+
});
207+
208+
it('should register TransportRequestExecutor that returns the correct object from server response (success but rate-limit)', async () => {
209+
await setupTestServer({
210+
statusCode: SUCCESS,
211+
responseHeaders: {
212+
'Retry-After': '2700',
213+
'X-Sentry-Rate-Limits': '60::organization, 2700::organization',
214+
},
215+
});
216+
217+
makeNewHttpTransport({ url: testServerUrl });
218+
const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1];
219+
220+
const executorResult = registeredRequestExecutor({
221+
body: serializeEnvelope(EVENT_ENVELOPE),
222+
category: 'error',
223+
});
224+
225+
await expect(executorResult).resolves.toEqual(
226+
expect.objectContaining({
227+
headers: {
228+
'retry-after': '2700',
229+
'x-sentry-rate-limits': '60::organization, 2700::organization',
230+
},
231+
statusCode: SUCCESS,
232+
}),
233+
);
234+
});
235+
});

0 commit comments

Comments
 (0)