@platformatic/service 0.22.0 → 0.23.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.
Files changed (38) hide show
  1. package/fixtures/hello-client-ts/platformatic.service.json +2 -1
  2. package/index.js +118 -169
  3. package/lib/load-config.js +6 -1
  4. package/lib/plugins/clients.js +12 -0
  5. package/lib/plugins/cors.js +29 -0
  6. package/lib/plugins/file-watcher.js +44 -0
  7. package/lib/plugins/health-check.js +17 -0
  8. package/lib/plugins/plugins.js +56 -0
  9. package/lib/plugins/typescript.js +46 -0
  10. package/lib/root-endpoint/index.js +1 -0
  11. package/lib/schema.js +8 -1
  12. package/lib/start.mjs +27 -136
  13. package/lib/utils.js +2 -2
  14. package/package.json +9 -8
  15. package/test/autoload.test.js +77 -59
  16. package/test/cli/compile.test.mjs +45 -33
  17. package/test/cli/watch.test.mjs +39 -0
  18. package/test/clients.test.js +24 -17
  19. package/test/config.test.js +58 -86
  20. package/test/cors.test.js +48 -30
  21. package/test/fixtures/bad-typescript-plugin/platformatic.service.json +3 -1
  22. package/test/fixtures/typescript-autoload/platformatic.service.json +3 -1
  23. package/test/fixtures/typescript-plugin/platformatic.service.json +3 -1
  24. package/test/fixtures/typescript-plugin-nocompile/platformatic.service.json +2 -1
  25. package/test/graphql.test.js +18 -14
  26. package/test/healthcheck.test.js +22 -13
  27. package/test/https.test.js +11 -8
  28. package/test/lambda.test.js +103 -0
  29. package/test/load-and-reload-files.test.js +97 -112
  30. package/test/load-plugin.test.js +8 -4
  31. package/test/metrics.test.js +59 -39
  32. package/test/routes.test.js +43 -25
  33. package/test/utils.test.js +2 -2
  34. package/test/watch.test.js +182 -0
  35. /package/lib/{graphql.js → plugins/graphql.js} +0 -0
  36. /package/lib/{metrics-plugin.js → plugins/metrics.js} +0 -0
  37. /package/lib/{openapi.js → plugins/openapi.js} +0 -0
  38. /package/lib/{sandbox-wrapper.js → plugins/sandbox-wrapper.js} +0 -0
package/lib/start.mjs CHANGED
@@ -1,9 +1,6 @@
1
- import { dirname } from 'path'
2
- import { FileWatcher } from '@platformatic/utils'
3
1
  import { buildServer } from '../index.js'
4
2
  import close from 'close-with-grace'
5
3
  import { loadConfig, generateDefaultConfig } from './load-config.js'
6
- import { compileWatch } from './compile.js'
7
4
  import { addLoggerToTheConfig } from './utils.js'
8
5
 
9
6
  // TODO make sure coverage is reported for Windows
@@ -18,183 +15,77 @@ function defaultConfig () {
18
15
  }
19
16
  }
20
17
 
21
- export function buildStart (_loadConfig, _buildServer) {
18
+ export function buildStart (_loadConfig, _buildServer, _configManagerConfig) {
22
19
  return async function start (_args) {
23
- const _defaultConfig = defaultConfig()
20
+ const _defaultConfig = _configManagerConfig ?? defaultConfig()
24
21
  const { configManager, args } = await _loadConfig({}, _args, _defaultConfig)
25
22
 
26
23
  const config = configManager.current
27
24
 
28
- // Set the logger if not present
25
+ // Disable hot reload from the CLI
26
+ if (args.hotReload === false && configManager.current.plugins) {
27
+ configManager.current.plugins.hotReload = false
28
+ }
29
29
  addLoggerToTheConfig(config)
30
30
 
31
- if (
32
- config.plugins?.typescript &&
33
- config.plugins?.watch !== false
34
- ) {
35
- try {
36
- await compileWatch(dirname(configManager.fullPath), config)
37
- } catch (error) {
38
- // TODO route this to a logger
39
- console.error(error)
40
- process.exit(1)
31
+ const _transformConfig = configManager._transformConfig.bind(configManager)
32
+ configManager._transformConfig = function () {
33
+ const config = configManager.current
34
+ if (args.hotReload === false && config.plugins) {
35
+ config.plugins.hotReload = false
41
36
  }
37
+ addLoggerToTheConfig(config)
38
+ return _transformConfig(config)
42
39
  }
43
40
 
44
- let server = null
45
-
46
- // Disable hot reload from the CLI
47
- if (args.hotReload === false && configManager.current.plugins) {
48
- configManager.current.plugins.hotReload = false
49
- }
41
+ let app = null
50
42
 
51
43
  try {
52
44
  // Set the location of the config
53
- server = await _buildServer({
54
- ...config,
55
- configManager
56
- })
45
+ app = await _buildServer({ ...config, configManager })
46
+
47
+ await app.start()
48
+ // TODO: this log is used in the start command. Should be replaced
49
+ app.log.info({ url: app.url })
57
50
  } catch (err) {
58
51
  // TODO route this to a logger
59
52
  console.error(err)
60
53
  process.exit(1)
61
54
  }
62
55
 
63
- server.app.platformatic.configManager = configManager
64
- server.app.platformatic.config = config
65
- configManager.on('update', (newConfig) => {
66
- if (args.hotReload === false && configManager.current.plugins) {
67
- configManager.current.plugins.hotReload = false
68
- }
69
- onConfigUpdated(newConfig, server)
70
- })
71
-
72
- if (
73
- config.plugins !== undefined &&
74
- config.watch !== false
75
- ) {
76
- await startFileWatching(server, args.hotReload)
77
- }
78
-
79
- try {
80
- await server.listen()
81
- } catch (err) {
82
- server.app.log.error({ err })
83
- process.exit(1)
84
- }
85
-
86
- configManager.on('error', function (err) {
87
- server.app.log.error({
88
- err
89
- }, 'error reloading the configuration')
90
- })
91
-
92
56
  // Ignore from CI because SIGUSR2 is not available
93
57
  // on Windows
94
58
  process.on('SIGUSR2', function () {
95
- server.app.log.info('reloading configuration')
96
- server.restart()
97
- .catch((err) => {
98
- server.app.log.error({
99
- err: {
100
- message: err.message,
101
- stack: err.stack
102
- }
103
- }, 'failed to restart')
104
- })
59
+ app.log.info('reloading configuration')
60
+ safeRestart(app)
105
61
  return false
106
62
  })
107
63
 
108
64
  close(async ({ signal, err }) => {
109
65
  // Windows does not support trapping signals
110
66
  if (err) {
111
- server.app.log.error({
67
+ app.log.error({
112
68
  err: {
113
69
  message: err.message,
114
70
  stack: err.stack
115
71
  }
116
72
  }, 'exiting')
117
73
  } else if (signal) {
118
- server.app.log.info({ signal }, 'received signal')
74
+ app.log.info({ signal }, 'received signal')
119
75
  }
120
76
 
121
- await server.stop()
77
+ await app.close()
122
78
  })
123
79
  }
124
80
  }
125
81
 
126
82
  const start = buildStart(loadConfig, buildServer)
127
83
 
128
- async function startFileWatching (server, hotReload) {
129
- const configManager = server.app.platformatic.configManager
130
- const config = configManager.current
131
-
132
- const fileWatcher = new FileWatcher({
133
- path: dirname(configManager.fullPath),
134
- allowToWatch: config.watch?.allow,
135
- watchIgnore: config.watch?.ignore
136
- })
137
- fileWatcher.on('update', () => {
138
- onFilesUpdated(server, hotReload)
139
- })
140
- fileWatcher.startWatching()
141
-
142
- server.app.log.debug('start watching files')
143
- server.app.platformatic.fileWatcher = fileWatcher
144
- }
145
-
146
- async function stopFileWatching (server) {
147
- const fileWatcher = server.app.platformatic.fileWatcher
148
- if (fileWatcher !== undefined) {
149
- await fileWatcher.stopWatching()
150
-
151
- server.app.log.debug('stop watching files')
152
- server.app.platformatic.fileWatcher = undefined
153
- }
154
- }
155
-
156
- async function onConfigUpdated (newConfig, server) {
84
+ async function safeRestart (app) {
157
85
  try {
158
- server.app.platformatic.config = newConfig
159
- server.app.log.debug('config changed')
160
- server.app.log.trace({ newConfig }, 'new config')
161
-
162
- await stopFileWatching(server)
163
-
164
- await server.restart(newConfig)
165
- } catch (err) {
166
- // TODO: test this
167
- server.app.log.error({
168
- err: {
169
- message: err.message,
170
- stack: err.stack
171
- }
172
- }, 'failed to reload config')
173
- } finally {
174
- if (
175
- newConfig.plugins !== undefined &&
176
- newConfig.plugins.watch !== false
177
- ) {
178
- await startFileWatching(server)
179
- }
180
- }
181
- }
182
-
183
- async function onFilesUpdated (server, hotReload) {
184
- // Reload the config as well, otherwise we will have problems
185
- // in case the files watcher triggers the config watcher too
186
- const configManager = server.app.platformatic.configManager
187
- try {
188
- server.app.log.debug('files changed')
189
- await configManager.parse()
190
- if (hotReload === false && configManager.current.plugins) {
191
- configManager.current.plugins.hotReload = false
192
- }
193
- addLoggerToTheConfig(configManager.current)
194
- await server.restart(configManager.current)
86
+ await app.restart()
195
87
  } catch (err) {
196
- // TODO: test this
197
- server.app.log.error({
88
+ app.log.error({
198
89
  err: {
199
90
  message: err.message,
200
91
  stack: err.stack
package/lib/utils.js CHANGED
@@ -33,7 +33,7 @@ function addLoggerToTheConfig (config) {
33
33
  }
34
34
  /* c8 ignore stop */
35
35
 
36
- function getJSPluginPath (configPath, tsPluginPath, compileDir) {
36
+ function getJSPluginPath (workingDir, tsPluginPath, compileDir) {
37
37
  if (tsPluginPath.endsWith('js')) {
38
38
  return tsPluginPath
39
39
  }
@@ -49,7 +49,7 @@ function getJSPluginPath (configPath, tsPluginPath, compileDir) {
49
49
  newBaseName = basename(tsPluginPath)
50
50
  }
51
51
 
52
- const tsPluginRelativePath = relative(dirname(configPath), tsPluginPath)
52
+ const tsPluginRelativePath = relative(workingDir, tsPluginPath)
53
53
  const jsPluginRelativePath = join(
54
54
  dirname(tsPluginRelativePath),
55
55
  newBaseName
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -17,7 +17,8 @@
17
17
  },
18
18
  "homepage": "https://github.com/platformatic/platformatic#readme",
19
19
  "devDependencies": {
20
- "@matteo.collina/worker": "^3.0.0",
20
+ "@fastify/aws-lambda": "^3.2.0",
21
+ "@matteo.collina/worker": "^3.1.0",
21
22
  "bindings": "^1.5.0",
22
23
  "c8": "^7.13.0",
23
24
  "self-cert": "^2.0.0",
@@ -39,10 +40,10 @@
39
40
  "@fastify/basic-auth": "^5.0.0",
40
41
  "@fastify/cors": "^8.2.1",
41
42
  "@fastify/deepmerge": "^1.3.0",
42
- "@fastify/restartable": "^1.4.0",
43
+ "@fastify/restartable": "^2.1.0",
43
44
  "@fastify/static": "^6.10.1",
44
45
  "@fastify/swagger": "^8.3.1",
45
- "@fastify/swagger-ui": "^1.8.0",
46
+ "@fastify/swagger-ui": "^1.8.1",
46
47
  "@fastify/under-pressure": "^8.2.0",
47
48
  "@mercuriusjs/federation": "^2.0.0",
48
49
  "close-with-grace": "^1.2.0",
@@ -59,13 +60,13 @@
59
60
  "help-me": "^4.2.0",
60
61
  "mercurius": "^13.0.0",
61
62
  "minimist": "^1.2.8",
62
- "pino": "^8.11.0",
63
+ "pino": "^8.12.0",
63
64
  "pino-pretty": "^10.0.0",
64
65
  "rfdc": "^1.3.0",
65
66
  "ua-parser-js": "^1.0.35",
66
- "@platformatic/client": "0.22.0",
67
- "@platformatic/config": "0.22.0",
68
- "@platformatic/utils": "0.22.0"
67
+ "@platformatic/client": "0.23.0",
68
+ "@platformatic/config": "0.23.0",
69
+ "@platformatic/utils": "0.23.0"
69
70
  },
70
71
  "standard": {
71
72
  "ignore": [
@@ -19,26 +19,28 @@ test('autoload & filesystem based routing / watch disabled', async ({ teardown,
19
19
  metrics: false
20
20
  }
21
21
 
22
- const server = await buildServer(config)
23
- teardown(server.stop)
24
- await server.listen()
22
+ const app = await buildServer(config)
23
+ teardown(async () => {
24
+ await app.close()
25
+ })
26
+ await app.start()
25
27
 
26
28
  {
27
- const res = await request(`${server.url}/`)
29
+ const res = await request(`${app.url}/`)
28
30
  equal(res.statusCode, 200, 'status code')
29
31
  const body = await res.body.json()
30
32
  equal(body.hello, 'from root', 'body')
31
33
  }
32
34
 
33
35
  {
34
- const res = await request(`${server.url}/foo/bar`)
36
+ const res = await request(`${app.url}/foo/bar`)
35
37
  equal(res.statusCode, 200, 'status code')
36
38
  const body = await res.body.json()
37
39
  equal(body.hello, 'from bar', 'body')
38
40
  }
39
41
 
40
42
  {
41
- const res = await request(`${server.url}/foo/baz`)
43
+ const res = await request(`${app.url}/foo/baz`)
42
44
  equal(res.statusCode, 200, 'status code')
43
45
  const body = await res.body.json()
44
46
  equal(body.hello, 'from baz', 'body')
@@ -58,26 +60,28 @@ test('autoload & filesystem based routing / watch enabled', async ({ teardown, e
58
60
  metrics: false
59
61
  }
60
62
 
61
- const server = await buildServer(config)
62
- teardown(server.stop)
63
- await server.listen()
63
+ const app = await buildServer(config)
64
+ teardown(async () => {
65
+ await app.close()
66
+ })
67
+ await app.start()
64
68
 
65
69
  {
66
- const res = await request(`${server.url}/`)
70
+ const res = await request(`${app.url}/`)
67
71
  equal(res.statusCode, 200, 'status code')
68
72
  const body = await res.body.json()
69
73
  equal(body.hello, 'from root', 'body')
70
74
  }
71
75
 
72
76
  {
73
- const res = await request(`${server.url}/foo/bar`)
77
+ const res = await request(`${app.url}/foo/bar`)
74
78
  equal(res.statusCode, 200, 'status code')
75
79
  const body = await res.body.json()
76
80
  equal(body.hello, 'from bar', 'body')
77
81
  }
78
82
 
79
83
  {
80
- const res = await request(`${server.url}/foo/baz`)
84
+ const res = await request(`${app.url}/foo/baz`)
81
85
  equal(res.statusCode, 200, 'status code')
82
86
  const body = await res.body.json()
83
87
  equal(body.hello, 'from baz', 'body')
@@ -101,33 +105,35 @@ test('multiple files', async ({ teardown, equal }) => {
101
105
  metrics: false
102
106
  }
103
107
 
104
- const server = await buildServer(config)
105
- teardown(server.stop)
106
- await server.listen()
108
+ const app = await buildServer(config)
109
+ teardown(async () => {
110
+ await app.close()
111
+ })
112
+ await app.start()
107
113
 
108
114
  {
109
- const res = await request(`${server.url}/`)
115
+ const res = await request(`${app.url}/`)
110
116
  equal(res.statusCode, 200, 'status code')
111
117
  const body = await res.body.json()
112
118
  equal(body.hello, 'from root', 'body')
113
119
  }
114
120
 
115
121
  {
116
- const res = await request(`${server.url}/foo/bar`)
122
+ const res = await request(`${app.url}/foo/bar`)
117
123
  equal(res.statusCode, 200, 'status code')
118
124
  const body = await res.body.json()
119
125
  equal(body.hello, 'from bar', 'body')
120
126
  }
121
127
 
122
128
  {
123
- const res = await request(`${server.url}/foo/baz`)
129
+ const res = await request(`${app.url}/foo/baz`)
124
130
  equal(res.statusCode, 200, 'status code')
125
131
  const body = await res.body.json()
126
132
  equal(body.hello, 'from baz', 'body')
127
133
  }
128
134
 
129
135
  {
130
- const res = await request(`${server.url}/foo/with-decorator`)
136
+ const res = await request(`${app.url}/foo/with-decorator`)
131
137
  equal(res.statusCode, 200, 'status code')
132
138
  const body = await res.body.json()
133
139
  equal(body.hello, 'bar', 'body')
@@ -151,33 +157,35 @@ test('multiple files / watch false', async ({ teardown, equal }) => {
151
157
  metrics: false
152
158
  }
153
159
 
154
- const server = await buildServer(config)
155
- teardown(server.stop)
156
- await server.listen()
160
+ const app = await buildServer(config)
161
+ teardown(async () => {
162
+ await app.close()
163
+ })
164
+ await app.start()
157
165
 
158
166
  {
159
- const res = await request(`${server.url}/`)
167
+ const res = await request(`${app.url}/`)
160
168
  equal(res.statusCode, 200, 'status code')
161
169
  const body = await res.body.json()
162
170
  equal(body.hello, 'from root', 'body')
163
171
  }
164
172
 
165
173
  {
166
- const res = await request(`${server.url}/foo/bar`)
174
+ const res = await request(`${app.url}/foo/bar`)
167
175
  equal(res.statusCode, 200, 'status code')
168
176
  const body = await res.body.json()
169
177
  equal(body.hello, 'from bar', 'body')
170
178
  }
171
179
 
172
180
  {
173
- const res = await request(`${server.url}/foo/baz`)
181
+ const res = await request(`${app.url}/foo/baz`)
174
182
  equal(res.statusCode, 200, 'status code')
175
183
  const body = await res.body.json()
176
184
  equal(body.hello, 'from baz', 'body')
177
185
  }
178
186
 
179
187
  {
180
- const res = await request(`${server.url}/foo/with-decorator`)
188
+ const res = await request(`${app.url}/foo/with-decorator`)
181
189
  equal(res.statusCode, 200, 'status code')
182
190
  const body = await res.body.json()
183
191
  equal(body.hello, 'bar', 'body')
@@ -197,26 +205,28 @@ test('autoload & filesystem based routing / watch disabled / no object', async (
197
205
  metrics: false
198
206
  }
199
207
 
200
- const server = await buildServer(config)
201
- teardown(server.stop)
202
- await server.listen()
208
+ const app = await buildServer(config)
209
+ teardown(async () => {
210
+ await app.close()
211
+ })
212
+ await app.start()
203
213
 
204
214
  {
205
- const res = await request(`${server.url}/`)
215
+ const res = await request(`${app.url}/`)
206
216
  equal(res.statusCode, 200, 'status code')
207
217
  const body = await res.body.json()
208
218
  equal(body.hello, 'from root', 'body')
209
219
  }
210
220
 
211
221
  {
212
- const res = await request(`${server.url}/foo/bar`)
222
+ const res = await request(`${app.url}/foo/bar`)
213
223
  equal(res.statusCode, 200, 'status code')
214
224
  const body = await res.body.json()
215
225
  equal(body.hello, 'from bar', 'body')
216
226
  }
217
227
 
218
228
  {
219
- const res = await request(`${server.url}/foo/baz`)
229
+ const res = await request(`${app.url}/foo/baz`)
220
230
  equal(res.statusCode, 200, 'status code')
221
231
  const body = await res.body.json()
222
232
  equal(body.hello, 'from baz', 'body')
@@ -236,26 +246,28 @@ test('autoload & filesystem based routing / watch enabled / no object', async ({
236
246
  metrics: false
237
247
  }
238
248
 
239
- const server = await buildServer(config)
240
- teardown(server.stop)
241
- await server.listen()
249
+ const app = await buildServer(config)
250
+ teardown(async () => {
251
+ await app.close()
252
+ })
253
+ await app.start()
242
254
 
243
255
  {
244
- const res = await request(`${server.url}/`)
256
+ const res = await request(`${app.url}/`)
245
257
  equal(res.statusCode, 200, 'status code')
246
258
  const body = await res.body.json()
247
259
  equal(body.hello, 'from root', 'body')
248
260
  }
249
261
 
250
262
  {
251
- const res = await request(`${server.url}/foo/bar`)
263
+ const res = await request(`${app.url}/foo/bar`)
252
264
  equal(res.statusCode, 200, 'status code')
253
265
  const body = await res.body.json()
254
266
  equal(body.hello, 'from bar', 'body')
255
267
  }
256
268
 
257
269
  {
258
- const res = await request(`${server.url}/foo/baz`)
270
+ const res = await request(`${app.url}/foo/baz`)
259
271
  equal(res.statusCode, 200, 'status code')
260
272
  const body = await res.body.json()
261
273
  equal(body.hello, 'from baz', 'body')
@@ -275,33 +287,35 @@ test('multiple files / no object', async ({ teardown, equal }) => {
275
287
  metrics: false
276
288
  }
277
289
 
278
- const server = await buildServer(config)
279
- teardown(server.stop)
280
- await server.listen()
290
+ const app = await buildServer(config)
291
+ teardown(async () => {
292
+ await app.close()
293
+ })
294
+ await app.start()
281
295
 
282
296
  {
283
- const res = await request(`${server.url}/`)
297
+ const res = await request(`${app.url}/`)
284
298
  equal(res.statusCode, 200, 'status code')
285
299
  const body = await res.body.json()
286
300
  equal(body.hello, 'from root', 'body')
287
301
  }
288
302
 
289
303
  {
290
- const res = await request(`${server.url}/foo/bar`)
304
+ const res = await request(`${app.url}/foo/bar`)
291
305
  equal(res.statusCode, 200, 'status code')
292
306
  const body = await res.body.json()
293
307
  equal(body.hello, 'from bar', 'body')
294
308
  }
295
309
 
296
310
  {
297
- const res = await request(`${server.url}/foo/baz`)
311
+ const res = await request(`${app.url}/foo/baz`)
298
312
  equal(res.statusCode, 200, 'status code')
299
313
  const body = await res.body.json()
300
314
  equal(body.hello, 'from baz', 'body')
301
315
  }
302
316
 
303
317
  {
304
- const res = await request(`${server.url}/foo/with-decorator`)
318
+ const res = await request(`${app.url}/foo/with-decorator`)
305
319
  equal(res.statusCode, 200, 'status code')
306
320
  const body = await res.body.json()
307
321
  equal(body.hello, 'bar', 'body')
@@ -324,33 +338,35 @@ test('multiple files / watch false / no object', async ({ teardown, equal }) =>
324
338
  metrics: false
325
339
  }
326
340
 
327
- const server = await buildServer(config)
328
- teardown(server.stop)
329
- await server.listen()
341
+ const app = await buildServer(config)
342
+ teardown(async () => {
343
+ await app.close()
344
+ })
345
+ await app.start()
330
346
 
331
347
  {
332
- const res = await request(`${server.url}/`)
348
+ const res = await request(`${app.url}/`)
333
349
  equal(res.statusCode, 200, 'status code')
334
350
  const body = await res.body.json()
335
351
  equal(body.hello, 'from root', 'body')
336
352
  }
337
353
 
338
354
  {
339
- const res = await request(`${server.url}/foo/bar`)
355
+ const res = await request(`${app.url}/foo/bar`)
340
356
  equal(res.statusCode, 200, 'status code')
341
357
  const body = await res.body.json()
342
358
  equal(body.hello, 'from bar', 'body')
343
359
  }
344
360
 
345
361
  {
346
- const res = await request(`${server.url}/foo/baz`)
362
+ const res = await request(`${app.url}/foo/baz`)
347
363
  equal(res.statusCode, 200, 'status code')
348
364
  const body = await res.body.json()
349
365
  equal(body.hello, 'from baz', 'body')
350
366
  }
351
367
 
352
368
  {
353
- const res = await request(`${server.url}/foo/with-decorator`)
369
+ const res = await request(`${app.url}/foo/with-decorator`)
354
370
  equal(res.statusCode, 200, 'status code')
355
371
  const body = await res.body.json()
356
372
  equal(body.hello, 'bar', 'body')
@@ -380,33 +396,35 @@ test('nested directories', async ({ teardown, equal, same }) => {
380
396
  }
381
397
  }
382
398
 
383
- const server = await buildServer(config)
384
- teardown(server.stop)
385
- await server.listen()
399
+ const app = await buildServer(config)
400
+ teardown(async () => {
401
+ await app.close()
402
+ })
403
+ await app.start()
386
404
 
387
405
  {
388
- const res = await request(`${server.url}/inventory/product/42`)
406
+ const res = await request(`${app.url}/inventory/product/42`)
389
407
  equal(res.statusCode, 200, 'status code')
390
408
  const body = await res.body.json()
391
409
  same(body, { sku: 42, inStore: 2 }, 'body')
392
410
  }
393
411
 
394
412
  {
395
- const res = await request(`${server.url}/catalogue/products`)
413
+ const res = await request(`${app.url}/catalogue/products`)
396
414
  equal(res.statusCode, 200, 'status code')
397
415
  const body = await res.body.json()
398
416
  same(body, [{ sku: 42, name: 'foo', inStore: 2 }, { sku: 43, name: 'bar', inStore: 0 }], 'body')
399
417
  }
400
418
 
401
419
  {
402
- const res = await request(`${server.url}/foo/baz`)
420
+ const res = await request(`${app.url}/foo/baz`)
403
421
  equal(res.statusCode, 404, 'status code')
404
422
  const body = await res.body.text()
405
423
  equal(body, 'I\'m sorry, I couldn\'t find what you were looking for.')
406
424
  }
407
425
 
408
426
  {
409
- const res = await request(`${server.url}/catalogue/error`)
427
+ const res = await request(`${app.url}/catalogue/error`)
410
428
  equal(res.statusCode, 500, 'status code')
411
429
  const body = await res.body.text()
412
430
  equal(body, 'I\'m sorry, there was an error processing your request.')