@platformatic/runtime 0.26.1 → 0.28.1
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/fixtures/composerApp/api1.json +65 -0
- package/fixtures/composerApp/platformatic.composer.json +23 -0
- package/fixtures/configs/invalid-schema-type.config.json +19 -0
- package/fixtures/configs/monorepo-client-without-id.json +24 -0
- package/fixtures/configs/no-schema.config.json +18 -0
- package/fixtures/dbApp/platformatic.db.json +22 -0
- package/fixtures/monorepo/serviceApp/platformatic.service-client-without-id.json +21 -0
- package/fixtures/monorepo/serviceApp/platformatic.service.json +1 -1
- package/fixtures/serviceAppThrowsOnStart/platformatic.service.json +0 -1
- package/fixtures/start-command-in-runtime.js +14 -0
- package/fixtures/start-command.js +9 -0
- package/fixtures/starter.js +9 -0
- package/index.js +5 -25
- package/lib/api.js +257 -0
- package/lib/app.js +62 -68
- package/lib/build-server.js +28 -0
- package/lib/config.js +36 -8
- package/lib/loader.mjs +21 -13
- package/lib/start.js +7 -19
- package/lib/unified-api.js +198 -0
- package/lib/worker.js +29 -53
- package/package.json +16 -13
- package/test/api.test.js +294 -0
- package/test/app.test.js +76 -13
- package/test/cli/start.test.mjs +4 -4
- package/test/config.test.js +9 -0
- package/test/unified-api.test.js +354 -0
- package/test/worker.test.js +0 -21
package/lib/worker.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
|
|
2
3
|
const { parentPort, workerData } = require('node:worker_threads')
|
|
3
4
|
const FastifyUndiciDispatcher = require('fastify-undici-dispatcher')
|
|
4
5
|
const { Agent, setGlobalDispatcher } = require('undici')
|
|
5
6
|
const { PlatformaticApp } = require('./app')
|
|
7
|
+
const { RuntimeApi } = require('./api')
|
|
8
|
+
|
|
6
9
|
const loaderPort = globalThis.LOADER_PORT // Added by loader.mjs.
|
|
7
10
|
const globalAgent = new Agent()
|
|
8
11
|
const globalDispatcher = new FastifyUndiciDispatcher({
|
|
@@ -10,41 +13,36 @@ const globalDispatcher = new FastifyUndiciDispatcher({
|
|
|
10
13
|
// setting the domain here allows for fail-fast scenarios
|
|
11
14
|
domain: '.plt.local'
|
|
12
15
|
})
|
|
16
|
+
const pino = require('pino')
|
|
17
|
+
const { isatty } = require('tty')
|
|
18
|
+
|
|
13
19
|
const applications = new Map()
|
|
14
|
-
let entrypoint
|
|
15
20
|
|
|
16
21
|
delete globalThis.LOADER_PORT
|
|
17
22
|
setGlobalDispatcher(globalDispatcher)
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
for (const app of applications.values()) {
|
|
21
|
-
await app.handleProcessLevelEvent(msg)
|
|
24
|
+
let transport
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
/* c8 ignore next 5 */
|
|
27
|
+
if (isatty(1)) {
|
|
28
|
+
transport = pino.transport({
|
|
29
|
+
target: 'pino-pretty'
|
|
30
|
+
})
|
|
31
|
+
}
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
33
|
+
const logger = pino(transport)
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
break
|
|
42
|
-
case undefined:
|
|
43
|
-
// Ignore
|
|
44
|
-
break
|
|
45
|
-
default:
|
|
46
|
-
throw new Error(`unknown message type: '${msg.msg}'`)
|
|
47
|
-
}
|
|
35
|
+
/* c8 ignore next 4 */
|
|
36
|
+
process.once('uncaughtException', (err) => {
|
|
37
|
+
logger.error({ err }, 'runtime error')
|
|
38
|
+
throw err
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Tested by test/cli/start.test.mjs by C8 does not see it.
|
|
42
|
+
/* c8 ignore next 4 */
|
|
43
|
+
process.once('unhandledRejection', (err) => {
|
|
44
|
+
logger.error({ err }, 'runtime error')
|
|
45
|
+
throw err
|
|
48
46
|
})
|
|
49
47
|
|
|
50
48
|
async function main () {
|
|
@@ -52,37 +50,15 @@ async function main () {
|
|
|
52
50
|
|
|
53
51
|
for (let i = 0; i < services.length; ++i) {
|
|
54
52
|
const service = services[i]
|
|
55
|
-
const app = new PlatformaticApp(service, loaderPort)
|
|
53
|
+
const app = new PlatformaticApp(service, loaderPort, logger)
|
|
56
54
|
|
|
57
55
|
applications.set(service.id, app)
|
|
58
|
-
|
|
59
|
-
if (service.entrypoint) {
|
|
60
|
-
entrypoint = app
|
|
61
|
-
}
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
function configureDispatcher () {
|
|
68
|
-
const { services } = workerData.config
|
|
69
|
-
|
|
70
|
-
// Setup the local services in the global dispatcher.
|
|
71
|
-
for (let i = 0; i < services.length; ++i) {
|
|
72
|
-
const service = services[i]
|
|
73
|
-
const serviceApp = applications.get(service.id)
|
|
74
|
-
const serviceUrl = new URL(service.localUrl)
|
|
75
|
-
|
|
76
|
-
globalDispatcher.route(serviceUrl.host, serviceApp.server)
|
|
77
|
-
|
|
78
|
-
for (let j = 0; j < service.dependencies.length; ++j) {
|
|
79
|
-
const depConfig = service.dependencies[j]
|
|
80
|
-
const depApp = applications.get(depConfig.id)
|
|
81
|
-
const depUrl = new URL(depConfig.url)
|
|
58
|
+
const runtime = new RuntimeApi(applications, globalDispatcher)
|
|
59
|
+
runtime.startListening(parentPort)
|
|
82
60
|
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
}
|
|
61
|
+
parentPort.postMessage('plt:init')
|
|
86
62
|
}
|
|
87
63
|
|
|
88
64
|
main()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://github.com/platformatic/platformatic#readme",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"c8": "^
|
|
21
|
-
"execa": "^7.
|
|
20
|
+
"c8": "^8.0.0",
|
|
21
|
+
"execa": "^7.1.1",
|
|
22
22
|
"snazzy": "^9.0.0",
|
|
23
|
-
"split2": "^4.
|
|
24
|
-
"standard": "^17.
|
|
25
|
-
"tsd": "^0.28.
|
|
23
|
+
"split2": "^4.2.0",
|
|
24
|
+
"standard": "^17.1.0",
|
|
25
|
+
"tsd": "^0.28.1"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@hapi/topo": "^6.0.2",
|
|
@@ -30,15 +30,18 @@
|
|
|
30
30
|
"commist": "^3.2.0",
|
|
31
31
|
"desm": "^1.3.0",
|
|
32
32
|
"es-main": "^1.2.0",
|
|
33
|
-
"fastify": "^4.
|
|
34
|
-
"fastify-undici-dispatcher": "^0.4.
|
|
33
|
+
"fastify": "^4.18.0",
|
|
34
|
+
"fastify-undici-dispatcher": "^0.4.1",
|
|
35
35
|
"help-me": "^4.2.0",
|
|
36
36
|
"minimist": "^1.2.8",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
37
|
+
"pino": "^8.14.1",
|
|
38
|
+
"pino-pretty": "^10.0.0",
|
|
39
|
+
"undici": "^5.22.1",
|
|
40
|
+
"@platformatic/composer": "0.28.1",
|
|
41
|
+
"@platformatic/config": "0.28.1",
|
|
42
|
+
"@platformatic/db": "0.28.1",
|
|
43
|
+
"@platformatic/service": "0.28.1",
|
|
44
|
+
"@platformatic/utils": "0.28.1"
|
|
42
45
|
},
|
|
43
46
|
"standard": {
|
|
44
47
|
"ignore": [
|
package/test/api.test.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert')
|
|
4
|
+
const { join } = require('node:path')
|
|
5
|
+
const { test } = require('node:test')
|
|
6
|
+
|
|
7
|
+
const { loadConfig } = require('@platformatic/service')
|
|
8
|
+
const { buildServer, platformaticRuntime } = require('..')
|
|
9
|
+
const fixturesDir = join(__dirname, '..', 'fixtures')
|
|
10
|
+
|
|
11
|
+
// Each test runtime app adds own process listeners
|
|
12
|
+
process.setMaxListeners(100)
|
|
13
|
+
|
|
14
|
+
test('should get service details', async (t) => {
|
|
15
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
16
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
17
|
+
const app = await buildServer(config.configManager.current)
|
|
18
|
+
|
|
19
|
+
await app.start()
|
|
20
|
+
|
|
21
|
+
t.after(async () => {
|
|
22
|
+
await app.close()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const serviceDetails = await app.getServiceDetails('with-logger')
|
|
26
|
+
assert.deepStrictEqual(serviceDetails, {
|
|
27
|
+
id: 'with-logger',
|
|
28
|
+
status: 'started',
|
|
29
|
+
entrypoint: false,
|
|
30
|
+
localUrl: 'http://with-logger.plt.local',
|
|
31
|
+
dependencies: []
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('should get service config', async (t) => {
|
|
36
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
37
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
38
|
+
const app = await buildServer(config.configManager.current)
|
|
39
|
+
|
|
40
|
+
await app.start()
|
|
41
|
+
|
|
42
|
+
t.after(async () => {
|
|
43
|
+
await app.close()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const serviceConfig = await app.getServiceConfig('with-logger')
|
|
47
|
+
|
|
48
|
+
// TODO: should return correct logger config
|
|
49
|
+
assert.deepStrictEqual(serviceConfig, {
|
|
50
|
+
$schema: 'https://platformatic.dev/schemas/v0.27.0/service',
|
|
51
|
+
server: {
|
|
52
|
+
hostname: '127.0.0.1',
|
|
53
|
+
port: 0,
|
|
54
|
+
logger: {},
|
|
55
|
+
keepAliveTimeout: 5000
|
|
56
|
+
},
|
|
57
|
+
service: { openapi: true },
|
|
58
|
+
plugins: {
|
|
59
|
+
paths: [
|
|
60
|
+
join(fixturesDir, 'monorepo', 'serviceAppWithLogger', 'plugin.js')
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
watch: false
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('should fail to get service config if service is not started', async (t) => {
|
|
68
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
69
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
70
|
+
const app = await buildServer(config.configManager.current)
|
|
71
|
+
|
|
72
|
+
t.after(async () => {
|
|
73
|
+
await app.close()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await app.getServiceConfig('with-logger')
|
|
78
|
+
assert.fail('should have thrown')
|
|
79
|
+
} catch (err) {
|
|
80
|
+
assert.strictEqual(err.message, 'Service with id \'with-logger\' is not started')
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('should get services topology', async (t) => {
|
|
85
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
86
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
87
|
+
const app = await buildServer(config.configManager.current)
|
|
88
|
+
|
|
89
|
+
await app.start()
|
|
90
|
+
|
|
91
|
+
t.after(async () => {
|
|
92
|
+
await app.close()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const topology = await app.getServices()
|
|
96
|
+
|
|
97
|
+
assert.deepStrictEqual(topology, {
|
|
98
|
+
entrypoint: 'serviceApp',
|
|
99
|
+
services: [
|
|
100
|
+
{
|
|
101
|
+
id: 'serviceApp',
|
|
102
|
+
status: 'started',
|
|
103
|
+
entrypoint: true,
|
|
104
|
+
localUrl: 'http://serviceApp.plt.local',
|
|
105
|
+
dependencies: [
|
|
106
|
+
{
|
|
107
|
+
id: 'with-logger',
|
|
108
|
+
url: 'http://with-logger.plt.local',
|
|
109
|
+
local: true
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'with-logger',
|
|
115
|
+
status: 'started',
|
|
116
|
+
entrypoint: false,
|
|
117
|
+
localUrl: 'http://with-logger.plt.local',
|
|
118
|
+
dependencies: []
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'multi-plugin-service',
|
|
122
|
+
status: 'started',
|
|
123
|
+
entrypoint: false,
|
|
124
|
+
localUrl: 'http://multi-plugin-service.plt.local',
|
|
125
|
+
dependencies: []
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('should stop service by service id', async (t) => {
|
|
132
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
133
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
134
|
+
const app = await buildServer(config.configManager.current)
|
|
135
|
+
|
|
136
|
+
await app.start()
|
|
137
|
+
|
|
138
|
+
t.after(async () => {
|
|
139
|
+
await app.close()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
{
|
|
143
|
+
const serviceDetails = await app.getServiceDetails('with-logger')
|
|
144
|
+
assert.strictEqual(serviceDetails.status, 'started')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await app.stopService('with-logger')
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
const serviceDetails = await app.getServiceDetails('with-logger')
|
|
151
|
+
assert.strictEqual(serviceDetails.status, 'stopped')
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('should fail to stop service with a wrong id', async (t) => {
|
|
156
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
157
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
158
|
+
const app = await buildServer(config.configManager.current)
|
|
159
|
+
|
|
160
|
+
t.after(async () => {
|
|
161
|
+
await app.close()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await app.stopService('wrong-service-id')
|
|
166
|
+
assert.fail('should have thrown')
|
|
167
|
+
} catch (err) {
|
|
168
|
+
assert.strictEqual(err.message, 'Service with id \'wrong-service-id\' not found')
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('should start stopped service by service id', async (t) => {
|
|
173
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
174
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
175
|
+
const app = await buildServer(config.configManager.current)
|
|
176
|
+
|
|
177
|
+
await app.start()
|
|
178
|
+
|
|
179
|
+
t.after(async () => {
|
|
180
|
+
await app.close()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
await app.stopService('with-logger')
|
|
184
|
+
|
|
185
|
+
{
|
|
186
|
+
const serviceDetails = await app.getServiceDetails('with-logger')
|
|
187
|
+
assert.strictEqual(serviceDetails.status, 'stopped')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
await app.startService('with-logger')
|
|
191
|
+
|
|
192
|
+
{
|
|
193
|
+
const serviceDetails = await app.getServiceDetails('with-logger')
|
|
194
|
+
assert.strictEqual(serviceDetails.status, 'started')
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('should fail to start service with a wrong id', async (t) => {
|
|
199
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
200
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
201
|
+
const app = await buildServer(config.configManager.current)
|
|
202
|
+
|
|
203
|
+
t.after(async () => {
|
|
204
|
+
await app.close()
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await app.startService('wrong-service-id')
|
|
209
|
+
assert.fail('should have thrown')
|
|
210
|
+
} catch (err) {
|
|
211
|
+
assert.strictEqual(err.message, 'Service with id \'wrong-service-id\' not found')
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('should fail to start running service', async (t) => {
|
|
216
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
217
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
218
|
+
const app = await buildServer(config.configManager.current)
|
|
219
|
+
|
|
220
|
+
await app.start()
|
|
221
|
+
|
|
222
|
+
t.after(async () => {
|
|
223
|
+
await app.close()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
await app.startService('with-logger')
|
|
228
|
+
assert.fail('should have thrown')
|
|
229
|
+
} catch (err) {
|
|
230
|
+
assert.strictEqual(err.message, 'application is already started')
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test('should inject request to service', async (t) => {
|
|
235
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
236
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
237
|
+
const app = await buildServer(config.configManager.current)
|
|
238
|
+
|
|
239
|
+
await app.start()
|
|
240
|
+
|
|
241
|
+
t.after(async () => {
|
|
242
|
+
await app.close()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
const res = await app.inject('with-logger', {
|
|
246
|
+
method: 'GET',
|
|
247
|
+
url: '/'
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
assert.strictEqual(res.statusCode, 200)
|
|
251
|
+
assert.strictEqual(res.statusMessage, 'OK')
|
|
252
|
+
|
|
253
|
+
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
|
|
254
|
+
assert.strictEqual(res.headers['content-length'], '17')
|
|
255
|
+
assert.strictEqual(res.headers.connection, 'keep-alive')
|
|
256
|
+
|
|
257
|
+
assert.strictEqual(res.body, '{"hello":"world"}')
|
|
258
|
+
assert.strictEqual(res.payload, '{"hello":"world"}')
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test('should fail inject request is service is not started', async (t) => {
|
|
262
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
263
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
264
|
+
const app = await buildServer(config.configManager.current)
|
|
265
|
+
|
|
266
|
+
t.after(async () => {
|
|
267
|
+
await app.close()
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
await app.inject('with-logger', { method: 'GET', url: '/' })
|
|
272
|
+
} catch (err) {
|
|
273
|
+
assert.strictEqual(err.message, 'Service with id \'with-logger\' is not started')
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
test('should handle a lot of runtime api requests', async (t) => {
|
|
278
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
279
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
280
|
+
const app = await buildServer(config.configManager.current)
|
|
281
|
+
|
|
282
|
+
await app.start()
|
|
283
|
+
|
|
284
|
+
t.after(async () => {
|
|
285
|
+
await app.close()
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
const promises = []
|
|
289
|
+
for (let i = 0; i < 100; i++) {
|
|
290
|
+
promises.push(app.getServiceDetails('with-logger'))
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await Promise.all(promises)
|
|
294
|
+
})
|
package/test/app.test.js
CHANGED
|
@@ -3,10 +3,20 @@
|
|
|
3
3
|
const assert = require('node:assert')
|
|
4
4
|
const { join } = require('node:path')
|
|
5
5
|
const { test } = require('node:test')
|
|
6
|
+
const { utimes } = require('node:fs/promises')
|
|
6
7
|
const { PlatformaticApp } = require('../lib/app')
|
|
7
8
|
const fixturesDir = join(__dirname, '..', 'fixtures')
|
|
9
|
+
const pino = require('pino')
|
|
10
|
+
const split = require('split2')
|
|
11
|
+
|
|
12
|
+
function getLoggerAndStream () {
|
|
13
|
+
const stream = split(JSON.parse)
|
|
14
|
+
const logger = pino(stream)
|
|
15
|
+
return { logger, stream }
|
|
16
|
+
}
|
|
8
17
|
|
|
9
18
|
test('logs errors during startup', async (t) => {
|
|
19
|
+
const { logger, stream } = getLoggerAndStream()
|
|
10
20
|
const appPath = join(fixturesDir, 'serviceAppThrowsOnStart')
|
|
11
21
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
12
22
|
const config = {
|
|
@@ -16,9 +26,8 @@ test('logs errors during startup', async (t) => {
|
|
|
16
26
|
entrypoint: true,
|
|
17
27
|
hotReload: true
|
|
18
28
|
}
|
|
19
|
-
const app = new PlatformaticApp(config, null)
|
|
29
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
20
30
|
|
|
21
|
-
t.mock.method(console, 'error', () => {})
|
|
22
31
|
t.mock.method(process, 'exit', () => { throw new Error('exited') })
|
|
23
32
|
|
|
24
33
|
await assert.rejects(async () => {
|
|
@@ -26,11 +35,18 @@ test('logs errors during startup', async (t) => {
|
|
|
26
35
|
}, /exited/)
|
|
27
36
|
assert.strictEqual(process.exit.mock.calls.length, 1)
|
|
28
37
|
assert.strictEqual(process.exit.mock.calls[0].arguments[0], 1)
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
|
|
39
|
+
stream.end()
|
|
40
|
+
const lines = []
|
|
41
|
+
for await (const line of stream) {
|
|
42
|
+
lines.push(line)
|
|
43
|
+
}
|
|
44
|
+
const lastLine = lines[lines.length - 1]
|
|
45
|
+
assert.strictEqual(lastLine.msg, 'boom')
|
|
31
46
|
})
|
|
32
47
|
|
|
33
48
|
test('errors when starting an already started application', async (t) => {
|
|
49
|
+
const { logger } = getLoggerAndStream()
|
|
34
50
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
35
51
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
36
52
|
const config = {
|
|
@@ -43,7 +59,7 @@ test('errors when starting an already started application', async (t) => {
|
|
|
43
59
|
dependents: [],
|
|
44
60
|
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
45
61
|
}
|
|
46
|
-
const app = new PlatformaticApp(config, null)
|
|
62
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
47
63
|
|
|
48
64
|
t.after(app.stop.bind(app))
|
|
49
65
|
await app.start()
|
|
@@ -53,6 +69,7 @@ test('errors when starting an already started application', async (t) => {
|
|
|
53
69
|
})
|
|
54
70
|
|
|
55
71
|
test('errors when stopping an already stopped application', async (t) => {
|
|
72
|
+
const { logger } = getLoggerAndStream()
|
|
56
73
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
57
74
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
58
75
|
const config = {
|
|
@@ -65,7 +82,7 @@ test('errors when stopping an already stopped application', async (t) => {
|
|
|
65
82
|
dependents: [],
|
|
66
83
|
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
67
84
|
}
|
|
68
|
-
const app = new PlatformaticApp(config, null)
|
|
85
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
69
86
|
|
|
70
87
|
await assert.rejects(async () => {
|
|
71
88
|
await app.stop()
|
|
@@ -73,6 +90,7 @@ test('errors when stopping an already stopped application', async (t) => {
|
|
|
73
90
|
})
|
|
74
91
|
|
|
75
92
|
test('does not restart while restarting', async (t) => {
|
|
93
|
+
const { logger } = getLoggerAndStream()
|
|
76
94
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
77
95
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
78
96
|
const config = {
|
|
@@ -85,7 +103,7 @@ test('does not restart while restarting', async (t) => {
|
|
|
85
103
|
dependents: [],
|
|
86
104
|
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
87
105
|
}
|
|
88
|
-
const app = new PlatformaticApp(config, null)
|
|
106
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
89
107
|
|
|
90
108
|
t.after(app.stop.bind(app))
|
|
91
109
|
await app.start()
|
|
@@ -101,6 +119,7 @@ test('does not restart while restarting', async (t) => {
|
|
|
101
119
|
})
|
|
102
120
|
|
|
103
121
|
test('restarts on SIGUSR2', async (t) => {
|
|
122
|
+
const { logger } = getLoggerAndStream()
|
|
104
123
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
105
124
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
106
125
|
const config = {
|
|
@@ -113,7 +132,7 @@ test('restarts on SIGUSR2', async (t) => {
|
|
|
113
132
|
dependents: [],
|
|
114
133
|
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
115
134
|
}
|
|
116
|
-
const app = new PlatformaticApp(config, null)
|
|
135
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
117
136
|
|
|
118
137
|
t.after(app.stop.bind(app))
|
|
119
138
|
await app.start()
|
|
@@ -123,6 +142,7 @@ test('restarts on SIGUSR2', async (t) => {
|
|
|
123
142
|
})
|
|
124
143
|
|
|
125
144
|
test('stops on signals other than SIGUSR2', async (t) => {
|
|
145
|
+
const { logger } = getLoggerAndStream()
|
|
126
146
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
127
147
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
128
148
|
const config = {
|
|
@@ -135,7 +155,7 @@ test('stops on signals other than SIGUSR2', async (t) => {
|
|
|
135
155
|
dependents: [],
|
|
136
156
|
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
137
157
|
}
|
|
138
|
-
const app = new PlatformaticApp(config, null)
|
|
158
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
139
159
|
|
|
140
160
|
t.after(async () => {
|
|
141
161
|
try {
|
|
@@ -151,6 +171,7 @@ test('stops on signals other than SIGUSR2', async (t) => {
|
|
|
151
171
|
})
|
|
152
172
|
|
|
153
173
|
test('stops on uncaught exceptions', async (t) => {
|
|
174
|
+
const { logger } = getLoggerAndStream()
|
|
154
175
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
155
176
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
156
177
|
const config = {
|
|
@@ -163,7 +184,7 @@ test('stops on uncaught exceptions', async (t) => {
|
|
|
163
184
|
dependents: [],
|
|
164
185
|
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
165
186
|
}
|
|
166
|
-
const app = new PlatformaticApp(config, null)
|
|
187
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
167
188
|
|
|
168
189
|
t.after(async () => {
|
|
169
190
|
try {
|
|
@@ -193,8 +214,9 @@ test('supports configuration overrides', async (t) => {
|
|
|
193
214
|
}
|
|
194
215
|
|
|
195
216
|
await t.test('throws on non-string config paths', async (t) => {
|
|
217
|
+
const { logger } = getLoggerAndStream()
|
|
196
218
|
config._configOverrides = new Map([[null, 5]])
|
|
197
|
-
const app = new PlatformaticApp(config, null)
|
|
219
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
198
220
|
|
|
199
221
|
t.after(async () => {
|
|
200
222
|
try {
|
|
@@ -210,8 +232,9 @@ test('supports configuration overrides', async (t) => {
|
|
|
210
232
|
})
|
|
211
233
|
|
|
212
234
|
await t.test('ignores invalid config paths', async (t) => {
|
|
235
|
+
const { logger } = getLoggerAndStream()
|
|
213
236
|
config._configOverrides = new Map([['foo.bar.baz', 5]])
|
|
214
|
-
const app = new PlatformaticApp(config, null)
|
|
237
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
215
238
|
|
|
216
239
|
t.after(async () => {
|
|
217
240
|
try {
|
|
@@ -225,11 +248,12 @@ test('supports configuration overrides', async (t) => {
|
|
|
225
248
|
})
|
|
226
249
|
|
|
227
250
|
await t.test('sets valid config paths', async (t) => {
|
|
251
|
+
const { logger } = getLoggerAndStream()
|
|
228
252
|
config._configOverrides = new Map([
|
|
229
253
|
['server.keepAliveTimeout', 1],
|
|
230
254
|
['server.port', 0]
|
|
231
255
|
])
|
|
232
|
-
const app = new PlatformaticApp(config, null)
|
|
256
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
233
257
|
|
|
234
258
|
t.after(async () => {
|
|
235
259
|
try {
|
|
@@ -243,3 +267,42 @@ test('supports configuration overrides', async (t) => {
|
|
|
243
267
|
assert.strictEqual(app.config.configManager.current.server.keepAliveTimeout, 1)
|
|
244
268
|
})
|
|
245
269
|
})
|
|
270
|
+
|
|
271
|
+
test('restarts on config change without overriding the configManager', { only: true }, async (t) => {
|
|
272
|
+
const { logger, stream } = getLoggerAndStream()
|
|
273
|
+
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
274
|
+
const configFile = join(appPath, 'platformatic.service.json')
|
|
275
|
+
const config = {
|
|
276
|
+
id: 'serviceApp',
|
|
277
|
+
config: configFile,
|
|
278
|
+
path: appPath,
|
|
279
|
+
entrypoint: true,
|
|
280
|
+
hotReload: true,
|
|
281
|
+
dependencies: [],
|
|
282
|
+
dependents: [],
|
|
283
|
+
localServiceEnvVars: new Map([['PLT_WITH_LOGGER_URL', ' ']])
|
|
284
|
+
}
|
|
285
|
+
const app = new PlatformaticApp(config, null, logger)
|
|
286
|
+
|
|
287
|
+
t.after(async function () {
|
|
288
|
+
try {
|
|
289
|
+
await app.stop()
|
|
290
|
+
} catch (err) {
|
|
291
|
+
console.error(err)
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
await app.start()
|
|
295
|
+
const configManager = app.config.configManager
|
|
296
|
+
await utimes(configFile, new Date(), new Date())
|
|
297
|
+
let first = false
|
|
298
|
+
for await (const log of stream) {
|
|
299
|
+
// Wait for the server to restart, it will print a line containing "Server listening"
|
|
300
|
+
if (log.msg.includes('listening')) {
|
|
301
|
+
if (first) {
|
|
302
|
+
break
|
|
303
|
+
}
|
|
304
|
+
first = true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
assert.strictEqual(configManager, app.server.platformatic.configManager)
|
|
308
|
+
})
|