odac 1.1.0 → 1.2.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/.agent/rules/coding.md +27 -0
- package/.agent/rules/memory.md +33 -0
- package/.agent/rules/project.md +30 -0
- package/.agent/rules/workflow.md +16 -0
- package/.github/workflows/release.yml +42 -1
- package/.github/workflows/test-coverage.yml +6 -5
- package/.github/workflows/test-publish.yml +36 -0
- package/.husky/pre-commit +10 -0
- package/.husky/pre-push +13 -0
- package/.releaserc.js +3 -3
- package/CHANGELOG.md +67 -0
- package/README.md +16 -0
- package/bin/odac.js +182 -40
- package/client/odac.js +10 -4
- package/docs/backend/01-overview/03-development-server.md +38 -45
- package/docs/backend/02-structure/01-typical-project-layout.md +59 -26
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +2 -2
- package/docs/backend/03-config/02-static-route-mapping-optional.md +1 -1
- package/docs/backend/03-config/03-request-timeout.md +1 -1
- package/docs/backend/03-config/04-environment-variables.md +4 -4
- package/docs/backend/03-config/05-early-hints.md +2 -2
- package/docs/backend/04-routing/03-api-and-data-routes.md +18 -0
- package/docs/backend/04-routing/07-cron-jobs.md +17 -1
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +48 -3
- package/docs/backend/05-controllers/03-controller-classes.md +40 -20
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +17 -0
- package/docs/backend/07-views/10-styling-and-tailwind.md +93 -0
- package/docs/backend/08-database/01-getting-started.md +2 -2
- package/docs/backend/10-authentication/03-register.md +1 -1
- package/docs/backend/10-authentication/04-odac-register-forms.md +2 -2
- package/docs/backend/10-authentication/05-session-management.md +15 -1
- package/docs/backend/10-authentication/06-odac-login-forms.md +2 -2
- package/docs/backend/10-authentication/07-magic-links.md +1 -1
- package/docs/index.json +5 -1
- package/jest.config.js +1 -1
- package/package.json +9 -5
- package/src/Auth.js +58 -23
- package/src/Config.js +7 -7
- package/src/Env.js +3 -1
- package/src/Ipc.js +7 -0
- package/src/Lang.js +9 -2
- package/src/Odac.js +44 -35
- package/src/Request.js +1 -1
- package/src/Route/Cron.js +58 -17
- package/src/Route/Internal.js +1 -1
- package/src/Route.js +282 -99
- package/src/Server.js +40 -3
- package/src/Storage.js +4 -0
- package/src/Token.js +6 -4
- package/src/Validator.js +1 -1
- package/src/Var.js +22 -6
- package/src/View/EarlyHints.js +43 -33
- package/src/View/Form.js +17 -11
- package/src/View.js +62 -6
- package/template/package.json +3 -1
- package/template/view/content/home.html +3 -3
- package/template/view/head/main.html +2 -2
- package/test/Client.test.js +168 -0
- package/test/Config.test.js +112 -0
- package/test/Lang.test.js +92 -0
- package/test/Odac.test.js +86 -0
- package/test/{framework/middleware.test.js → Route/Middleware.test.js} +2 -2
- package/test/{framework/Route.test.js → Route.test.js} +1 -1
- package/test/{framework/View → View}/EarlyHints.test.js +1 -1
- package/test/{framework/WebSocket.test.js → WebSocket.test.js} +2 -2
- package/test/scripts/check-coverage.js +4 -4
- package/test/cli/Cli.test.js +0 -36
- package/test/core/Commands.test.js +0 -538
- package/test/core/Config.test.js +0 -1432
- package/test/core/Lang.test.js +0 -250
- package/test/core/Odac.test.js +0 -234
- package/test/core/Process.test.js +0 -156
- package/test/server/Api.test.js +0 -647
- package/test/server/DNS.test.js +0 -2050
- package/test/server/DNS.test.js.bak +0 -2084
- package/test/server/Hub.test.js +0 -497
- package/test/server/Log.test.js +0 -73
- package/test/server/Mail.account.test_.js +0 -460
- package/test/server/Mail.init.test_.js +0 -411
- package/test/server/Mail.test_.js +0 -1340
- package/test/server/SSL.test_.js +0 -1491
- package/test/server/Server.test.js +0 -765
- package/test/server/Service.test_.js +0 -1127
- package/test/server/Subdomain.test.js +0 -440
- package/test/server/Web/Firewall.test.js +0 -175
- package/test/server/Web/Proxy.test.js +0 -397
- package/test/server/Web.test.js +0 -1494
- package/test/server/__mocks__/acme-client.js +0 -17
- package/test/server/__mocks__/bcrypt.js +0 -50
- package/test/server/__mocks__/child_process.js +0 -389
- package/test/server/__mocks__/crypto.js +0 -432
- package/test/server/__mocks__/fs.js +0 -450
- package/test/server/__mocks__/globalOdac.js +0 -227
- package/test/server/__mocks__/http.js +0 -575
- package/test/server/__mocks__/https.js +0 -272
- package/test/server/__mocks__/index.js +0 -249
- package/test/server/__mocks__/mail/server.js +0 -100
- package/test/server/__mocks__/mail/smtp.js +0 -31
- package/test/server/__mocks__/mailparser.js +0 -81
- package/test/server/__mocks__/net.js +0 -369
- package/test/server/__mocks__/node-forge.js +0 -328
- package/test/server/__mocks__/os.js +0 -320
- package/test/server/__mocks__/path.js +0 -291
- package/test/server/__mocks__/selfsigned.js +0 -8
- package/test/server/__mocks__/server/src/mail/server.js +0 -100
- package/test/server/__mocks__/server/src/mail/smtp.js +0 -31
- package/test/server/__mocks__/smtp-server.js +0 -106
- package/test/server/__mocks__/sqlite3.js +0 -394
- package/test/server/__mocks__/testFactories.js +0 -299
- package/test/server/__mocks__/testHelpers.js +0 -363
- package/test/server/__mocks__/tls.js +0 -229
- /package/template/{config.json → odac.json} +0 -0
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mock implementation of sqlite3 for server tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const {createMockEventEmitter} = require('./testHelpers')
|
|
6
|
-
|
|
7
|
-
class MockDatabase {
|
|
8
|
-
constructor(filename, mode, callback) {
|
|
9
|
-
Object.assign(this, createMockEventEmitter())
|
|
10
|
-
|
|
11
|
-
if (typeof mode === 'function') {
|
|
12
|
-
callback = mode
|
|
13
|
-
mode = undefined
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
this.filename = filename
|
|
17
|
-
this.mode = mode
|
|
18
|
-
this.open = true
|
|
19
|
-
this.statements = new Map()
|
|
20
|
-
this.data = new Map() // Mock data storage
|
|
21
|
-
|
|
22
|
-
// Simulate async database opening
|
|
23
|
-
setTimeout(() => {
|
|
24
|
-
if (callback) {
|
|
25
|
-
callback(null) // No error
|
|
26
|
-
}
|
|
27
|
-
this.emit('open')
|
|
28
|
-
}, 0)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
serialize(callback) {
|
|
32
|
-
if (callback) {
|
|
33
|
-
callback()
|
|
34
|
-
}
|
|
35
|
-
return this
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
parallelize(callback) {
|
|
39
|
-
if (callback) {
|
|
40
|
-
callback()
|
|
41
|
-
}
|
|
42
|
-
return this
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
run(sql, params, callback) {
|
|
46
|
-
if (typeof params === 'function') {
|
|
47
|
-
callback = params
|
|
48
|
-
params = []
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Mock SQL execution
|
|
52
|
-
const result = {
|
|
53
|
-
lastID: Math.floor(Math.random() * 1000) + 1,
|
|
54
|
-
changes: 1
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Store mock data for SELECT queries
|
|
58
|
-
if (sql.includes('INSERT INTO')) {
|
|
59
|
-
const tableName = this.extractTableName(sql)
|
|
60
|
-
if (!this.data.has(tableName)) {
|
|
61
|
-
this.data.set(tableName, [])
|
|
62
|
-
}
|
|
63
|
-
this.data.get(tableName).push({id: result.lastID, ...params})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
if (callback) {
|
|
68
|
-
callback.call(result, null)
|
|
69
|
-
}
|
|
70
|
-
this.emit('run', sql, params)
|
|
71
|
-
}, 0)
|
|
72
|
-
|
|
73
|
-
return this
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
get(sql, params, callback) {
|
|
77
|
-
if (typeof params === 'function') {
|
|
78
|
-
callback = params
|
|
79
|
-
params = []
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
let row = null
|
|
83
|
-
|
|
84
|
-
// Mock data retrieval
|
|
85
|
-
if (sql.includes('SELECT')) {
|
|
86
|
-
const tableName = this.extractTableName(sql)
|
|
87
|
-
const tableData = this.data.get(tableName) || []
|
|
88
|
-
|
|
89
|
-
if (tableData.length > 0) {
|
|
90
|
-
row = tableData[0] // Return first row
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
setTimeout(() => {
|
|
95
|
-
if (callback) {
|
|
96
|
-
callback(null, row)
|
|
97
|
-
}
|
|
98
|
-
this.emit('get', sql, params, row)
|
|
99
|
-
}, 0)
|
|
100
|
-
|
|
101
|
-
return this
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
all(sql, params, callback) {
|
|
105
|
-
if (typeof params === 'function') {
|
|
106
|
-
callback = params
|
|
107
|
-
params = []
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let rows = []
|
|
111
|
-
|
|
112
|
-
// Mock data retrieval
|
|
113
|
-
if (sql.includes('SELECT')) {
|
|
114
|
-
const tableName = this.extractTableName(sql)
|
|
115
|
-
rows = this.data.get(tableName) || []
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
setTimeout(() => {
|
|
119
|
-
if (callback) {
|
|
120
|
-
callback(null, rows)
|
|
121
|
-
}
|
|
122
|
-
this.emit('all', sql, params, rows)
|
|
123
|
-
}, 0)
|
|
124
|
-
|
|
125
|
-
return this
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
each(sql, params, rowCallback, completeCallback) {
|
|
129
|
-
if (typeof params === 'function') {
|
|
130
|
-
completeCallback = rowCallback
|
|
131
|
-
rowCallback = params
|
|
132
|
-
params = []
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
let rows = []
|
|
136
|
-
let count = 0
|
|
137
|
-
|
|
138
|
-
// Mock data retrieval
|
|
139
|
-
if (sql.includes('SELECT')) {
|
|
140
|
-
const tableName = this.extractTableName(sql)
|
|
141
|
-
rows = this.data.get(tableName) || []
|
|
142
|
-
count = rows.length
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
setTimeout(() => {
|
|
146
|
-
// Call row callback for each row
|
|
147
|
-
rows.forEach(row => {
|
|
148
|
-
if (rowCallback) {
|
|
149
|
-
rowCallback(null, row)
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// Call complete callback
|
|
154
|
-
if (completeCallback) {
|
|
155
|
-
completeCallback(null, count)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this.emit('each', sql, params, count)
|
|
159
|
-
}, 0)
|
|
160
|
-
|
|
161
|
-
return this
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
prepare(sql, params, callback) {
|
|
165
|
-
if (typeof params === 'function') {
|
|
166
|
-
callback = params
|
|
167
|
-
params = []
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const statement = new MockStatement(sql, params, this)
|
|
171
|
-
const statementId = Math.random().toString(36)
|
|
172
|
-
this.statements.set(statementId, statement)
|
|
173
|
-
|
|
174
|
-
setTimeout(() => {
|
|
175
|
-
if (callback) {
|
|
176
|
-
callback(null, statement)
|
|
177
|
-
}
|
|
178
|
-
this.emit('prepare', sql, params)
|
|
179
|
-
}, 0)
|
|
180
|
-
|
|
181
|
-
return statement
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
exec(sql, callback) {
|
|
185
|
-
setTimeout(() => {
|
|
186
|
-
if (callback) {
|
|
187
|
-
callback(null)
|
|
188
|
-
}
|
|
189
|
-
this.emit('exec', sql)
|
|
190
|
-
}, 0)
|
|
191
|
-
|
|
192
|
-
return this
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
close(callback) {
|
|
196
|
-
this.open = false
|
|
197
|
-
|
|
198
|
-
setTimeout(() => {
|
|
199
|
-
if (callback) {
|
|
200
|
-
callback(null)
|
|
201
|
-
}
|
|
202
|
-
this.emit('close')
|
|
203
|
-
}, 0)
|
|
204
|
-
|
|
205
|
-
return this
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Helper method to extract table name from SQL
|
|
209
|
-
extractTableName(sql) {
|
|
210
|
-
const match = sql.match(/(?:FROM|INTO|UPDATE)\s+(\w+)/i)
|
|
211
|
-
return match ? match[1] : 'unknown'
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Test helper methods
|
|
215
|
-
setMockData(tableName, data) {
|
|
216
|
-
this.data.set(tableName, data)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
getMockData(tableName) {
|
|
220
|
-
return this.data.get(tableName) || []
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
clearMockData() {
|
|
224
|
-
this.data.clear()
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
class MockStatement {
|
|
229
|
-
constructor(sql, params, database) {
|
|
230
|
-
Object.assign(this, createMockEventEmitter())
|
|
231
|
-
|
|
232
|
-
this.sql = sql
|
|
233
|
-
this.params = params
|
|
234
|
-
this.database = database
|
|
235
|
-
this.finalized = false
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
bind(params, callback) {
|
|
239
|
-
this.params = Array.isArray(params) ? params : [params]
|
|
240
|
-
|
|
241
|
-
setTimeout(() => {
|
|
242
|
-
if (callback) {
|
|
243
|
-
callback(null)
|
|
244
|
-
}
|
|
245
|
-
this.emit('bind', params)
|
|
246
|
-
}, 0)
|
|
247
|
-
|
|
248
|
-
return this
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
reset(callback) {
|
|
252
|
-
this.params = []
|
|
253
|
-
|
|
254
|
-
setTimeout(() => {
|
|
255
|
-
if (callback) {
|
|
256
|
-
callback(null)
|
|
257
|
-
}
|
|
258
|
-
this.emit('reset')
|
|
259
|
-
}, 0)
|
|
260
|
-
|
|
261
|
-
return this
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
finalize(callback) {
|
|
265
|
-
this.finalized = true
|
|
266
|
-
|
|
267
|
-
setTimeout(() => {
|
|
268
|
-
if (callback) {
|
|
269
|
-
callback(null)
|
|
270
|
-
}
|
|
271
|
-
this.emit('finalize')
|
|
272
|
-
}, 0)
|
|
273
|
-
|
|
274
|
-
return this
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
run(params, callback) {
|
|
278
|
-
if (typeof params === 'function') {
|
|
279
|
-
callback = params
|
|
280
|
-
params = this.params
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const result = {
|
|
284
|
-
lastID: Math.floor(Math.random() * 1000) + 1,
|
|
285
|
-
changes: 1
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
setTimeout(() => {
|
|
289
|
-
if (callback) {
|
|
290
|
-
callback.call(result, null)
|
|
291
|
-
}
|
|
292
|
-
this.emit('run', params)
|
|
293
|
-
}, 0)
|
|
294
|
-
|
|
295
|
-
return this
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
get(params, callback) {
|
|
299
|
-
if (typeof params === 'function') {
|
|
300
|
-
callback = params
|
|
301
|
-
params = this.params
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const row = {id: 1, data: 'mock'}
|
|
305
|
-
|
|
306
|
-
setTimeout(() => {
|
|
307
|
-
if (callback) {
|
|
308
|
-
callback(null, row)
|
|
309
|
-
}
|
|
310
|
-
this.emit('get', params, row)
|
|
311
|
-
}, 0)
|
|
312
|
-
|
|
313
|
-
return this
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
all(params, callback) {
|
|
317
|
-
if (typeof params === 'function') {
|
|
318
|
-
callback = params
|
|
319
|
-
params = this.params
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const rows = [{id: 1, data: 'mock'}]
|
|
323
|
-
|
|
324
|
-
setTimeout(() => {
|
|
325
|
-
if (callback) {
|
|
326
|
-
callback(null, rows)
|
|
327
|
-
}
|
|
328
|
-
this.emit('all', params, rows)
|
|
329
|
-
}, 0)
|
|
330
|
-
|
|
331
|
-
return this
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
each(params, rowCallback, completeCallback) {
|
|
335
|
-
if (typeof params === 'function') {
|
|
336
|
-
completeCallback = rowCallback
|
|
337
|
-
rowCallback = params
|
|
338
|
-
params = this.params
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const rows = [{id: 1, data: 'mock'}]
|
|
342
|
-
|
|
343
|
-
setTimeout(() => {
|
|
344
|
-
rows.forEach(row => {
|
|
345
|
-
if (rowCallback) {
|
|
346
|
-
rowCallback(null, row)
|
|
347
|
-
}
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
if (completeCallback) {
|
|
351
|
-
completeCallback(null, rows.length)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
this.emit('each', params, rows.length)
|
|
355
|
-
}, 0)
|
|
356
|
-
|
|
357
|
-
return this
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Mock constants
|
|
362
|
-
const OPEN_READONLY = 1
|
|
363
|
-
const OPEN_READWRITE = 2
|
|
364
|
-
const OPEN_CREATE = 4
|
|
365
|
-
const OPEN_FULLMUTEX = 16
|
|
366
|
-
const OPEN_SHAREDCACHE = 32
|
|
367
|
-
const OPEN_PRIVATECACHE = 64
|
|
368
|
-
|
|
369
|
-
const sqlite3 = {
|
|
370
|
-
Database: MockDatabase,
|
|
371
|
-
Statement: MockStatement,
|
|
372
|
-
|
|
373
|
-
// Constants
|
|
374
|
-
OPEN_READONLY,
|
|
375
|
-
OPEN_READWRITE,
|
|
376
|
-
OPEN_CREATE,
|
|
377
|
-
OPEN_FULLMUTEX,
|
|
378
|
-
OPEN_SHAREDCACHE,
|
|
379
|
-
OPEN_PRIVATECACHE,
|
|
380
|
-
|
|
381
|
-
// Verbose mode
|
|
382
|
-
verbose: jest.fn(() => ({
|
|
383
|
-
Database: MockDatabase,
|
|
384
|
-
Statement: MockStatement,
|
|
385
|
-
OPEN_READONLY,
|
|
386
|
-
OPEN_READWRITE,
|
|
387
|
-
OPEN_CREATE,
|
|
388
|
-
OPEN_FULLMUTEX,
|
|
389
|
-
OPEN_SHAREDCACHE,
|
|
390
|
-
OPEN_PRIVATECACHE
|
|
391
|
-
}))
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
module.exports = sqlite3
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test data factory functions for consistent mock data generation
|
|
3
|
-
* Provides builders for common data structures used across server tests
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Factory for creating mock website configurations
|
|
8
|
-
*/
|
|
9
|
-
const createMockWebsiteConfig = (domain = 'example.com', overrides = {}) => {
|
|
10
|
-
return {
|
|
11
|
-
domain,
|
|
12
|
-
path: `/var/odac/${domain}`,
|
|
13
|
-
subdomain: ['www'],
|
|
14
|
-
cert: {
|
|
15
|
-
ssl: {
|
|
16
|
-
key: `/etc/ssl/private/${domain}.key`,
|
|
17
|
-
cert: `/etc/ssl/certs/${domain}.crt`,
|
|
18
|
-
expiry: Date.now() + 86400000 * 30 // 30 days from now
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
DNS: {
|
|
22
|
-
A: [{name: domain, value: '127.0.0.1'}],
|
|
23
|
-
AAAA: [{name: domain, value: '::1'}],
|
|
24
|
-
CNAME: [{name: `www.${domain}`, value: domain}],
|
|
25
|
-
MX: [{name: domain, value: `mail.${domain}`, priority: 10}],
|
|
26
|
-
TXT: [{name: domain, value: 'v=spf1 mx ~all'}],
|
|
27
|
-
NS: [
|
|
28
|
-
{name: domain, value: `ns1.${domain}`},
|
|
29
|
-
{name: domain, value: `ns2.${domain}`}
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
...overrides
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Factory for creating mock service configurations
|
|
38
|
-
*/
|
|
39
|
-
const createMockServiceConfig = (name = 'test-service.js', overrides = {}) => {
|
|
40
|
-
return {
|
|
41
|
-
id: Math.floor(Math.random() * 1000),
|
|
42
|
-
name,
|
|
43
|
-
file: `/path/to/${name}`,
|
|
44
|
-
active: true,
|
|
45
|
-
pid: Math.floor(Math.random() * 10000) + 1000,
|
|
46
|
-
status: 'running',
|
|
47
|
-
started: Date.now(),
|
|
48
|
-
restarts: 0,
|
|
49
|
-
errors: 0,
|
|
50
|
-
...overrides
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Factory for creating mock mail account data
|
|
56
|
-
*/
|
|
57
|
-
const createMockMailAccount = (email = 'test@example.com', overrides = {}) => {
|
|
58
|
-
const [localPart, domain] = email.split('@')
|
|
59
|
-
return {
|
|
60
|
-
email,
|
|
61
|
-
localPart,
|
|
62
|
-
domain,
|
|
63
|
-
password: '$2b$10$hashedpasswordexample',
|
|
64
|
-
created: Date.now(),
|
|
65
|
-
lastLogin: null,
|
|
66
|
-
...overrides
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Factory for creating mock DNS records
|
|
72
|
-
*/
|
|
73
|
-
const createMockDNSRecord = (type = 'A', name = 'example.com', value = '127.0.0.1', overrides = {}) => {
|
|
74
|
-
const baseRecord = {
|
|
75
|
-
name,
|
|
76
|
-
type,
|
|
77
|
-
value,
|
|
78
|
-
ttl: 300
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Add type-specific properties
|
|
82
|
-
switch (type) {
|
|
83
|
-
case 'MX':
|
|
84
|
-
baseRecord.priority = 10
|
|
85
|
-
break
|
|
86
|
-
case 'SOA':
|
|
87
|
-
baseRecord.serial = Math.floor(Date.now() / 1000)
|
|
88
|
-
baseRecord.refresh = 3600
|
|
89
|
-
baseRecord.retry = 1800
|
|
90
|
-
baseRecord.expire = 604800
|
|
91
|
-
baseRecord.minimum = 86400
|
|
92
|
-
break
|
|
93
|
-
case 'SRV':
|
|
94
|
-
baseRecord.priority = 10
|
|
95
|
-
baseRecord.weight = 5
|
|
96
|
-
baseRecord.port = 443
|
|
97
|
-
break
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
...baseRecord,
|
|
102
|
-
...overrides
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Factory for creating mock SSL certificate data
|
|
108
|
-
*/
|
|
109
|
-
const createMockSSLCertificate = (domain = 'example.com', overrides = {}) => {
|
|
110
|
-
return {
|
|
111
|
-
domain,
|
|
112
|
-
keyPath: `/etc/ssl/private/${domain}.key`,
|
|
113
|
-
certPath: `/etc/ssl/certs/${domain}.crt`,
|
|
114
|
-
chainPath: `/etc/ssl/certs/${domain}-chain.crt`,
|
|
115
|
-
issued: Date.now() - 86400000 * 10, // 10 days ago
|
|
116
|
-
expiry: Date.now() + 86400000 * 80, // 80 days from now
|
|
117
|
-
issuer: "Let's Encrypt Authority X3",
|
|
118
|
-
algorithm: 'RSA',
|
|
119
|
-
keySize: 2048,
|
|
120
|
-
...overrides
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Factory for creating mock HTTP request objects
|
|
126
|
-
*/
|
|
127
|
-
const createMockRequest = (url = '/', method = 'GET', overrides = {}) => {
|
|
128
|
-
return {
|
|
129
|
-
url,
|
|
130
|
-
method,
|
|
131
|
-
headers: {
|
|
132
|
-
host: 'example.com',
|
|
133
|
-
'user-agent': 'Mozilla/5.0 Test Browser',
|
|
134
|
-
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
135
|
-
...overrides.headers
|
|
136
|
-
},
|
|
137
|
-
connection: {
|
|
138
|
-
remoteAddress: '127.0.0.1',
|
|
139
|
-
remotePort: 12345
|
|
140
|
-
},
|
|
141
|
-
body: '',
|
|
142
|
-
query: {},
|
|
143
|
-
params: {},
|
|
144
|
-
...overrides
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Factory for creating mock HTTP response objects
|
|
150
|
-
*/
|
|
151
|
-
const createMockResponse = (overrides = {}) => {
|
|
152
|
-
const response = {
|
|
153
|
-
statusCode: 200,
|
|
154
|
-
headers: {},
|
|
155
|
-
headersSent: false,
|
|
156
|
-
finished: false,
|
|
157
|
-
setHeader: jest.fn((name, value) => {
|
|
158
|
-
response.headers[name.toLowerCase()] = value
|
|
159
|
-
}),
|
|
160
|
-
getHeader: jest.fn(name => response.headers[name.toLowerCase()]),
|
|
161
|
-
removeHeader: jest.fn(name => {
|
|
162
|
-
delete response.headers[name.toLowerCase()]
|
|
163
|
-
}),
|
|
164
|
-
writeHead: jest.fn((statusCode, headers) => {
|
|
165
|
-
response.statusCode = statusCode
|
|
166
|
-
if (headers) {
|
|
167
|
-
Object.entries(headers).forEach(([name, value]) => {
|
|
168
|
-
response.setHeader(name, value)
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
response.headersSent = true
|
|
172
|
-
}),
|
|
173
|
-
write: jest.fn(),
|
|
174
|
-
end: jest.fn(data => {
|
|
175
|
-
if (data) response.write(data)
|
|
176
|
-
response.finished = true
|
|
177
|
-
}),
|
|
178
|
-
...overrides
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return response
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Factory for creating mock TCP socket objects
|
|
186
|
-
*/
|
|
187
|
-
const createMockSocket = (overrides = {}) => {
|
|
188
|
-
return {
|
|
189
|
-
remoteAddress: '127.0.0.1',
|
|
190
|
-
remotePort: 12345,
|
|
191
|
-
localAddress: '127.0.0.1',
|
|
192
|
-
localPort: 1453,
|
|
193
|
-
readable: true,
|
|
194
|
-
writable: true,
|
|
195
|
-
destroyed: false,
|
|
196
|
-
write: jest.fn(),
|
|
197
|
-
end: jest.fn(),
|
|
198
|
-
destroy: jest.fn(() => {
|
|
199
|
-
socket.destroyed = true
|
|
200
|
-
}),
|
|
201
|
-
on: jest.fn(),
|
|
202
|
-
once: jest.fn(),
|
|
203
|
-
emit: jest.fn(),
|
|
204
|
-
removeListener: jest.fn(),
|
|
205
|
-
removeAllListeners: jest.fn(),
|
|
206
|
-
...overrides
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Factory for creating mock child process objects
|
|
212
|
-
*/
|
|
213
|
-
const createMockChildProcess = (pid = 12345, overrides = {}) => {
|
|
214
|
-
return {
|
|
215
|
-
pid,
|
|
216
|
-
killed: false,
|
|
217
|
-
exitCode: null,
|
|
218
|
-
signalCode: null,
|
|
219
|
-
connected: true,
|
|
220
|
-
stdout: {
|
|
221
|
-
on: jest.fn(),
|
|
222
|
-
pipe: jest.fn(),
|
|
223
|
-
readable: true
|
|
224
|
-
},
|
|
225
|
-
stderr: {
|
|
226
|
-
on: jest.fn(),
|
|
227
|
-
pipe: jest.fn(),
|
|
228
|
-
readable: true
|
|
229
|
-
},
|
|
230
|
-
stdin: {
|
|
231
|
-
write: jest.fn(),
|
|
232
|
-
end: jest.fn(),
|
|
233
|
-
writable: true
|
|
234
|
-
},
|
|
235
|
-
kill: jest.fn((signal = 'SIGTERM') => {
|
|
236
|
-
process.killed = true
|
|
237
|
-
process.signalCode = signal
|
|
238
|
-
return true
|
|
239
|
-
}),
|
|
240
|
-
send: jest.fn(),
|
|
241
|
-
disconnect: jest.fn(),
|
|
242
|
-
on: jest.fn(),
|
|
243
|
-
once: jest.fn(),
|
|
244
|
-
emit: jest.fn(),
|
|
245
|
-
removeListener: jest.fn(),
|
|
246
|
-
removeAllListeners: jest.fn(),
|
|
247
|
-
...overrides
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Factory for creating mock database result objects
|
|
253
|
-
*/
|
|
254
|
-
const createMockDatabaseResult = (rows = [], overrides = {}) => {
|
|
255
|
-
return {
|
|
256
|
-
rows,
|
|
257
|
-
rowCount: rows.length,
|
|
258
|
-
command: 'SELECT',
|
|
259
|
-
fields: [],
|
|
260
|
-
...overrides
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Factory for creating mock email message objects
|
|
266
|
-
*/
|
|
267
|
-
const createMockEmailMessage = (from = 'sender@example.com', to = 'recipient@example.com', overrides = {}) => {
|
|
268
|
-
return {
|
|
269
|
-
messageId: `<${Date.now()}.${Math.random()}@example.com>`,
|
|
270
|
-
from: {address: from, name: ''},
|
|
271
|
-
to: [{address: to, name: ''}],
|
|
272
|
-
subject: 'Test Email',
|
|
273
|
-
date: new Date(),
|
|
274
|
-
headers: new Map([
|
|
275
|
-
['from', from],
|
|
276
|
-
['to', to],
|
|
277
|
-
['subject', 'Test Email'],
|
|
278
|
-
['date', new Date().toISOString()]
|
|
279
|
-
]),
|
|
280
|
-
text: 'This is a test email message.',
|
|
281
|
-
html: '<p>This is a test email message.</p>',
|
|
282
|
-
attachments: [],
|
|
283
|
-
...overrides
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
module.exports = {
|
|
288
|
-
createMockWebsiteConfig,
|
|
289
|
-
createMockServiceConfig,
|
|
290
|
-
createMockMailAccount,
|
|
291
|
-
createMockDNSRecord,
|
|
292
|
-
createMockSSLCertificate,
|
|
293
|
-
createMockRequest,
|
|
294
|
-
createMockResponse,
|
|
295
|
-
createMockSocket,
|
|
296
|
-
createMockChildProcess,
|
|
297
|
-
createMockDatabaseResult,
|
|
298
|
-
createMockEmailMessage
|
|
299
|
-
}
|