Skip to content

Commit 28250c1

Browse files
committed
test: HTTP(S)Transport proxy tests implementation
1 parent 58c9111 commit 28250c1

File tree

7 files changed

+213
-108
lines changed

7 files changed

+213
-108
lines changed

packages/node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
],
7171
"globals": {
7272
"ts-jest": {
73-
"tsConfigFile": "./tsconfig.json"
73+
"tsConfig": "./tsconfig.json"
7474
}
7575
}
7676
},

packages/node/src/declarations.d.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,2 @@
11
declare module 'lsmod';
2-
3-
declare module 'https-proxy-agent' {
4-
/** JSDoc */
5-
// tslint:disable-next-line:no-unnecessary-class
6-
export default class HttpsProxyAgent {
7-
public constructor(proxy: string);
8-
}
9-
}
2+
declare module 'https-proxy-agent';

packages/node/src/transports/base.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ export abstract class BaseTransport implements Transport {
2121
protected api: API;
2222

2323
/** The Agent used for corresponding transport */
24-
protected client: http.Agent | https.Agent | undefined;
24+
public module?: HTTPRequest;
25+
26+
/** The Agent used for corresponding transport */
27+
public client?: http.Agent | https.Agent;
2528

2629
/** Create instance and set this.dsn */
2730
public constructor(public options: TransportOptions) {

packages/node/src/transports/http.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
1+
import { SentryError } from '@sentry/core';
12
import { SentryEvent, SentryResponse, TransportOptions } from '@sentry/types';
23
import * as http from 'http';
3-
import HttpsProxyAgent from 'https-proxy-agent';
4+
import * as HttpsProxyAgent from 'https-proxy-agent';
45
import { BaseTransport } from './base';
56

67
/** Node http module transport */
78
export class HTTPTransport extends BaseTransport {
89
/** Create a new instance and set this.agent */
910
public constructor(public options: TransportOptions) {
1011
super(options);
12+
this.module = http;
1113
const proxy = options.httpProxy || process.env.http_proxy;
1214
this.client = proxy
13-
? (new HttpsProxyAgent(proxy) as http.Agent)
15+
? // tslint:disable-next-line:no-unsafe-any
16+
(new HttpsProxyAgent(proxy) as http.Agent)
1417
: new http.Agent({ keepAlive: true, maxSockets: 100 });
1518
}
1619

1720
/**
1821
* @inheritDoc
1922
*/
2023
public async captureEvent(event: SentryEvent): Promise<SentryResponse> {
21-
return this.sendWithModule(http, event);
24+
if (!this.module) {
25+
throw new SentryError('No module available in HTTPTransport');
26+
}
27+
return this.sendWithModule(this.module, event);
2228
}
2329
}

packages/node/src/transports/https.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1+
import { SentryError } from '@sentry/core';
12
import { SentryEvent, SentryResponse, TransportOptions } from '@sentry/types';
23
import * as https from 'https';
3-
import HttpsProxyAgent from 'https-proxy-agent';
4+
import * as HttpsProxyAgent from 'https-proxy-agent';
45
import { BaseTransport } from './base';
56

67
/** Node https module transport */
78
export class HTTPSTransport extends BaseTransport {
89
/** Create a new instance and set this.agent */
910
public constructor(public options: TransportOptions) {
1011
super(options);
12+
this.module = https;
1113
const proxy = options.httpsProxy || options.httpProxy || process.env.https_proxy || process.env.http_proxy;
1214
this.client = proxy
13-
? (new HttpsProxyAgent(proxy) as https.Agent)
15+
? // tslint:disable-next-line:no-unsafe-any
16+
(new HttpsProxyAgent(proxy) as https.Agent)
1417
: new https.Agent({ keepAlive: true, maxSockets: 100 });
1518
}
1619

1720
/**
1821
* @inheritDoc
1922
*/
2023
public async captureEvent(event: SentryEvent): Promise<SentryResponse> {
21-
return this.sendWithModule(https, event);
24+
if (!this.module) {
25+
throw new SentryError('No module available in HTTPSTransport');
26+
}
27+
28+
return this.sendWithModule(this.module, event);
2229
}
2330
}
Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1+
import { SentryError } from '@sentry/core';
2+
import { TransportOptions } from '@sentry/types';
3+
import * as HttpsProxyAgent from 'https-proxy-agent';
4+
import { HTTPTransport } from '../../src/transports/http';
5+
6+
const mockSetEncoding = jest.fn();
7+
const dsn = 'http://[email protected]:8989/mysubpath/50622';
18
let mockReturnCode = 200;
29
let mockHeaders = {};
3-
let mockCheckHeaders: any;
4-
const mockSetEncoding = jest.fn();
5-
jest.mock('http', () => ({
6-
Agent: jest.fn(),
7-
request: (options: any, callback: any) => {
8-
expect(options.headers['X-Sentry-Auth']).toContain('sentry_version');
9-
expect(options.headers['X-Sentry-Auth']).toContain('sentry_timestamp');
10-
expect(options.headers['X-Sentry-Auth']).toContain('sentry_client');
11-
expect(options.headers['X-Sentry-Auth']).toContain('sentry_key');
12-
expect(options.port).toEqual('8989');
13-
expect(options.path).toEqual('/mysubpath/api/50622/store/');
14-
expect(options.hostname).toEqual('sentry.io');
15-
if (mockCheckHeaders) {
16-
expect(options.headers).toEqual(expect.objectContaining(mockCheckHeaders));
17-
}
18-
return {
10+
11+
function createTransport(options: TransportOptions): HTTPTransport {
12+
const transport = new HTTPTransport(options);
13+
transport.module = {
14+
request: jest.fn().mockImplementation((_options: any, callback: any) => ({
1915
end: () => {
2016
callback({
2117
headers: mockHeaders,
@@ -24,63 +20,105 @@ jest.mock('http', () => ({
2420
});
2521
},
2622
on: jest.fn(),
27-
};
28-
},
29-
}));
23+
})),
24+
};
25+
return transport;
26+
}
3027

31-
import { Dsn, SentryError } from '@sentry/core';
32-
import { getCurrentHub, init, NodeClient } from '../../src';
33-
34-
const dsn = 'http://[email protected]:8989/mysubpath/50622';
28+
function assertBasicOptions(options: any): void {
29+
expect(options.headers['X-Sentry-Auth']).toContain('sentry_version');
30+
expect(options.headers['X-Sentry-Auth']).toContain('sentry_timestamp');
31+
expect(options.headers['X-Sentry-Auth']).toContain('sentry_client');
32+
expect(options.headers['X-Sentry-Auth']).toContain('sentry_key');
33+
expect(options.port).toEqual('8989');
34+
expect(options.path).toEqual('/mysubpath/api/50622/store/');
35+
expect(options.hostname).toEqual('sentry.io');
36+
}
3537

3638
describe('HTTPTransport', () => {
3739
beforeEach(() => {
38-
mockHeaders = {};
3940
mockReturnCode = 200;
41+
mockHeaders = {};
42+
jest.clearAllMocks();
4043
});
4144

4245
test('send 200', async () => {
43-
init({
44-
dsn,
46+
const transport = createTransport({ dsn });
47+
await transport.captureEvent({
48+
message: 'test',
4549
});
46-
await getCurrentHub()
47-
.getClient()
48-
.captureMessage('test');
50+
51+
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
52+
assertBasicOptions(requestOptions);
4953
expect(mockSetEncoding).toHaveBeenCalled();
5054
});
5155

5256
test('send 400', async () => {
5357
mockReturnCode = 400;
54-
const client = new NodeClient({ dsn });
55-
client.install();
56-
return expect(client.captureMessage('test')).rejects.toEqual(new SentryError(`HTTP Error (${mockReturnCode})`));
58+
const transport = createTransport({ dsn });
59+
60+
try {
61+
await transport.captureEvent({
62+
message: 'test',
63+
});
64+
} catch (e) {
65+
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
66+
assertBasicOptions(requestOptions);
67+
expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode})`));
68+
}
5769
});
5870

5971
test('send x-sentry-error header', async () => {
6072
mockReturnCode = 429;
6173
mockHeaders = {
6274
'x-sentry-error': 'test-failed',
6375
};
64-
const client = new NodeClient({ dsn });
65-
client.install();
66-
return expect(client.captureMessage('test')).rejects.toEqual(
67-
new SentryError(`HTTP Error (${mockReturnCode}): test-failed`),
68-
);
76+
const transport = createTransport({ dsn });
77+
78+
try {
79+
await transport.captureEvent({
80+
message: 'test',
81+
});
82+
} catch (e) {
83+
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
84+
assertBasicOptions(requestOptions);
85+
expect(e).toEqual(new SentryError(`HTTP Error (${mockReturnCode}): test-failed`));
86+
}
6987
});
7088

7189
test('transport options', async () => {
7290
mockReturnCode = 200;
73-
const client = new NodeClient({
91+
const transport = createTransport({
7492
dsn,
75-
transportOptions: {
76-
dsn: new Dsn(dsn),
77-
headers: {
78-
a: 'b',
79-
},
93+
headers: {
94+
a: 'b',
8095
},
8196
});
82-
client.install();
83-
mockCheckHeaders = { a: 'b' };
84-
await client.captureMessage('test');
97+
await transport.captureEvent({
98+
message: 'test',
99+
});
100+
101+
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
102+
assertBasicOptions(requestOptions);
103+
expect(requestOptions.headers).toEqual(expect.objectContaining({ a: 'b' }));
104+
});
105+
106+
test('http proxy', async () => {
107+
mockReturnCode = 200;
108+
const transport = createTransport({
109+
dsn,
110+
httpProxy: 'http://example.com:8080',
111+
});
112+
await transport.captureEvent({
113+
message: 'test',
114+
});
115+
116+
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
117+
assertBasicOptions(requestOptions);
118+
expect(requestOptions.agent).toBeInstanceOf(HttpsProxyAgent);
119+
expect(requestOptions.agent.secureProxy).toEqual(false);
120+
expect(requestOptions.agent.proxy).toEqual(
121+
expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' }),
122+
);
85123
});
86124
});

0 commit comments

Comments
 (0)