@platformatic/watt-extra 0.1.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/README.md +87 -0
- package/app.js +124 -0
- package/cli.js +141 -0
- package/clients/compliance/compliance-types.d.ts +887 -0
- package/clients/compliance/compliance.mjs +1049 -0
- package/clients/compliance/compliance.openapi.json +6127 -0
- package/clients/control-plane/control-plane-types.d.ts +2696 -0
- package/clients/control-plane/control-plane.mjs +3051 -0
- package/clients/control-plane/control-plane.openapi.json +13693 -0
- package/clients/cron/cron-types.d.ts +1479 -0
- package/clients/cron/cron.mjs +872 -0
- package/clients/cron/cron.openapi.json +9330 -0
- package/compliance/index.js +21 -0
- package/compliance/rules/dependencies.js +76 -0
- package/compliance/rules/utils.js +12 -0
- package/eslint.config.js +11 -0
- package/help/start.txt +12 -0
- package/help/watt-extra.txt +12 -0
- package/index.js +45 -0
- package/lib/banner.js +22 -0
- package/lib/errors.js +34 -0
- package/lib/utils.js +34 -0
- package/lib/wattpro.js +580 -0
- package/package.json +50 -0
- package/plugins/alerts.js +115 -0
- package/plugins/auth.js +89 -0
- package/plugins/compliancy.js +70 -0
- package/plugins/env.js +58 -0
- package/plugins/flamegraphs.js +100 -0
- package/plugins/init.js +70 -0
- package/plugins/metadata.js +84 -0
- package/plugins/scheduler.js +48 -0
- package/plugins/update.js +128 -0
- package/renovate.json +6 -0
- package/test/alerts.test.js +607 -0
- package/test/auth.test.js +128 -0
- package/test/auto-cache.test.js +401 -0
- package/test/cli.test.js +75 -0
- package/test/compliancy.test.js +87 -0
- package/test/fixtures/runtime-domains/alpha/package.json +5 -0
- package/test/fixtures/runtime-domains/alpha/platformatic.json +6 -0
- package/test/fixtures/runtime-domains/alpha/plugin.js +16 -0
- package/test/fixtures/runtime-domains/beta/package.json +5 -0
- package/test/fixtures/runtime-domains/beta/platformatic.json +6 -0
- package/test/fixtures/runtime-domains/beta/plugin.js +7 -0
- package/test/fixtures/runtime-domains/composer/package.json +5 -0
- package/test/fixtures/runtime-domains/composer/platformatic.json +19 -0
- package/test/fixtures/runtime-domains/package.json +1 -0
- package/test/fixtures/runtime-domains/platformatic.json +27 -0
- package/test/fixtures/runtime-health/package.json +20 -0
- package/test/fixtures/runtime-health/platformatic.json +16 -0
- package/test/fixtures/runtime-health/services/service-1/package.json +17 -0
- package/test/fixtures/runtime-health/services/service-1/platformatic.json +16 -0
- package/test/fixtures/runtime-health/services/service-1/plugins/example.js +6 -0
- package/test/fixtures/runtime-health/services/service-1/routes/root.cjs +8 -0
- package/test/fixtures/runtime-health/services/service-2/package.json +17 -0
- package/test/fixtures/runtime-health/services/service-2/platformatic.json +16 -0
- package/test/fixtures/runtime-health/services/service-2/plugins/example.js +6 -0
- package/test/fixtures/runtime-health/services/service-2/routes/root.cjs +8 -0
- package/test/fixtures/runtime-next/package.json +5 -0
- package/test/fixtures/runtime-next/platformatic.json +9 -0
- package/test/fixtures/runtime-next/web/next/next.config.js +2 -0
- package/test/fixtures/runtime-next/web/next/package.json +7 -0
- package/test/fixtures/runtime-next/web/next/platformatic.json +9 -0
- package/test/fixtures/runtime-next/web/next/src/app/direct/route.js +3 -0
- package/test/fixtures/runtime-next/web/next/src/app/layout.jsx +7 -0
- package/test/fixtures/runtime-next/web/next/src/app/page.jsx +3 -0
- package/test/fixtures/runtime-scheduler/main/package.json +5 -0
- package/test/fixtures/runtime-scheduler/main/platformatic.json +9 -0
- package/test/fixtures/runtime-scheduler/main/routes/root.cjs +11 -0
- package/test/fixtures/runtime-scheduler/package.json +1 -0
- package/test/fixtures/runtime-scheduler/platformatic.json +27 -0
- package/test/fixtures/runtime-service/main/package.json +5 -0
- package/test/fixtures/runtime-service/main/platformatic.json +12 -0
- package/test/fixtures/runtime-service/main/routes/root.cjs +11 -0
- package/test/fixtures/runtime-service/package.json +1 -0
- package/test/fixtures/runtime-service/platformatic.json +19 -0
- package/test/fixtures/service-1/package.json +7 -0
- package/test/fixtures/service-1/platformatic.json +18 -0
- package/test/fixtures/service-1/routes/root.cjs +48 -0
- package/test/fixtures/service-2/platformatic.json +21 -0
- package/test/fixtures/service-2/routes/root.cjs +5 -0
- package/test/fixtures/service-3/package.json +5 -0
- package/test/fixtures/service-3/platformatic.json +21 -0
- package/test/fixtures/service-3/routes/root.cjs +8 -0
- package/test/health.test.js +44 -0
- package/test/helper.js +274 -0
- package/test/init.test.js +243 -0
- package/test/patch-config.test.js +434 -0
- package/test/scheduler.test.js +71 -0
- package/test/send-to-icc-retry.test.js +138 -0
- package/test/shared-context.test.js +82 -0
- package/test/spawn.test.js +110 -0
- package/test/trigger-flamegraphs.test.js +226 -0
- package/test/update.test.js +519 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { join, dirname } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
import { randomUUID } from 'node:crypto'
|
|
7
|
+
import { request } from 'undici'
|
|
8
|
+
import { setUpEnvironment, startICC, installDeps } from './helper.js'
|
|
9
|
+
import { start } from '../index.js'
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = dirname(__filename)
|
|
13
|
+
|
|
14
|
+
test('should spawn a service app settings labels for metrics', async (t) => {
|
|
15
|
+
const applicationName = 'test-application'
|
|
16
|
+
const applicationId = randomUUID()
|
|
17
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
18
|
+
|
|
19
|
+
const icc = await startICC(t, {
|
|
20
|
+
applicationId,
|
|
21
|
+
applicationName,
|
|
22
|
+
enableOpenTelemetry: true,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
setUpEnvironment({
|
|
26
|
+
PLT_APP_NAME: applicationName,
|
|
27
|
+
PLT_APP_DIR: applicationPath,
|
|
28
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const app = await start()
|
|
32
|
+
|
|
33
|
+
t.after(async () => {
|
|
34
|
+
await app.close()
|
|
35
|
+
await icc.close()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const mainConfig = app.wattpro.runtime.getRuntimeConfig(true)
|
|
39
|
+
|
|
40
|
+
const { metrics, telemetry } = mainConfig
|
|
41
|
+
|
|
42
|
+
const expectedTelemetry = {
|
|
43
|
+
enabled: true,
|
|
44
|
+
applicationName: 'test-application',
|
|
45
|
+
skip: [
|
|
46
|
+
{
|
|
47
|
+
method: 'GET',
|
|
48
|
+
path: '/documentation',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
method: 'GET',
|
|
52
|
+
path: '/documentation/json',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
exporter: {
|
|
56
|
+
type: 'otlp',
|
|
57
|
+
options: {
|
|
58
|
+
url: 'http://127.0.0.1:3000/risk-service/v1/traces',
|
|
59
|
+
headers: {
|
|
60
|
+
'x-platformatic-application-id': applicationId,
|
|
61
|
+
},
|
|
62
|
+
keepAlive: true,
|
|
63
|
+
httpAgentOptions: {
|
|
64
|
+
rejectUnauthorized: false,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
assert.deepStrictEqual(telemetry, expectedTelemetry)
|
|
70
|
+
|
|
71
|
+
const expectedMetrics = {
|
|
72
|
+
server: 'hide',
|
|
73
|
+
defaultMetrics: {
|
|
74
|
+
enabled: true,
|
|
75
|
+
},
|
|
76
|
+
hostname: '127.0.0.1',
|
|
77
|
+
port: 9090,
|
|
78
|
+
labels: {
|
|
79
|
+
serviceId: 'main',
|
|
80
|
+
instanceId: app.instanceId,
|
|
81
|
+
applicationId,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
assert.deepStrictEqual(metrics, expectedMetrics)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('should configure system resources', async (t) => {
|
|
88
|
+
const applicationName = 'test-next'
|
|
89
|
+
const applicationId = randomUUID()
|
|
90
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-next')
|
|
91
|
+
|
|
92
|
+
await installDeps(t, applicationPath, ['@platformatic/next', 'next'])
|
|
93
|
+
const { execa } = await import('execa')
|
|
94
|
+
await execa(join(__dirname, '../node_modules/.bin/plt'), ['build'], {
|
|
95
|
+
cwd: applicationPath,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const iccConfig = {
|
|
99
|
+
resources: {
|
|
100
|
+
threads: 1,
|
|
101
|
+
heap: 256,
|
|
102
|
+
services: [
|
|
103
|
+
{
|
|
104
|
+
name: 'next',
|
|
105
|
+
threads: 3,
|
|
106
|
+
heap: 200,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
const icc = await startICC(t, { applicationId, applicationName, iccConfig })
|
|
112
|
+
|
|
113
|
+
setUpEnvironment({
|
|
114
|
+
PLT_APP_NAME: applicationName,
|
|
115
|
+
PLT_APP_DIR: applicationPath,
|
|
116
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const app = await start()
|
|
120
|
+
|
|
121
|
+
t.after(async () => {
|
|
122
|
+
await app.close()
|
|
123
|
+
await icc.close()
|
|
124
|
+
})
|
|
125
|
+
const config = await app.wattpro.runtime.getRuntimeConfig(true)
|
|
126
|
+
|
|
127
|
+
// Check generic resources
|
|
128
|
+
assert.strictEqual(config.workers, 1)
|
|
129
|
+
assert.strictEqual(config.health.maxHeapTotal, 256 * Math.pow(1024, 2))
|
|
130
|
+
|
|
131
|
+
// Check system resources
|
|
132
|
+
assert.strictEqual(config.applications[0].workers, 3)
|
|
133
|
+
assert.strictEqual(
|
|
134
|
+
config.applications[0].health.maxHeapTotal,
|
|
135
|
+
200 * Math.pow(1024, 2)
|
|
136
|
+
)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('should remove server https configs', async (t) => {
|
|
140
|
+
const appName = 'test-app'
|
|
141
|
+
const applicationId = randomUUID()
|
|
142
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-service')
|
|
143
|
+
|
|
144
|
+
await installDeps(t, applicationPath)
|
|
145
|
+
|
|
146
|
+
const icc = await startICC(t, {
|
|
147
|
+
applicationId,
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
setUpEnvironment({
|
|
151
|
+
PLT_APP_NAME: appName,
|
|
152
|
+
PLT_APP_DIR: applicationPath,
|
|
153
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
154
|
+
PLT_CONTROL_PLANE_URL: 'http://127.0.0.1:3002',
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const app = await start()
|
|
158
|
+
|
|
159
|
+
t.after(async () => {
|
|
160
|
+
await app.close()
|
|
161
|
+
await icc.close()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
const mainConfig = app.wattpro.runtime.getRuntimeConfig(true)
|
|
166
|
+
const { server } = mainConfig
|
|
167
|
+
assert.strictEqual(server.https, undefined)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
{
|
|
171
|
+
const runtimeConfig = await app.wattpro.runtime.getRuntimeConfig(true)
|
|
172
|
+
|
|
173
|
+
const { server } = runtimeConfig
|
|
174
|
+
assert.strictEqual(server.https, undefined)
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('should configure health options', async (t) => {
|
|
179
|
+
const appName = 'test-app'
|
|
180
|
+
const applicationId = randomUUID()
|
|
181
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-service')
|
|
182
|
+
|
|
183
|
+
await installDeps(t, applicationPath)
|
|
184
|
+
|
|
185
|
+
const icc = await startICC(t, {
|
|
186
|
+
applicationId,
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
setUpEnvironment({
|
|
190
|
+
PLT_APP_NAME: appName,
|
|
191
|
+
PLT_APP_DIR: applicationPath,
|
|
192
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const app = await start()
|
|
196
|
+
|
|
197
|
+
t.after(async () => {
|
|
198
|
+
await app.close()
|
|
199
|
+
await icc.close()
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const runtimeConfig = await app.wattpro.runtime.getRuntimeConfig(true)
|
|
203
|
+
|
|
204
|
+
const { health } = runtimeConfig
|
|
205
|
+
assert.strictEqual(health.enabled, true)
|
|
206
|
+
assert.strictEqual(health.interval, 1000)
|
|
207
|
+
assert.strictEqual(health.maxUnhealthyChecks, 30)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test('should call updateServicesResources with maxHeapTotal', async (t) => {
|
|
211
|
+
const appName = 'test-update-resources'
|
|
212
|
+
const applicationId = randomUUID()
|
|
213
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-service')
|
|
214
|
+
|
|
215
|
+
await installDeps(t, applicationPath)
|
|
216
|
+
|
|
217
|
+
const icc = await startICC(t, {
|
|
218
|
+
applicationId,
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
setUpEnvironment({
|
|
222
|
+
PLT_APP_NAME: appName,
|
|
223
|
+
PLT_APP_DIR: applicationPath,
|
|
224
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
const app = await start()
|
|
228
|
+
|
|
229
|
+
t.after(async () => {
|
|
230
|
+
await app.close()
|
|
231
|
+
await icc.close()
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const updateCalls = []
|
|
235
|
+
const originalUpdateServicesResources =
|
|
236
|
+
app.wattpro.runtime.updateServicesResources
|
|
237
|
+
app.wattpro.runtime.updateServicesResources = async (resourceUpdates) => {
|
|
238
|
+
updateCalls.push(resourceUpdates)
|
|
239
|
+
if (originalUpdateServicesResources) {
|
|
240
|
+
return originalUpdateServicesResources.call(
|
|
241
|
+
app.wattpro.runtime,
|
|
242
|
+
resourceUpdates
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const config = {
|
|
248
|
+
resources: {
|
|
249
|
+
services: [
|
|
250
|
+
{ name: 'main', threads: 2, heap: 512 },
|
|
251
|
+
{ name: 'service-1', threads: 1, heap: 256 },
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await app.wattpro.applyIccConfigUpdates(config)
|
|
257
|
+
|
|
258
|
+
assert.strictEqual(
|
|
259
|
+
updateCalls.length,
|
|
260
|
+
1,
|
|
261
|
+
'updateServicesResources should be called once'
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
const resourceUpdates = updateCalls[0]
|
|
265
|
+
assert.strictEqual(
|
|
266
|
+
resourceUpdates.length,
|
|
267
|
+
2,
|
|
268
|
+
'Should have updates for 2 services'
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
assert.strictEqual(resourceUpdates[0].service, 'main')
|
|
272
|
+
assert.strictEqual(resourceUpdates[0].workers, 2)
|
|
273
|
+
assert.strictEqual(resourceUpdates[0].health.maxHeapTotal, '512MB')
|
|
274
|
+
|
|
275
|
+
assert.strictEqual(resourceUpdates[1].service, 'service-1')
|
|
276
|
+
assert.strictEqual(resourceUpdates[1].workers, 1)
|
|
277
|
+
assert.strictEqual(resourceUpdates[1].health.maxHeapTotal, '256MB')
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test('should handle updateServicesResources with different heap sizes', async (t) => {
|
|
281
|
+
const appName = 'test-heap-sizes'
|
|
282
|
+
const applicationId = randomUUID()
|
|
283
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-service')
|
|
284
|
+
|
|
285
|
+
await installDeps(t, applicationPath)
|
|
286
|
+
|
|
287
|
+
const icc = await startICC(t, {
|
|
288
|
+
applicationId,
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
setUpEnvironment({
|
|
292
|
+
PLT_APP_NAME: appName,
|
|
293
|
+
PLT_APP_DIR: applicationPath,
|
|
294
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
const app = await start()
|
|
298
|
+
|
|
299
|
+
t.after(async () => {
|
|
300
|
+
await app.close()
|
|
301
|
+
await icc.close()
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
const updateCalls = []
|
|
305
|
+
app.wattpro.runtime.updateServicesResources = async (resourceUpdates) => {
|
|
306
|
+
updateCalls.push(resourceUpdates)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const configs = [
|
|
310
|
+
{
|
|
311
|
+
resources: {
|
|
312
|
+
services: [
|
|
313
|
+
{ name: 'small-service', threads: 1, heap: 128 },
|
|
314
|
+
{ name: 'large-service', threads: 4, heap: 2048 },
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
for (const config of configs) {
|
|
321
|
+
await app.wattpro.applyIccConfigUpdates(config)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
assert.strictEqual(updateCalls.length, 1)
|
|
325
|
+
|
|
326
|
+
const resourceUpdates = updateCalls[0]
|
|
327
|
+
assert.strictEqual(resourceUpdates[0].health.maxHeapTotal, '128MB')
|
|
328
|
+
assert.strictEqual(resourceUpdates[1].health.maxHeapTotal, '2048MB')
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
test('should handle updateServicesResources error gracefully', async (t) => {
|
|
332
|
+
const appName = 'test-update-error'
|
|
333
|
+
const applicationId = randomUUID()
|
|
334
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-service')
|
|
335
|
+
|
|
336
|
+
await installDeps(t, applicationPath)
|
|
337
|
+
|
|
338
|
+
const icc = await startICC(t, {
|
|
339
|
+
applicationId,
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
setUpEnvironment({
|
|
343
|
+
PLT_APP_NAME: appName,
|
|
344
|
+
PLT_APP_DIR: applicationPath,
|
|
345
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
const app = await start()
|
|
349
|
+
|
|
350
|
+
t.after(async () => {
|
|
351
|
+
await app.close()
|
|
352
|
+
await icc.close()
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
let errorThrown = false
|
|
356
|
+
app.wattpro.runtime.updateServicesResources = async (resourceUpdates) => {
|
|
357
|
+
assert.strictEqual(resourceUpdates[0].health.maxHeapTotal, '256MB')
|
|
358
|
+
errorThrown = true
|
|
359
|
+
throw new Error('Mock update error')
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const config = {
|
|
363
|
+
resources: {
|
|
364
|
+
services: [{ name: 'test-service', threads: 1, heap: 256 }],
|
|
365
|
+
},
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
await app.wattpro.applyIccConfigUpdates(config)
|
|
369
|
+
|
|
370
|
+
assert.strictEqual(
|
|
371
|
+
errorThrown,
|
|
372
|
+
true,
|
|
373
|
+
'updateServicesResources should have been called and thrown an error'
|
|
374
|
+
)
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
test('should not set opentelemetry if it is disabled', async (t) => {
|
|
378
|
+
const applicationName = 'test-application'
|
|
379
|
+
const applicationId = randomUUID()
|
|
380
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
381
|
+
|
|
382
|
+
const icc = await startICC(t, {
|
|
383
|
+
applicationId,
|
|
384
|
+
applicationName,
|
|
385
|
+
enableOpenTelemetry: false,
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
setUpEnvironment({
|
|
389
|
+
PLT_APP_NAME: applicationName,
|
|
390
|
+
PLT_APP_DIR: applicationPath,
|
|
391
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
const app = await start()
|
|
395
|
+
|
|
396
|
+
t.after(async () => {
|
|
397
|
+
await app.close()
|
|
398
|
+
await icc.close()
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
// main config
|
|
402
|
+
const { statusCode, body } = await request('http://127.0.0.1:3042/config')
|
|
403
|
+
assert.strictEqual(statusCode, 200)
|
|
404
|
+
|
|
405
|
+
const expectedTelemetry = {
|
|
406
|
+
enabled: false,
|
|
407
|
+
applicationName: 'test-application-main',
|
|
408
|
+
skip: [
|
|
409
|
+
{
|
|
410
|
+
method: 'GET',
|
|
411
|
+
path: '/documentation',
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
method: 'GET',
|
|
415
|
+
path: '/documentation/json',
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
exporter: {
|
|
419
|
+
type: 'otlp',
|
|
420
|
+
options: {
|
|
421
|
+
url: 'http://127.0.0.1:3000/risk-service/v1/traces',
|
|
422
|
+
headers: {
|
|
423
|
+
'x-platformatic-application-id': applicationId,
|
|
424
|
+
},
|
|
425
|
+
keepAlive: true,
|
|
426
|
+
httpAgentOptions: {
|
|
427
|
+
rejectUnauthorized: false,
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
}
|
|
432
|
+
const mainConfig = await body.json()
|
|
433
|
+
assert.deepStrictEqual(mainConfig.telemetry, expectedTelemetry)
|
|
434
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { join, dirname } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
import { randomUUID } from 'node:crypto'
|
|
7
|
+
import { start } from '../index.js'
|
|
8
|
+
import {
|
|
9
|
+
setUpEnvironment,
|
|
10
|
+
startICC,
|
|
11
|
+
installDeps
|
|
12
|
+
} from './helper.js'
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
15
|
+
const __dirname = dirname(__filename)
|
|
16
|
+
|
|
17
|
+
test('should spawn a runtime disabling all the scheduler jobs', async (t) => {
|
|
18
|
+
const applicationName = 'test-app'
|
|
19
|
+
const applicationId = randomUUID()
|
|
20
|
+
const applicationPath = join(__dirname, 'fixtures', 'runtime-scheduler')
|
|
21
|
+
|
|
22
|
+
await installDeps(t, applicationPath)
|
|
23
|
+
|
|
24
|
+
let savedWattJob = null
|
|
25
|
+
const icc = await startICC(t, {
|
|
26
|
+
applicationId,
|
|
27
|
+
applicationName,
|
|
28
|
+
saveWattJob: (job) => {
|
|
29
|
+
savedWattJob = job
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
setUpEnvironment({
|
|
34
|
+
PLT_APP_NAME: applicationName,
|
|
35
|
+
PLT_APP_DIR: applicationPath,
|
|
36
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000'
|
|
37
|
+
})
|
|
38
|
+
const app = await start()
|
|
39
|
+
|
|
40
|
+
t.after(async () => {
|
|
41
|
+
await app.close()
|
|
42
|
+
await icc.close()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const config = await app.wattpro.runtime.getRuntimeConfig()
|
|
46
|
+
|
|
47
|
+
const { scheduler } = config
|
|
48
|
+
|
|
49
|
+
// The scheduler should be disabled
|
|
50
|
+
const expectedSchedulerConfig = [{
|
|
51
|
+
enabled: false,
|
|
52
|
+
name: 'test',
|
|
53
|
+
callbackUrl: 'http://localhost:3000',
|
|
54
|
+
cron: '*/5 * * * *',
|
|
55
|
+
method: 'GET',
|
|
56
|
+
maxRetries: 3
|
|
57
|
+
}]
|
|
58
|
+
|
|
59
|
+
assert.deepStrictEqual(scheduler, expectedSchedulerConfig)
|
|
60
|
+
|
|
61
|
+
// ICC is called to save the job
|
|
62
|
+
const expectedWattJob = {
|
|
63
|
+
name: 'test',
|
|
64
|
+
callbackUrl: 'http://localhost:3000',
|
|
65
|
+
schedule: '*/5 * * * *',
|
|
66
|
+
method: 'GET',
|
|
67
|
+
maxRetries: 3,
|
|
68
|
+
applicationId
|
|
69
|
+
}
|
|
70
|
+
assert.deepEqual(savedWattJob, expectedWattJob)
|
|
71
|
+
})
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { join, dirname } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
import buildApp from '../app.js'
|
|
7
|
+
import pino from 'pino'
|
|
8
|
+
import {
|
|
9
|
+
setUpEnvironment
|
|
10
|
+
} from './helper.js'
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
13
|
+
const __dirname = dirname(__filename)
|
|
14
|
+
|
|
15
|
+
// Create a null logger for tests
|
|
16
|
+
const logger = pino({
|
|
17
|
+
level: 'silent'
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// Helper to close resources after each test
|
|
21
|
+
const closeResources = async (app) => {
|
|
22
|
+
if (app && typeof app.close === 'function') {
|
|
23
|
+
try {
|
|
24
|
+
await app.close()
|
|
25
|
+
} catch (err) {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test('should retry sending info to ICC with exponential backoff', async (t) => {
|
|
30
|
+
const applicationName = 'test-app'
|
|
31
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
32
|
+
const retryInterval = 50 // Small interval for test
|
|
33
|
+
|
|
34
|
+
setUpEnvironment({
|
|
35
|
+
PLT_APP_NAME: applicationName,
|
|
36
|
+
PLT_APP_DIR: applicationPath,
|
|
37
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
38
|
+
PLT_ICC_RETRY_TIME: retryInterval
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const app = await buildApp(logger)
|
|
42
|
+
t.after(async () => closeResources(app))
|
|
43
|
+
|
|
44
|
+
// Mock failure for the first 2 calls, then succeed
|
|
45
|
+
let callCount = 0
|
|
46
|
+
const retryTimes = []
|
|
47
|
+
let lastCallTime = Date.now()
|
|
48
|
+
|
|
49
|
+
app.sendToICC = () => {
|
|
50
|
+
const now = Date.now()
|
|
51
|
+
if (callCount > 0) {
|
|
52
|
+
// Calculate actual delay between calls
|
|
53
|
+
retryTimes.push(now - lastCallTime)
|
|
54
|
+
}
|
|
55
|
+
lastCallTime = now
|
|
56
|
+
|
|
57
|
+
callCount++
|
|
58
|
+
if (callCount <= 2) {
|
|
59
|
+
throw new Error('Mock ICC connection failure')
|
|
60
|
+
}
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await app.sendToICCWithRetry()
|
|
65
|
+
assert.strictEqual(callCount, 3, 'ICC should be called 3 times (1 initial + 2 retries)')
|
|
66
|
+
assert.ok(retryTimes[0] >= retryInterval, 'First retry should wait at least the base retry interval')
|
|
67
|
+
assert.ok(retryTimes[1] > retryTimes[0], 'Second retry should have longer delay due to exponential backoff')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('should continue retrying until success within max attempts', async (t) => {
|
|
71
|
+
const applicationName = 'test-app'
|
|
72
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
73
|
+
const retryInterval = 10 // Very small interval for fast test
|
|
74
|
+
|
|
75
|
+
setUpEnvironment({
|
|
76
|
+
PLT_APP_NAME: applicationName,
|
|
77
|
+
PLT_APP_DIR: applicationPath,
|
|
78
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
79
|
+
PLT_ICC_RETRY_TIME: retryInterval
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const app = await buildApp(logger)
|
|
83
|
+
t.after(async () => closeResources(app))
|
|
84
|
+
let callCount = 0
|
|
85
|
+
const succeedAfter = 5 // Succeed after 5 attempts
|
|
86
|
+
|
|
87
|
+
const retryTimes = []
|
|
88
|
+
let lastCallTime = Date.now()
|
|
89
|
+
|
|
90
|
+
app.sendToICC = () => {
|
|
91
|
+
const now = Date.now()
|
|
92
|
+
if (callCount > 0) {
|
|
93
|
+
retryTimes.push(now - lastCallTime)
|
|
94
|
+
}
|
|
95
|
+
lastCallTime = now
|
|
96
|
+
|
|
97
|
+
callCount++
|
|
98
|
+
if (callCount <= succeedAfter) {
|
|
99
|
+
throw new Error(`Mock ICC connection failure (attempt ${callCount})`)
|
|
100
|
+
}
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await app.sendToICCWithRetry()
|
|
105
|
+
assert.strictEqual(callCount, succeedAfter + 1, `ICC should be called ${succeedAfter + 1} times (1 initial + ${succeedAfter} retries)`)
|
|
106
|
+
|
|
107
|
+
for (let i = 1; i < retryTimes.length; i++) {
|
|
108
|
+
assert.ok(
|
|
109
|
+
retryTimes[i] > retryTimes[i - 1],
|
|
110
|
+
`Retry interval should increase: ${retryTimes[i]} > ${retryTimes[i - 1]}`
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('should not retry if first attempt succeeds', async (t) => {
|
|
116
|
+
const applicationName = 'test-app'
|
|
117
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
118
|
+
const retryInterval = 100 // Small interval for test
|
|
119
|
+
|
|
120
|
+
setUpEnvironment({
|
|
121
|
+
PLT_APP_NAME: applicationName,
|
|
122
|
+
PLT_APP_DIR: applicationPath,
|
|
123
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
124
|
+
PLT_ICC_RETRY_TIME: retryInterval
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const app = await buildApp(logger)
|
|
128
|
+
t.after(async () => closeResources(app))
|
|
129
|
+
|
|
130
|
+
let callCount = 0
|
|
131
|
+
app.sendToICC = () => {
|
|
132
|
+
callCount++
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await app.sendToICCWithRetry()
|
|
137
|
+
assert.strictEqual(callCount, 1, 'ICC should be called only once')
|
|
138
|
+
})
|