@contrast/route-coverage 1.23.1 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -15,9 +15,9 @@
15
15
 
16
16
  import { Installable, Messages, RouteInfo } from '@contrast/common';
17
17
  import { Config } from '@contrast/config';
18
+ import { DepHooks } from '@contrast/dep-hooks';
18
19
  import { Logger } from '@contrast/logger';
19
20
  import { Patcher } from '@contrast/patcher';
20
- import RequireHook from '@contrast/require-hook';
21
21
  import { Scopes } from '@contrast/scopes';
22
22
 
23
23
  export { RouteInfo };
@@ -32,7 +32,7 @@ export interface RouteCoverage extends Installable {
32
32
 
33
33
  export interface Core {
34
34
  readonly config: Config;
35
- readonly depHooks: RequireHook;
35
+ readonly depHooks: DepHooks;
36
36
  readonly logger: Logger;
37
37
  readonly messages: Messages;
38
38
  readonly patcher: Patcher;
@@ -14,8 +14,8 @@
14
14
  */
15
15
  'use strict';
16
16
 
17
- const { StringPrototypeToLowerCase, isString } = require('@contrast/common');
18
- const { getFastifyMethods } = require('./../utils/methods');
17
+ const { getFastifyMethods } = require('../utils/methods');
18
+ const { StringPrototypeToLowerCase } = require('@contrast/common');
19
19
  const { patchType } = require('./../utils/route-info');
20
20
 
21
21
  // Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Fastify
@@ -28,19 +28,14 @@ module.exports = function init(core) {
28
28
  We need a way to keep track of which routes were fully declared using .route
29
29
  So, we instrument the route method below and add identifying info to this array
30
30
  */
31
- const fullyDeclaredRoutes = [];
32
31
  const { patcher, depHooks, routeCoverage } = core;
33
32
 
34
- const isFullyDeclared = (method, url) => isString(method) && fullyDeclaredRoutes.includes(method + url);
35
- function fullyDeclare(method, url) {
36
- if (isString(method)) {
37
- fullyDeclaredRoutes.push(method + url);
38
- if (method === 'GET') fullyDeclaredRoutes.push(`HEAD${url}`);
39
- }
33
+ function getPrefix(route) {
34
+ const kRoutePrefix = Object.getOwnPropertySymbols(route).find((s) => s.description === 'fastify.routePrefix');
35
+ return route?.[kRoutePrefix];
40
36
  }
41
37
 
42
- function createRouteInfo(method, url, routePath) {
43
- const fullyDeclared = isFullyDeclared(method, routePath);
38
+ function createRouteInfo(method, url, fullyDeclared) {
44
39
  method = StringPrototypeToLowerCase.call(method);
45
40
 
46
41
  const signature = fullyDeclared
@@ -57,67 +52,122 @@ module.exports = function init(core) {
57
52
  return routeInfo;
58
53
  }
59
54
 
55
+ function patchHandler(route, handle, routeInfo) {
56
+ const pre = ({ args }) => {
57
+ const [req] = args;
58
+ const method = StringPrototypeToLowerCase.call(req.raw?.method);
59
+ const [url] = req.url.split(/\?/);
60
+ routeCoverage.observe({ ...routeInfo, url, method });
61
+ };
62
+
63
+ const name = `fastify.route.${handle}`;
64
+ if (route[handle]) {
65
+ patcher.patch(route, handle, {
66
+ name,
67
+ patchType,
68
+ pre
69
+ });
70
+ } else {
71
+ const idx = handle === 'options' ? 2 : handle === 'handler' ? 3 : null;
72
+ if (idx) route[idx] = patcher.patch(route[idx], {
73
+ name,
74
+ patchType,
75
+ pre
76
+ });
77
+ }
78
+ }
79
+
80
+ function discoverAndPatch(method, path, handler, handle, methods, fullyDeclared) {
81
+ if (Array.isArray(method)) {
82
+ // If all valid methods are included in `method` then .all shorthand was most likely used
83
+ if (methods.every(m => method.includes(m))) {
84
+ const routeInfo = createRouteInfo('all', path, fullyDeclared);
85
+ routeCoverage.discover(routeInfo);
86
+ patchHandler(handler, handle, routeInfo);
87
+ } else {
88
+ method.forEach((verb) => {
89
+ const routeInfo = createRouteInfo(verb, path, fullyDeclared);
90
+ routeCoverage.discover(routeInfo);
91
+ patchHandler(handler, handle, routeInfo);
92
+ });
93
+ }
94
+ } else {
95
+ const routeInfo = createRouteInfo(method, path, fullyDeclared);
96
+ routeCoverage.discover(routeInfo);
97
+ patchHandler(handler, handle, routeInfo);
98
+ }
99
+ }
100
+
60
101
  return core.routeCoverage.fastify = {
61
102
  install() {
62
- // The routePath property used below was introduced in 3.2.0
63
- return depHooks.resolve({ name: 'fastify', version: '>=3.2.0' }, (fastify) => patcher.patch(fastify, {
64
- name: 'fastify',
65
- patchType,
66
- post({ result: server }) {
67
- patcher.patch(server, 'route', {
68
- name: 'server.route',
103
+ /**
104
+ * There are some subtle differences between fastify minor versions the instrumentation must account for
105
+ * >=3 <4.1.0 the route options are passed in as regular args i.e. route(method, url, options, handler)
106
+ * and prepareRoute(method, url, options, handler)
107
+ * >= 4.1.0 the route options are passed in as an object i.e. route({ method, url, options, handler })
108
+ * and prepareRoute({ options: { method, url, options, handler }})
109
+ */
110
+ [
111
+ { version: '>=3 <4.1.0', routeObj: false },
112
+ { version: '>=4.1.0 <4.4.0', routeObj: true },
113
+ { version: '>=4.4.0', routeObj: true }
114
+ ].forEach(({ version, routeObj }) => {
115
+ // See ../utils/methods
116
+ // Using .all shorthand uses all valid methods but those change after version 4.4.0
117
+ const FASTIFY_METHODS = getFastifyMethods(version);
118
+ depHooks.resolve({ name: 'fastify', version, file: 'lib/route.js' }, (Route) => {
119
+ patcher.patch(Route, 'buildRouting', {
120
+ name: 'fastify.Route.buildRouting',
69
121
  patchType,
70
- pre({ args }) {
71
- const [{ method, url }] = args;
72
- if (!isString(url)) return;
73
-
74
- if (Array.isArray(method)) {
75
- method.forEach((verb) => {
76
- fullyDeclare(verb, url);
77
- });
78
- } else {
79
- fullyDeclare(method, url);
80
- }
81
- }
82
- });
83
- server.addHook('onRoute', (routeOptions) => {
84
- if (!routeOptions) return;
85
-
86
- const { method, url, routePath } = routeOptions;
87
- if (!method || !url || !routePath || !isString(routePath)) return;
88
-
89
- let routeInfo;
90
- const FASTIFY_METHODS = getFastifyMethods(server.version);
91
- if (Array.isArray(method)) {
92
- // If a route was defined using .all this method property will be an
93
- // array of all methods supported by Fastify
94
- if (FASTIFY_METHODS.every(m => method.includes(m))) {
95
- routeInfo = createRouteInfo('all', url, routePath);
96
- routeCoverage.discover(routeInfo);
97
- } else {
98
- method.forEach((verb) => {
99
- routeInfo = createRouteInfo(verb, url, routePath);
100
- routeCoverage.discover(routeInfo);
101
- });
102
- }
103
- } else {
104
- routeInfo = createRouteInfo(method, url, routePath);
105
- routeCoverage.discover(routeInfo);
106
- }
122
+ post(data) {
123
+
124
+ // Handles routes defined through fastify.<method> shorthand
125
+ patcher.patch(data.result, 'prepareRoute', {
126
+ name: 'prepareRoute',
127
+ patchType,
128
+ pre(data) {
129
+ if (!data.args.length) return;
130
+
131
+ let method, url, options, handler;
132
+ if (routeObj) {
133
+ [{ method, url, options, handler }] = data.args;
134
+ } else {
135
+ [method, url, options, handler] = data.args;
136
+ }
137
+
138
+ if (!method || !url || !(options || handler)) return;
107
139
 
108
- patcher.patch(routeOptions, 'handler', {
109
- name: 'fastify.routeOptions.handler',
110
- patchType,
111
- pre({ args }) {
112
- const [req] = args;
113
- const method = StringPrototypeToLowerCase.call(req.raw?.method);
114
- const [url] = req.url.split(/\?/);
115
- routeCoverage.observe({ ...routeInfo, url, method });
116
- },
117
- });
140
+ const prefix = getPrefix(data.obj);
141
+ const path = prefix ? prefix + url : url;
142
+
143
+ // See lib/route.js: prepareRoute
144
+ let handle = 'handler';
145
+ if (!handler && typeof options === 'function') handle = 'options';
146
+
147
+ discoverAndPatch(method, path, routeObj ? data.args[0] : data.args, handle, FASTIFY_METHODS, false);
148
+ }
149
+ });
150
+
151
+ // Handles routes defined fully fastify.route({})
152
+ patcher.patch(data.result, 'route', {
153
+ name: 'route',
154
+ patchType,
155
+ pre(data) {
156
+
157
+ if (!data.args.length) return;
158
+ const routeArgs = routeObj ? data.args[0]?.options : data.args[0];
159
+ const { url, method, handler } = routeArgs;
160
+ if (!method || !url || !handler) return;
161
+
162
+ const prefix = getPrefix(data.obj);
163
+ const path = prefix ? prefix + url : url;
164
+ discoverAndPatch(method, path, routeArgs, 'handler', FASTIFY_METHODS, true);
165
+ }
166
+ });
167
+ }
118
168
  });
119
- }
120
- }));
169
+ });
170
+ });
121
171
  }
122
172
  };
123
173
  };
@@ -7,191 +7,212 @@ const patcher = require('@contrast/patcher');
7
7
  const mocks = require('@contrast/test/mocks');
8
8
  const { getFastifyMethods } = require('../utils/methods');
9
9
 
10
- describe('route-coverage fastify', function () {
11
- let core, serverMock, framework;
12
-
13
- beforeEach(function () {
14
- core = mocks.core();
15
- core.logger = mocks.logger();
16
- core.routeCoverage = mocks.routeCoverage();
17
- core.scopes = scopes(core);
18
- core.depHooks = mocks.depHooks();
19
- core.patcher = patcher(core);
20
-
21
- serverMock = {
22
- addHook: sinon.stub(),
23
- route: sinon.stub(),
24
- version: '4.4.0'
25
- };
26
-
27
- let fastify;
28
- framework = 'fastify';
29
- core.depHooks.resolve.callsFake((desc, cb) => {
30
- fastify = cb(() => serverMock);
10
+ [
11
+ { version: '>=3 <4.1.0', routeObj: false },
12
+ { version: '>=4.1.0 <4.4.0', routeObj: true },
13
+ { version: '>=4.4.0', routeObj: true }
14
+ ].forEach(({ version, routeObj }) => {
15
+ describe(`route-coverage fastify (${version})`, function () {
16
+ const FASTIFY_METHODS = getFastifyMethods(version);
17
+ let core, RouteMock, framework, routing, fn;
18
+
19
+ function routeOptions(options, fullyDeclared, routeObj) {
20
+ if (fullyDeclared && routeObj) {
21
+ return [{ options }];
22
+ } else if ((fullyDeclared && !routeObj) || (!fullyDeclared && routeObj)) {
23
+ return [options];
24
+ } else if (!fullyDeclared && !routeObj) {
25
+ return Object.values(options);
26
+ }
27
+ }
28
+
29
+ beforeEach(function () {
30
+ core = mocks.core();
31
+ core.logger = mocks.logger();
32
+ core.routeCoverage = mocks.routeCoverage();
33
+ core.scopes = scopes(core);
34
+ core.depHooks = mocks.depHooks();
35
+ core.patcher = patcher(core);
36
+ sinon.spy(core.patcher, 'patch');
37
+
38
+ RouteMock = {
39
+ buildRouting: () => ({
40
+ prepareRoute: sinon.stub(),
41
+ route: sinon.stub()
42
+ })
43
+ };
44
+
45
+ framework = 'fastify';
46
+ core.depHooks.resolve
47
+ .withArgs({ name: 'fastify', version, file: 'lib/route.js' })
48
+ .yields(RouteMock);
49
+
50
+ require('./fastify')(core).install();
51
+
52
+ routing = RouteMock.buildRouting();
53
+ fn = sinon.stub();
31
54
  });
32
55
 
33
- require('./fastify')(core).install();
34
-
35
- fastify();
36
- });
37
-
38
- it('skips instrumenting if route options does not exist', function () {
39
- serverMock.addHook.withArgs('onRoute').yield();
40
- expect(core.routeCoverage.discover).not.to.have.been.called;
41
- });
42
-
43
- it('skips instrumenting if there is no method provided', function () {
44
- serverMock.addHook.withArgs('onRoute').yield({});
45
- expect(core.routeCoverage.discover).not.to.have.been.called;
46
- });
47
-
48
- it('skips instrumenting if there is no url provided', function () {
49
- serverMock.addHook.withArgs('onRoute').yield({ method: 'GET' });
50
- expect(core.routeCoverage.discover).not.to.have.been.called;
51
- });
52
-
53
- it('discovers a route with a single method', function () {
54
- serverMock.addHook
55
- .withArgs('onRoute')
56
- .yield({ method: 'GET', url: '/test/route', routePath: '/test/route' });
57
-
58
- expect(core.routeCoverage.discover).to.have.been.calledWith({
59
- signature: "fastify.get('/test/route', [Function])",
60
- url: '/test/route',
61
- normalizedUrl: '/test/route',
62
- method: 'get',
63
- framework
56
+ [
57
+ { method: 'prepareRoute', fullyDefined: false },
58
+ { method: 'route', fullyDefined: true },
59
+ ].forEach(({ method, fullyDefined }) => {
60
+ describe(`.${method} base cases`, function() {
61
+ it('skips instrumenting if nothing is provided', function () {
62
+ routing[method]();
63
+ expect(core.routeCoverage.discover).not.to.have.been.called;
64
+ });
65
+
66
+ it('skips instrumenting if there is no method provided', function () {
67
+ routing[method](...routeOptions({ url: '/test/route', handler: fn }, fullyDefined, routeObj));
68
+ expect(core.routeCoverage.discover).not.to.have.been.called;
69
+ });
70
+
71
+ it('skips instrumenting if there is no url provided', function () {
72
+ routing[method](...routeOptions({ method: 'GET', handler: fn }, fullyDefined, routeObj));
73
+ expect(core.routeCoverage.discover).not.to.have.been.called;
74
+ });
75
+
76
+ it('skips instrumenting if there is no handler is provided', function () {
77
+ routing[method](...routeOptions({ method: 'GET', url: '/test/route' }, fullyDefined, routeObj));
78
+ expect(core.routeCoverage.discover).not.to.have.been.called;
79
+ });
80
+ });
64
81
  });
65
- expect(core.routeCoverage.discover).to.have.been.called;
66
- expect(core.routeCoverage.discover).to.have.callCount(1);
67
- });
68
82
 
69
- it('discovers a fully declared (.route) route with a single method', function () {
70
- serverMock.route({ method: 'GET', url: '/test/route' });
71
- serverMock.addHook
72
- .withArgs('onRoute')
73
- .yield({ method: 'GET', url: '/test/route', routePath: '/test/route' });
74
-
75
- expect(core.routeCoverage.discover).to.have.been.calledWith({
76
- signature: "fastify.route({ method: 'get', url: '/test/route', handler: [Function] })",
77
- url: '/test/route',
78
- normalizedUrl: '/test/route',
79
- method: 'get',
80
- framework
83
+ it('discovers a route with a single method', function () {
84
+ routing.prepareRoute(...routeOptions({ method: 'GET', url: '/test/route', handler: fn }, false, routeObj));
85
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
86
+ signature: "fastify.get('/test/route', [Function])",
87
+ url: '/test/route',
88
+ normalizedUrl: '/test/route',
89
+ method: 'get',
90
+ framework
91
+ });
92
+ expect(core.routeCoverage.discover).to.have.been.called;
93
+ expect(core.routeCoverage.discover).to.have.callCount(1);
81
94
  });
82
- expect(core.routeCoverage.discover).to.have.been.called;
83
- expect(core.routeCoverage.discover).to.have.callCount(1);
84
- });
85
95
 
86
- it('discovers a route with multiple methods', function () {
87
- serverMock.addHook.withArgs('onRoute').yield({
88
- method: ['GET', 'POST'],
89
- url: '/test/route',
90
- routePath: '/test/route'
96
+ it('discovers a route with a prefixed path', function () {
97
+ routing[Symbol('fastify.routePrefix')] = '/prefix';
98
+ routing.prepareRoute(...routeOptions({ method: 'GET', url: '/test/route', handler: fn }, false, routeObj));
99
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
100
+ signature: "fastify.get('/prefix/test/route', [Function])",
101
+ url: '/prefix/test/route',
102
+ normalizedUrl: '/prefix/test/route',
103
+ method: 'get',
104
+ framework
105
+ });
106
+ expect(core.routeCoverage.discover).to.have.been.called;
107
+ expect(core.routeCoverage.discover).to.have.callCount(1);
91
108
  });
92
109
 
93
- expect(core.routeCoverage.discover).to.have.been.calledWith({
94
- signature: "fastify.get('/test/route', [Function])",
95
- url: '/test/route',
96
- normalizedUrl: '/test/route',
97
- method: 'get',
98
- framework
110
+ it('discovers a fully declared (.route) with a prefixed path', function () {
111
+ routing[Symbol('fastify.routePrefix')] = '/prefix';
112
+ routing.route(...routeOptions({ method: 'GET', url: '/test/route', handler: fn }, true, routeObj));
113
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
114
+ signature: "fastify.route({ method: 'get', url: '/prefix/test/route', handler: [Function] })",
115
+ url: '/prefix/test/route',
116
+ normalizedUrl: '/prefix/test/route',
117
+ method: 'get',
118
+ framework
119
+ });
120
+ expect(core.routeCoverage.discover).to.have.been.called;
121
+ expect(core.routeCoverage.discover).to.have.callCount(1);
99
122
  });
100
123
 
101
- expect(core.routeCoverage.discover).to.have.been.calledWith({
102
- signature: "fastify.post('/test/route', [Function])",
103
- url: '/test/route',
104
- normalizedUrl: '/test/route',
105
- method: 'post',
106
- framework
124
+ it('discovers a fully declared (.route) route with a single method', function () {
125
+ routing.route(...routeOptions({ method: 'GET', url: '/test/route', handler: fn }, true, routeObj));
126
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
127
+ signature: "fastify.route({ method: 'get', url: '/test/route', handler: [Function] })",
128
+ url: '/test/route',
129
+ normalizedUrl: '/test/route',
130
+ method: 'get',
131
+ framework
132
+ });
133
+ expect(core.routeCoverage.discover).to.have.been.called;
134
+ expect(core.routeCoverage.discover).to.have.callCount(1);
107
135
  });
108
136
 
109
- expect(core.routeCoverage.discover).to.have.callCount(2);
110
- });
111
-
112
- it('discovers a route with all methods', function () {
113
- const FASTIFY_METHODS = getFastifyMethods('4.4.0');
114
- serverMock.addHook.withArgs('onRoute').yield({
115
- method: FASTIFY_METHODS,
116
- url: '/test/route',
117
- routePath: '/test/route'
137
+ it('discovers a route with multiple methods', function () {
138
+ routing.prepareRoute(...routeOptions({ method: ['GET', 'POST'], url: '/test/route', handler: fn }, false, routeObj));
139
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
140
+ signature: "fastify.get('/test/route', [Function])",
141
+ url: '/test/route',
142
+ normalizedUrl: '/test/route',
143
+ method: 'get',
144
+ framework
145
+ });
146
+
147
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
148
+ signature: "fastify.post('/test/route', [Function])",
149
+ url: '/test/route',
150
+ normalizedUrl: '/test/route',
151
+ method: 'post',
152
+ framework
153
+ });
154
+
155
+ expect(core.routeCoverage.discover).to.have.callCount(2);
118
156
  });
119
157
 
120
- expect(core.routeCoverage.discover).to.have.been.calledWith({
121
- signature: "fastify.all('/test/route', [Function])",
122
- url: '/test/route',
123
- normalizedUrl: '/test/route',
124
- method: 'all',
125
- framework
158
+ it('discovers a route with all methods', function () {
159
+ routing.prepareRoute(...routeOptions({ method: FASTIFY_METHODS, url: '/test/route', handler: fn }, false, routeObj));
160
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
161
+ signature: "fastify.all('/test/route', [Function])",
162
+ url: '/test/route',
163
+ normalizedUrl: '/test/route',
164
+ method: 'all',
165
+ framework
166
+ });
126
167
  });
127
- });
128
-
129
- it('discovers a fully declared route (.route) with multiple methods', function () {
130
- serverMock.route({ method: ['GET', 'POST'], url: '/test/route' });
131
168
 
132
- serverMock.addHook.withArgs('onRoute').yield({
133
- method: ['GET', 'POST'],
134
- url: '/test/route',
135
- routePath: '/test/route'
169
+ it('discovers a fully declared route (.route) with multiple methods', function () {
170
+ routing.route(...routeOptions({ method: ['GET', 'POST'], url: '/test/route', handler: fn }, true, routeObj));
171
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
172
+ signature: "fastify.route({ method: 'get', url: '/test/route', handler: [Function] })",
173
+ url: '/test/route',
174
+ normalizedUrl: '/test/route',
175
+ method: 'get',
176
+ framework
177
+ });
178
+
179
+ expect(core.routeCoverage.discover).to.have.been.calledWith({
180
+ signature: "fastify.route({ method: 'post', url: '/test/route', handler: [Function] })",
181
+ url: '/test/route',
182
+ normalizedUrl: '/test/route',
183
+ method: 'post',
184
+ framework
185
+ });
186
+
187
+ expect(core.routeCoverage.discover).to.have.callCount(2);
136
188
  });
137
189
 
138
- expect(core.routeCoverage.discover).to.have.been.calledWith({
139
- signature: "fastify.route({ method: 'get', url: '/test/route', handler: [Function] })",
140
- url: '/test/route',
141
- normalizedUrl: '/test/route',
142
- method: 'get',
143
- framework
190
+ it('observes a route when the route handler is called', function () {
191
+ routing.prepareRoute(...routeOptions({ method: 'GET', url: '/test/route', handler: fn }, false, routeObj));
192
+ const patchedFn = core.patcher.patch.getCall(3).returnValue;
193
+ const handler = routeObj ? patchedFn.handler : patchedFn;
194
+ handler({ raw: { method: 'GET' }, url: '/test/route' });
195
+ expect(core.routeCoverage.observe).to.have.been.calledWith({
196
+ signature: "fastify.get('/test/route', [Function])",
197
+ url: '/test/route',
198
+ method: 'get',
199
+ normalizedUrl: '/test/route',
200
+ framework: 'fastify'
201
+ });
144
202
  });
145
203
 
146
- expect(core.routeCoverage.discover).to.have.been.calledWith({
147
- signature: "fastify.route({ method: 'post', url: '/test/route', handler: [Function] })",
148
- url: '/test/route',
149
- normalizedUrl: '/test/route',
150
- method: 'post',
151
- framework
204
+ it('removes query string from url before reporting observation', function () {
205
+ routing.prepareRoute(...routeOptions({ method: 'GET', url: '/test/route', handler: fn }, false, routeObj));
206
+ const patchedFn = core.patcher.patch.getCall(3).returnValue;
207
+ const handler = routeObj ? patchedFn.handler : patchedFn;
208
+ handler({ raw: { method: 'GET' }, url: '/test/route' });
209
+ expect(core.routeCoverage.observe).to.have.been.calledWith({
210
+ signature: "fastify.get('/test/route', [Function])",
211
+ url: '/test/route',
212
+ method: 'get',
213
+ normalizedUrl: '/test/route',
214
+ framework: 'fastify'
215
+ });
152
216
  });
153
-
154
- expect(core.routeCoverage.discover).to.have.callCount(2);
155
217
  });
156
-
157
- it('observes a route when the route handler is called', function () {
158
- const routeOptions = {
159
- method: 'GET',
160
- url: '/test/route',
161
- routePath: '/test/route',
162
- handler: sinon.stub(),
163
- };
164
-
165
- serverMock.addHook.withArgs('onRoute').yield(routeOptions);
166
- routeOptions.handler({ raw: { method: 'GET' }, url: '/test/route' });
167
-
168
- expect(core.routeCoverage.observe).to.have.been.calledWith({
169
- signature: "fastify.get('/test/route', [Function])",
170
- url: '/test/route',
171
- method: 'get',
172
- normalizedUrl: '/test/route',
173
- framework: 'fastify'
174
- });
175
- });
176
-
177
- it('removes query string from url before reporting observation', function () {
178
- const routeOptions = {
179
- method: 'GET',
180
- url: '/test/route',
181
- routePath: '/test/route',
182
- handler: sinon.stub(),
183
- };
184
-
185
- serverMock.addHook.withArgs('onRoute').yield(routeOptions);
186
- routeOptions.handler({ raw: { method: 'GET' }, url: '/test/route?input=foo' });
187
-
188
- expect(core.routeCoverage.observe).to.have.been.calledWith({
189
- signature: "fastify.get('/test/route', [Function])",
190
- url: '/test/route',
191
- method: 'get',
192
- normalizedUrl: '/test/route',
193
- framework: 'fastify'
194
- });
195
- });
196
-
197
218
  });
@@ -18,7 +18,7 @@
18
18
  const semver = require('semver');
19
19
  const { METHODS } = require('http');
20
20
 
21
- function getFastifyMethods(version) {
21
+ function getFastifyMethods(range) {
22
22
  return [
23
23
  'DELETE',
24
24
  'GET',
@@ -27,7 +27,7 @@ function getFastifyMethods(version) {
27
27
  'POST',
28
28
  'PUT',
29
29
  'OPTIONS',
30
- ...semver.gte(version, '4.4.0') ?
30
+ ...semver.subset(range, '>=4.4.0') ?
31
31
  [
32
32
  'PROPFIND',
33
33
  'PROPPATCH',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/route-coverage",
3
- "version": "1.23.1",
3
+ "version": "1.25.0",
4
4
  "description": "Handles route discovery and observation",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -17,12 +17,12 @@
17
17
  "test": "../scripts/test.sh"
18
18
  },
19
19
  "dependencies": {
20
- "@contrast/common": "1.24.0",
21
- "@contrast/config": "1.31.0",
22
- "@contrast/dep-hooks": "1.3.4",
20
+ "@contrast/common": "1.25.0",
21
+ "@contrast/config": "1.33.0",
22
+ "@contrast/dep-hooks": "1.5.0",
23
23
  "@contrast/fn-inspect": "^4.3.0",
24
- "@contrast/logger": "1.8.5",
25
- "@contrast/patcher": "1.7.5",
26
- "@contrast/scopes": "1.4.2"
24
+ "@contrast/logger": "1.10.0",
25
+ "@contrast/patcher": "1.9.0",
26
+ "@contrast/scopes": "1.6.0"
27
27
  }
28
28
  }