forked from PinSpace-Org/GistPin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp-metrics.interceptor.spec.ts
More file actions
119 lines (106 loc) 路 4.05 KB
/
Copy pathhttp-metrics.interceptor.spec.ts
File metadata and controls
119 lines (106 loc) 路 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { ExecutionContext, CallHandler } from '@nestjs/common';
import { of, throwError } from 'rxjs';
import { HttpMetricsInterceptor } from './http-metrics.interceptor';
import { Counter, Histogram } from 'prom-client';
function makeContext(method: string, url: string, statusCode: number, routePath?: string): ExecutionContext {
return {
switchToHttp: () => ({
getRequest: () => ({ method, url, route: routePath ? { path: routePath } : undefined }),
getResponse: () => ({ statusCode }),
}),
} as unknown as ExecutionContext;
}
function makeHandler(value: unknown = null, error?: { status?: number; message?: string }): CallHandler {
return { handle: () => (error ? throwError(() => error) : of(value)) };
}
function makeMetrics() {
const stopTimer = jest.fn();
const counter: Partial<Counter<string>> = { inc: jest.fn() };
const histogram: Partial<Histogram<string>> = {
startTimer: jest.fn().mockReturnValue(stopTimer),
};
return { counter, histogram, stopTimer };
}
describe('HttpMetricsInterceptor', () => {
it('should increment counter and stop timer on successful request', (done) => {
const { counter, histogram, stopTimer } = makeMetrics();
const interceptor = new HttpMetricsInterceptor(
counter as Counter<string>,
histogram as Histogram<string>,
);
const ctx = makeContext('GET', '/gists', 200, '/gists');
interceptor.intercept(ctx, makeHandler()).subscribe({
complete: () => {
expect(histogram.startTimer).toHaveBeenCalledWith({ method: 'GET', route: '/gists' });
expect(stopTimer).toHaveBeenCalledWith({ status_code: '200' });
expect(counter.inc).toHaveBeenCalledWith({ method: 'GET', route: '/gists', status_code: '200' });
done();
},
});
});
it('should record 4xx status on client error', (done) => {
const { counter, histogram, stopTimer } = makeMetrics();
const interceptor = new HttpMetricsInterceptor(
counter as Counter<string>,
histogram as Histogram<string>,
);
const ctx = makeContext('POST', '/gists', 400, '/gists');
interceptor.intercept(ctx, makeHandler()).subscribe({
complete: () => {
expect(counter.inc).toHaveBeenCalledWith(
expect.objectContaining({ status_code: '400' }),
);
done();
},
});
});
it('should use status from thrown error and still stop timer', (done) => {
const { counter, histogram, stopTimer } = makeMetrics();
const interceptor = new HttpMetricsInterceptor(
counter as Counter<string>,
histogram as Histogram<string>,
);
const ctx = makeContext('DELETE', '/gists/1', 500, '/gists/:id');
interceptor.intercept(ctx, makeHandler(null, { status: 422 })).subscribe({
error: () => {
expect(stopTimer).toHaveBeenCalledWith({ status_code: '422' });
expect(counter.inc).toHaveBeenCalledWith(
expect.objectContaining({ status_code: '422' }),
);
done();
},
});
});
it('should default to 500 when thrown error has no status', (done) => {
const { counter, histogram } = makeMetrics();
const interceptor = new HttpMetricsInterceptor(
counter as Counter<string>,
histogram as Histogram<string>,
);
const ctx = makeContext('GET', '/gists', 500);
interceptor.intercept(ctx, makeHandler(null, {})).subscribe({
error: () => {
expect(counter.inc).toHaveBeenCalledWith(
expect.objectContaining({ status_code: '500' }),
);
done();
},
});
});
it('should fall back to req.url when route path is not resolved yet', (done) => {
const { counter, histogram } = makeMetrics();
const interceptor = new HttpMetricsInterceptor(
counter as Counter<string>,
histogram as Histogram<string>,
);
const ctx = makeContext('GET', '/unknown-path', 404);
interceptor.intercept(ctx, makeHandler()).subscribe({
complete: () => {
expect(counter.inc).toHaveBeenCalledWith(
expect.objectContaining({ route: '/unknown-path' }),
);
done();
},
});
});
});