fastify 4.0.0 → 4.0.3

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.
@@ -1,195 +1,191 @@
1
1
  // This file is autogenerated by build/build-error-serializer.js, do not edit
2
2
  /* istanbul ignore file */
3
- module.exports = $main
4
- 'use strict'
5
-
6
-
7
- function $pad2Zeros (num) {
8
- const s = '00' + num
9
- return s[s.length - 2] + s[s.length - 1]
10
- }
11
3
 
12
- function $asAny (i) {
13
- return JSON.stringify(i)
14
- }
4
+ 'use strict'
15
5
 
16
- function $asNull () {
17
- return 'null'
18
- }
6
+
19
7
 
20
- function $asInteger (i) {
21
- if (typeof i === 'bigint') {
22
- return i.toString()
23
- } else if (Number.isInteger(i)) {
24
- return $asNumber(i)
25
- } else {
26
- /* eslint no-undef: "off" */
27
- return $asNumber(parseInteger(i))
8
+ class Serializer {
9
+ constructor (options = {}) {
10
+ switch (options.rounding) {
11
+ case 'floor':
12
+ this.parseInteger = Math.floor
13
+ break
14
+ case 'ceil':
15
+ this.parseInteger = Math.ceil
16
+ break
17
+ case 'round':
18
+ this.parseInteger = Math.round
19
+ break
20
+ default:
21
+ this.parseInteger = Math.trunc
22
+ break
23
+ }
28
24
  }
29
- }
30
25
 
31
- function $asIntegerNullable (i) {
32
- return i === null ? null : $asInteger(i)
33
- }
26
+ asAny (i) {
27
+ return JSON.stringify(i)
28
+ }
34
29
 
35
- function $asNumber (i) {
36
- const num = Number(i)
37
- if (isNaN(num)) {
30
+ asNull () {
38
31
  return 'null'
39
- } else {
40
- return '' + num
41
32
  }
42
- }
43
33
 
44
- function $asNumberNullable (i) {
45
- return i === null ? null : $asNumber(i)
46
- }
34
+ asInteger (i) {
35
+ if (typeof i === 'bigint') {
36
+ return i.toString()
37
+ } else if (Number.isInteger(i)) {
38
+ return '' + i
39
+ } else {
40
+ /* eslint no-undef: "off" */
41
+ const integer = this.parseInteger(i)
42
+ if (Number.isNaN(integer)) {
43
+ throw new Error(`The value "${i}" cannot be converted to an integer.`)
44
+ } else {
45
+ return '' + integer
46
+ }
47
+ }
48
+ }
47
49
 
48
- function $asBoolean (bool) {
49
- return bool && 'true' || 'false' // eslint-disable-line
50
- }
50
+ asIntegerNullable (i) {
51
+ return i === null ? 'null' : this.asInteger(i)
52
+ }
51
53
 
52
- function $asBooleanNullable (bool) {
53
- return bool === null ? null : $asBoolean(bool)
54
- }
54
+ asNumber (i) {
55
+ const num = Number(i)
56
+ if (Number.isNaN(num)) {
57
+ throw new Error(`The value "${i}" cannot be converted to a number.`)
58
+ } else {
59
+ return '' + num
60
+ }
61
+ }
55
62
 
56
- function $asDatetime (date, skipQuotes) {
57
- const quotes = skipQuotes === true ? '' : '"'
58
- if (date instanceof Date) {
59
- return quotes + date.toISOString() + quotes
60
- } else if (date && typeof date.toISOString === 'function') {
61
- return quotes + date.toISOString() + quotes
62
- } else {
63
- return $asString(date, skipQuotes)
63
+ asNumberNullable (i) {
64
+ return i === null ? 'null' : this.asNumber(i)
64
65
  }
65
- }
66
66
 
67
- function $asDate (date, skipQuotes) {
68
- const quotes = skipQuotes === true ? '' : '"'
69
- if (date instanceof Date) {
70
- return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000 )).toISOString().slice(0, 10) + quotes
71
- } else if (date && typeof date.format === 'function') {
72
- return quotes + date.format('YYYY-MM-DD') + quotes
73
- } else {
74
- return $asString(date, skipQuotes)
67
+ asBoolean (bool) {
68
+ return bool && 'true' || 'false' // eslint-disable-line
75
69
  }
76
- }
77
70
 
78
- function $asTime (date, skipQuotes) {
79
- const quotes = skipQuotes === true ? '' : '"'
80
- if (date instanceof Date) {
81
- const hour = new Intl.DateTimeFormat('en', { hour: 'numeric', hour12: false }).format(date)
82
- const minute = new Intl.DateTimeFormat('en', { minute: 'numeric' }).format(date)
83
- const second = new Intl.DateTimeFormat('en', { second: 'numeric' }).format(date)
84
- return quotes + $pad2Zeros(hour) + ':' + $pad2Zeros(minute) + ':' + $pad2Zeros(second) + quotes
85
- } else if (date && typeof date.format === 'function') {
86
- return quotes + date.format('HH:mm:ss') + quotes
87
- } else {
88
- return $asString(date, skipQuotes)
71
+ asBooleanNullable (bool) {
72
+ return bool === null ? 'null' : this.asBoolean(bool)
89
73
  }
90
- }
91
74
 
92
- function $asString (str, skipQuotes) {
93
- const quotes = skipQuotes === true ? '' : '"'
94
- if (str instanceof Date) {
95
- return quotes + str.toISOString() + quotes
96
- } else if (str === null) {
97
- return quotes + quotes
98
- } else if (str instanceof RegExp) {
99
- str = str.source
100
- } else if (typeof str !== 'string') {
101
- str = str.toString()
75
+ asDatetime (date, skipQuotes) {
76
+ const quotes = skipQuotes === true ? '' : '"'
77
+ if (date instanceof Date) {
78
+ return quotes + date.toISOString() + quotes
79
+ }
80
+ return this.asString(date, skipQuotes)
102
81
  }
103
- // If we skipQuotes it means that we are using it as test
104
- // no need to test the string length for the render
105
- if (skipQuotes) {
106
- return str
82
+
83
+ asDatetimeNullable (date, skipQuotes) {
84
+ return date === null ? 'null' : this.asDatetime(date, skipQuotes)
107
85
  }
108
86
 
109
- if (str.length < 42) {
110
- return $asStringSmall(str)
111
- } else {
112
- return JSON.stringify(str)
87
+ asDate (date, skipQuotes) {
88
+ const quotes = skipQuotes === true ? '' : '"'
89
+ if (date instanceof Date) {
90
+ return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + quotes
91
+ }
92
+ return this.asString(date, skipQuotes)
113
93
  }
114
- }
115
94
 
116
- function $asStringNullable (str) {
117
- return str === null ? null : $asString(str)
118
- }
95
+ asDateNullable (date, skipQuotes) {
96
+ return date === null ? 'null' : this.asDate(date, skipQuotes)
97
+ }
119
98
 
120
- // magically escape strings for json
121
- // relying on their charCodeAt
122
- // everything below 32 needs JSON.stringify()
123
- // every string that contain surrogate needs JSON.stringify()
124
- // 34 and 92 happens all the time, so we
125
- // have a fast case for them
126
- function $asStringSmall (str) {
127
- const l = str.length
128
- let result = ''
129
- let last = 0
130
- let found = false
131
- let surrogateFound = false
132
- let point = 255
133
- // eslint-disable-next-line
134
- for (var i = 0; i < l && point >= 32; i++) {
135
- point = str.charCodeAt(i)
136
- if (point >= 0xD800 && point <= 0xDFFF) {
137
- // The current character is a surrogate.
138
- surrogateFound = true
139
- }
140
- if (point === 34 || point === 92) {
141
- result += str.slice(last, i) + '\\'
142
- last = i
143
- found = true
99
+ asTime (date, skipQuotes) {
100
+ const quotes = skipQuotes === true ? '' : '"'
101
+ if (date instanceof Date) {
102
+ return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + quotes
144
103
  }
104
+ return this.asString(date, skipQuotes)
145
105
  }
146
106
 
147
- if (!found) {
148
- result = str
149
- } else {
150
- result += str.slice(last)
107
+ asTimeNullable (date, skipQuotes) {
108
+ return date === null ? 'null' : this.asTime(date, skipQuotes)
151
109
  }
152
- return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
153
- }
154
110
 
111
+ asString (str, skipQuotes) {
112
+ const quotes = skipQuotes === true ? '' : '"'
113
+ if (str instanceof Date) {
114
+ return quotes + str.toISOString() + quotes
115
+ } else if (str === null) {
116
+ return quotes + quotes
117
+ } else if (str instanceof RegExp) {
118
+ str = str.source
119
+ } else if (typeof str !== 'string') {
120
+ str = str.toString()
121
+ }
122
+ // If we skipQuotes it means that we are using it as test
123
+ // no need to test the string length for the render
124
+ if (skipQuotes) {
125
+ return str
126
+ }
155
127
 
156
-
157
- /**
158
- * Used by schemas that are dependant on calling 'ajv.validate' during runtime,
159
- * it stores the value of the '$id' property of the schema (if it has it) inside
160
- * a cache which is used to figure out if the schema was compiled into a validator
161
- * by ajv on a previous call, if it was then the '$id' string will be used to
162
- * invoke 'ajv.validate', this allows:
163
- *
164
- * 1. Schemas that depend on ajv.validate calls to leverage ajv caching system.
165
- * 2. To avoid errors, since directly invoking 'ajv.validate' with the same
166
- * schema (that contains an '$id' property) twice will throw an error.
167
- */
168
- const $validateWithAjv = (function() {
169
- const cache = new Set()
170
-
171
- return function (schema, target) {
172
- const id = schema.$id
173
-
174
- if (!id) {
175
- return ajv.validate(schema, target)
176
- }
128
+ if (str.length < 42) {
129
+ return this.asStringSmall(str)
130
+ } else {
131
+ return JSON.stringify(str)
132
+ }
133
+ }
177
134
 
178
- const cached = cache.has(id)
135
+ asStringNullable (str) {
136
+ return str === null ? 'null' : this.asString(str)
137
+ }
179
138
 
180
- if (cached) {
181
- return ajv.validate(id, target)
182
- } else {
183
- cache.add(id)
184
- return ajv.validate(schema, target)
185
- }
139
+ // magically escape strings for json
140
+ // relying on their charCodeAt
141
+ // everything below 32 needs JSON.stringify()
142
+ // every string that contain surrogate needs JSON.stringify()
143
+ // 34 and 92 happens all the time, so we
144
+ // have a fast case for them
145
+ asStringSmall (str) {
146
+ const l = str.length
147
+ let result = ''
148
+ let last = 0
149
+ let found = false
150
+ let surrogateFound = false
151
+ let point = 255
152
+ // eslint-disable-next-line
153
+ for (var i = 0; i < l && point >= 32; i++) {
154
+ point = str.charCodeAt(i)
155
+ if (point >= 0xD800 && point <= 0xDFFF) {
156
+ // The current character is a surrogate.
157
+ surrogateFound = true
186
158
  }
187
- })()
159
+ if (point === 34 || point === 92) {
160
+ result += str.slice(last, i) + '\\'
161
+ last = i
162
+ found = true
163
+ }
164
+ }
165
+
166
+ if (!found) {
167
+ result = str
168
+ } else {
169
+ result += str.slice(last)
170
+ }
171
+ return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
172
+ }
173
+ }
188
174
 
175
+
176
+
177
+ const serializer = new Serializer({"mode":"standalone"})
178
+
189
179
 
190
- function parseInteger(int) { return Math.trunc(int) }
180
+
181
+ function main (input) {
182
+ let json = ''
183
+ json += anonymous0(input)
184
+ return json
185
+ }
191
186
 
192
- function $main (input) {
187
+ function anonymous0 (input) {
188
+ // main
193
189
 
194
190
  var obj = (input && typeof input.toJSON === 'function')
195
191
  ? input.toJSON()
@@ -198,60 +194,60 @@ function $asStringSmall (str) {
198
194
  var json = '{'
199
195
  var addComma = false
200
196
 
201
- var t = Number(obj["statusCode"])
202
- if (!isNaN(t)) {
203
-
197
+ if (obj["statusCode"] !== undefined) {
198
+
204
199
  if (addComma) {
205
200
  json += ','
206
201
  } else {
207
202
  addComma = true
208
203
  }
209
204
 
210
- json += "\"statusCode\"" + ':' + t
211
-
205
+ json += "\"statusCode\"" + ':'
206
+ json += serializer.asNumber.bind(serializer)(obj["statusCode"])
212
207
  }
213
208
 
214
- if (obj["code"] !== undefined) {
215
-
209
+ if (obj["code"] !== undefined) {
210
+
216
211
  if (addComma) {
217
212
  json += ','
218
213
  } else {
219
214
  addComma = true
220
215
  }
221
216
 
222
- json += "\"code\"" + ':'
223
- json += $asString(obj["code"])
217
+ json += "\"code\"" + ':'
218
+ json += serializer.asString.bind(serializer)(obj["code"])
224
219
  }
225
220
 
226
- if (obj["error"] !== undefined) {
227
-
221
+ if (obj["error"] !== undefined) {
222
+
228
223
  if (addComma) {
229
224
  json += ','
230
225
  } else {
231
226
  addComma = true
232
227
  }
233
228
 
234
- json += "\"error\"" + ':'
235
- json += $asString(obj["error"])
229
+ json += "\"error\"" + ':'
230
+ json += serializer.asString.bind(serializer)(obj["error"])
236
231
  }
237
232
 
238
- if (obj["message"] !== undefined) {
239
-
233
+ if (obj["message"] !== undefined) {
234
+
240
235
  if (addComma) {
241
236
  json += ','
242
237
  } else {
243
238
  addComma = true
244
239
  }
245
240
 
246
- json += "\"message\"" + ':'
247
- json += $asString(obj["message"])
241
+ json += "\"message\"" + ':'
242
+ json += serializer.asString.bind(serializer)(obj["message"])
248
243
  }
249
244
 
250
245
  json += '}'
251
246
  return json
252
247
  }
253
-
254
-
255
- ;
256
- return $main
257
248
 
249
+
250
+
251
+
252
+ module.exports = main
253
+
@@ -104,7 +104,17 @@ function checkVersion (fn) {
104
104
  const requiredVersion = meta.fastify
105
105
 
106
106
  const fastifyRc = /-rc.+$/.test(this.version)
107
+ if (fastifyRc === true && semver.gt(this.version, semver.coerce(requiredVersion)) === true) {
108
+ // A Fastify release candidate phase is taking place. In order to reduce
109
+ // the effort needed to test plugins with the RC, we allow plugins targeting
110
+ // the prior Fastify release to be loaded.
111
+ return
112
+ }
107
113
  if (requiredVersion && semver.satisfies(this.version, requiredVersion, { includePrerelease: fastifyRc }) === false) {
114
+ // We are not in a release candidate phase. Thus, we must honor the semver
115
+ // ranges defined by the plugin's metadata. Which is to say, if the plugin
116
+ // expects an older version of Fastify than the _current_ version, we will
117
+ // throw an error.
108
118
  throw new FST_ERR_PLUGIN_VERSION_MISMATCH(meta.name, requiredVersion, this.version)
109
119
  }
110
120
  }
package/lib/reply.js CHANGED
@@ -291,7 +291,7 @@ Reply.prototype.removeTrailer = function (key) {
291
291
 
292
292
  Reply.prototype.code = function (code) {
293
293
  const intValue = parseInt(code)
294
- if (isNaN(intValue) || intValue < 100 || intValue > 600) {
294
+ if (isNaN(intValue) || intValue < 100 || intValue > 599) {
295
295
  throw new FST_ERR_BAD_STATUS_CODE(code || String(code))
296
296
  }
297
297
 
package/lib/server.js CHANGED
@@ -41,7 +41,15 @@ function createServer (options, httpHandler) {
41
41
  listenOptions.cb = cb
42
42
  }
43
43
 
44
- const { host = 'localhost' } = listenOptions
44
+ // If we have a path specified, don't default host to 'localhost' so we don't end up listening
45
+ // on both path and host
46
+ // See https://github.com/fastify/fastify/issues/4007
47
+ let host
48
+ if (listenOptions.path == null) {
49
+ host = listenOptions.host ?? 'localhost'
50
+ } else {
51
+ host = listenOptions.host
52
+ }
45
53
  if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false) {
46
54
  listenOptions.host = host
47
55
  }
@@ -11,9 +11,14 @@ function wrapThenable (thenable, reply) {
11
11
  return
12
12
  }
13
13
 
14
- // this is for async functions that
15
- // are using reply.send directly
16
- if (payload !== undefined || reply.sent === false) {
14
+ // this is for async functions that are using reply.send directly
15
+ //
16
+ // since wrap-thenable will be called when using reply.send directly
17
+ // without actual return. the response can be sent already or
18
+ // the request may be terminated during the reply. in this situation,
19
+ // it require an extra checking of request.aborted to see whether
20
+ // the request is killed by client.
21
+ if (payload !== undefined || (reply.sent === false && reply.raw.headersSent === false && reply.request.raw.aborted === false)) {
17
22
  // we use a try-catch internally to avoid adding a catch to another
18
23
  // promise, increase promise perf by 10%
19
24
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.0.0",
3
+ "version": "4.0.3",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -12,11 +12,12 @@
12
12
  "coverage:ci": "npm run unit -- --cov --coverage-report=html --no-browser --no-check-coverage -R terse",
13
13
  "coverage:ci-check-coverage": "nyc check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
14
14
  "license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"",
15
- "lint": "npm run lint:standard && npm run lint:typescript",
15
+ "lint": "npm run lint:standard && npm run lint:typescript && npm run lint:markdown",
16
16
  "lint:fix": "standard --fix",
17
+ "lint:markdown": "markdownlint-cli2",
17
18
  "lint:standard": "standard | snazzy",
18
19
  "lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
19
- "prepublishOnly": "tap --no-check-coverage test/internals/version.test.js",
20
+ "prepublishOnly": "tap --no-check-coverage test/build/**.test.js",
20
21
  "test": "npm run lint && npm run unit && npm run test:typescript",
21
22
  "test:ci": "npm run unit -- -R terse --cov --coverage-report=lcovonly && npm run test:typescript",
22
23
  "test:report": "npm run lint && npm run unit:report && npm run test:typescript",
@@ -145,7 +146,7 @@
145
146
  "eslint-plugin-n": "^15.2.0",
146
147
  "eslint-plugin-promise": "^6.0.0",
147
148
  "fast-json-body": "^1.1.0",
148
- "fast-json-stringify": "^4.0.0",
149
+ "fast-json-stringify": "^4.2.0",
149
150
  "fastify-plugin": "^3.0.1",
150
151
  "fluent-json-schema": "^3.1.0",
151
152
  "form-data": "^4.0.0",
@@ -158,6 +159,7 @@
158
159
  "json-schema-to-ts": "^2.5.3",
159
160
  "JSONStream": "^1.3.5",
160
161
  "license-checker": "^25.0.1",
162
+ "markdownlint-cli2": "^0.4.0",
161
163
  "proxyquire": "^2.1.3",
162
164
  "pump": "^3.0.0",
163
165
  "self-cert": "^2.0.0",
@@ -168,7 +170,7 @@
168
170
  "split2": "^4.1.0",
169
171
  "standard": "^17.0.0-2",
170
172
  "tap": "^16.2.0",
171
- "tsd": "^0.20.0",
173
+ "tsd": "^0.21.0",
172
174
  "typescript": "^4.7.2",
173
175
  "undici": "^5.4.0",
174
176
  "x-xss-protection": "^2.0.0",
@@ -177,7 +179,7 @@
177
179
  "dependencies": {
178
180
  "@fastify/ajv-compiler": "^3.1.0",
179
181
  "@fastify/error": "^3.0.0",
180
- "@fastify/fast-json-stringify-compiler": "^3.0.0",
182
+ "@fastify/fast-json-stringify-compiler": "^3.0.1",
181
183
  "abstract-logging": "^2.0.1",
182
184
  "avvio": "^8.1.3",
183
185
  "find-my-way": "^6.3.0",
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const fs = require('fs')
6
+ const path = require('path')
7
+
8
+ const { code } = require('../../build/build-error-serializer')
9
+
10
+ test('check generated code syntax', async (t) => {
11
+ t.plan(1)
12
+
13
+ // standard is a esm, we import it like this
14
+ const { default: standard } = await import('standard')
15
+ const result = await standard.lintText(code)
16
+
17
+ // if there are any invalid syntax
18
+ // fatal count will be greater than 0
19
+ t.equal(result[0].fatalErrorCount, 0)
20
+ })
21
+
22
+ test('ensure the current error serializer is latest', async (t) => {
23
+ t.plan(1)
24
+
25
+ const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
26
+
27
+ t.equal(current.toString(), code)
28
+ })
@@ -4,7 +4,7 @@ const fs = require('fs')
4
4
  const path = require('path')
5
5
  const t = require('tap')
6
6
  const test = t.test
7
- const fastify = require('../..')()
7
+ const fastify = require('../../fastify')()
8
8
 
9
9
  test('should be the same as package.json', t => {
10
10
  t.plan(1)
@@ -196,14 +196,28 @@ if (os.platform() !== 'win32') {
196
196
  const fastify = Fastify()
197
197
  t.teardown(fastify.close.bind(fastify))
198
198
 
199
- const sockFile = path.join(os.tmpdir(), `${(Math.random().toString(16) + '0000000').substr(2, 8)}-server.sock`)
199
+ const sockFile = path.join(os.tmpdir(), `${(Math.random().toString(16) + '0000000').slice(2, 10)}-server.sock`)
200
200
  try {
201
201
  fs.unlinkSync(sockFile)
202
202
  } catch (e) { }
203
203
 
204
204
  fastify.listen({ path: sockFile }, (err, address) => {
205
205
  t.error(err)
206
- t.equal(sockFile, fastify.server.address())
206
+ t.strictSame(fastify.addresses(), [sockFile])
207
+ t.equal(address, sockFile)
208
+ })
209
+ })
210
+ } else {
211
+ test('listen on socket', t => {
212
+ t.plan(3)
213
+ const fastify = Fastify()
214
+ t.teardown(fastify.close.bind(fastify))
215
+
216
+ const sockFile = `\\\\.\\pipe\\${(Math.random().toString(16) + '0000000').slice(2, 10)}-server-sock`
217
+
218
+ fastify.listen({ path: sockFile }, (err, address) => {
219
+ t.error(err)
220
+ t.strictSame(fastify.addresses(), [sockFile])
207
221
  t.equal(address, sockFile)
208
222
  })
209
223
  })
@@ -1057,6 +1057,38 @@ test('plugin metadata - release candidate', t => {
1057
1057
  }
1058
1058
  })
1059
1059
 
1060
+ test('fastify-rc loads prior version plugins', t => {
1061
+ t.plan(2)
1062
+ const fastify = Fastify()
1063
+ Object.defineProperty(fastify, 'version', {
1064
+ value: '99.0.0-rc.1'
1065
+ })
1066
+
1067
+ plugin[Symbol.for('plugin-meta')] = {
1068
+ name: 'plugin',
1069
+ fastify: '^98.1.0'
1070
+ }
1071
+ plugin2[Symbol.for('plugin-meta')] = {
1072
+ name: 'plugin2',
1073
+ fastify: '98.x'
1074
+ }
1075
+
1076
+ fastify.register(plugin)
1077
+
1078
+ fastify.ready((err) => {
1079
+ t.error(err)
1080
+ t.pass('everything right')
1081
+ })
1082
+
1083
+ function plugin (instance, opts, done) {
1084
+ done()
1085
+ }
1086
+
1087
+ function plugin2 (instance, opts, done) {
1088
+ done()
1089
+ }
1090
+ })
1091
+
1060
1092
  test('hasPlugin method exists as a function', t => {
1061
1093
  t.plan(1)
1062
1094
 
@@ -520,7 +520,13 @@ const invalidErrorCodes = [
520
520
  undefined,
521
521
  null,
522
522
  'error_code',
523
- 700 // out of the 100-600 range
523
+
524
+ // out of the 100-599 range:
525
+ 0,
526
+ 1,
527
+ 99,
528
+ 600,
529
+ 700
524
530
  ]
525
531
  invalidErrorCodes.forEach((invalidCode) => {
526
532
  test(`should throw error if error code is ${invalidCode}`, t => {