odac 0.9.0 → 1.0.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/.github/workflows/auto-pr-description.yml +0 -2
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/release.yml +13 -6
- package/.github/workflows/test-coverage.yml +10 -9
- package/.releaserc.js +9 -6
- package/CHANGELOG.md +62 -150
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +8 -8
- package/LICENSE +21 -661
- package/README.md +12 -12
- package/SECURITY.md +4 -4
- package/bin/odac.js +101 -0
- package/{framework/web/candy.js → client/odac.js} +310 -44
- package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
- package/docs/backend/01-overview/03-development-server.md +11 -11
- package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
- package/docs/backend/03-config/04-environment-variables.md +20 -20
- package/docs/backend/03-config/05-early-hints.md +4 -4
- package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
- package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
- package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
- package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
- package/docs/backend/04-routing/05-advanced-routing.md +3 -3
- package/docs/backend/04-routing/06-error-pages.md +17 -17
- package/docs/backend/04-routing/07-cron-jobs.md +13 -13
- package/docs/backend/04-routing/08-middleware.md +214 -0
- package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
- package/docs/backend/04-routing/09-websocket-examples.md +381 -0
- package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
- package/docs/backend/04-routing/09-websocket.md +298 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
- package/docs/backend/05-controllers/03-controller-classes.md +19 -19
- package/docs/backend/05-forms/01-custom-forms.md +114 -114
- package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
- package/docs/backend/07-views/01-the-view-directory.md +1 -1
- package/docs/backend/07-views/02-rendering-a-view.md +22 -22
- package/docs/backend/07-views/03-template-syntax.md +52 -52
- package/docs/backend/07-views/03-variables.md +84 -84
- package/docs/backend/07-views/04-request-data.md +57 -57
- package/docs/backend/07-views/05-conditionals.md +78 -78
- package/docs/backend/07-views/06-loops.md +114 -114
- package/docs/backend/07-views/07-translations.md +66 -66
- package/docs/backend/07-views/08-backend-javascript.md +103 -103
- package/docs/backend/07-views/09-comments.md +71 -71
- package/docs/backend/08-database/01-database-connection.md +8 -8
- package/docs/backend/08-database/02-using-mysql.md +49 -49
- package/docs/backend/09-validation/01-the-validator-service.md +38 -38
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
- package/docs/backend/10-authentication/03-register.md +12 -12
- package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
- package/docs/backend/10-authentication/05-session-management.md +10 -10
- package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
- package/docs/backend/11-mail/01-the-mail-service.md +5 -5
- package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
- package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
- package/docs/frontend/01-overview/01-introduction.md +30 -30
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
- package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
- package/docs/frontend/03-forms/01-form-handling.md +32 -32
- package/docs/frontend/04-api-requests/01-get-post.md +33 -33
- package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
- package/docs/frontend/06-websocket/00-overview.md +76 -0
- package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
- package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
- package/docs/index.json +49 -11
- package/eslint.config.mjs +6 -6
- package/{framework/index.js → index.js} +1 -1
- package/package.json +14 -39
- package/{framework/src → src}/Auth.js +59 -59
- package/{framework/src → src}/Config.js +3 -3
- package/{framework/src → src}/Lang.js +7 -7
- package/{framework/src → src}/Mail.js +5 -5
- package/{framework/src → src}/Mysql.js +42 -42
- package/src/Odac.js +112 -0
- package/{framework/src → src}/Request.js +38 -36
- package/{framework/src → src}/Route/Internal.js +116 -116
- package/src/Route/Middleware.js +75 -0
- package/src/Route.js +621 -0
- package/src/Server.js +22 -0
- package/{framework/src → src}/Stream.js +11 -3
- package/{framework/src → src}/Validator.js +21 -21
- package/{framework/src → src}/Var.js +5 -5
- package/{framework/src → src}/View/EarlyHints.js +1 -1
- package/{framework/src → src}/View/Form.js +69 -69
- package/{framework/src → src}/View.js +78 -81
- package/src/WebSocket.js +403 -0
- package/template/config.json +5 -0
- package/{web → template}/controller/page/about.js +6 -6
- package/{web → template}/controller/page/index.js +9 -9
- package/{web → template}/package.json +4 -5
- package/{web → template}/public/assets/css/style.css +4 -4
- package/{web → template}/public/assets/js/app.js +6 -6
- package/{web → template}/route/www.js +6 -6
- package/{web → template}/skeleton/main.html +1 -1
- package/{web → template}/view/content/about.html +5 -5
- package/{web → template}/view/content/home.html +12 -12
- package/template/view/footer/main.html +11 -0
- package/{web → template}/view/head/main.html +1 -1
- package/{web → template}/view/header/main.html +2 -2
- package/test/core/Candy.test.js +58 -58
- package/test/core/Commands.test.js +7 -7
- package/test/core/Config.test.js +82 -85
- package/test/core/Lang.test.js +2 -2
- package/test/core/Process.test.js +6 -6
- package/test/framework/Route.test.js +56 -37
- package/test/framework/View/EarlyHints.test.js +2 -2
- package/test/framework/WebSocket.test.js +100 -0
- package/test/framework/middleware.test.js +85 -0
- package/test/server/Api.test.js +31 -31
- package/test/server/DNS.test.js +11 -11
- package/test/server/Hub.test.js +497 -0
- package/test/server/Mail.account.test_.js +3 -3
- package/test/server/Mail.init.test_.js +10 -10
- package/test/server/Mail.test_.js +20 -20
- package/test/server/SSL.test_.js +54 -54
- package/test/server/Server.test.js +39 -39
- package/test/server/Service.test_.js +7 -7
- package/test/server/Subdomain.test.js +7 -7
- package/test/server/Web/Firewall.test.js +87 -87
- package/test/server/Web/Proxy.test.js +397 -0
- package/test/server/{Web.test_.js → Web.test.js} +137 -205
- package/test/server/__mocks__/fs.js +2 -2
- package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
- package/test/server/__mocks__/index.js +6 -6
- package/test/server/__mocks__/testFactories.js +1 -1
- package/test/server/__mocks__/testHelpers.js +7 -7
- package/.husky/pre-commit +0 -2
- package/.kiro/steering/code-style.md +0 -56
- package/.kiro/steering/product.md +0 -20
- package/.kiro/steering/structure.md +0 -77
- package/.kiro/steering/tech.md +0 -87
- package/AGENTS.md +0 -84
- package/bin/candy +0 -10
- package/bin/candypack +0 -10
- package/cli/index.js +0 -3
- package/cli/src/Cli.js +0 -348
- package/cli/src/Connector.js +0 -93
- package/cli/src/Monitor.js +0 -416
- package/core/Candy.js +0 -87
- package/core/Commands.js +0 -239
- package/core/Config.js +0 -1094
- package/core/Lang.js +0 -52
- package/core/Log.js +0 -43
- package/core/Process.js +0 -26
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
- package/docs/server/01-installation/01-quick-install.md +0 -19
- package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
- package/docs/server/02-get-started/01-core-concepts.md +0 -7
- package/docs/server/02-get-started/02-basic-commands.md +0 -57
- package/docs/server/02-get-started/03-cli-reference.md +0 -276
- package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
- package/docs/server/03-service/01-start-a-new-service.md +0 -57
- package/docs/server/03-service/02-delete-a-service.md +0 -48
- package/docs/server/04-web/01-create-a-website.md +0 -36
- package/docs/server/04-web/02-list-websites.md +0 -9
- package/docs/server/04-web/03-delete-a-website.md +0 -29
- package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
- package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
- package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
- package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
- package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
- package/docs/server/07-mail/04-change-account-password.md +0 -23
- package/framework/src/Candy.js +0 -81
- package/framework/src/Route.js +0 -455
- package/framework/src/Server.js +0 -15
- package/locale/de-DE.json +0 -80
- package/locale/en-US.json +0 -79
- package/locale/es-ES.json +0 -80
- package/locale/fr-FR.json +0 -80
- package/locale/pt-BR.json +0 -80
- package/locale/ru-RU.json +0 -80
- package/locale/tr-TR.json +0 -85
- package/locale/zh-CN.json +0 -80
- package/server/index.js +0 -5
- package/server/src/Api.js +0 -88
- package/server/src/DNS.js +0 -940
- package/server/src/Hub.js +0 -535
- package/server/src/Mail.js +0 -571
- package/server/src/SSL.js +0 -180
- package/server/src/Server.js +0 -27
- package/server/src/Service.js +0 -248
- package/server/src/Subdomain.js +0 -64
- package/server/src/Web/Firewall.js +0 -170
- package/server/src/Web/Proxy.js +0 -134
- package/server/src/Web.js +0 -451
- package/server/src/mail/imap.js +0 -1091
- package/server/src/mail/server.js +0 -32
- package/server/src/mail/smtp.js +0 -786
- package/test/server/Client.test.js +0 -338
- package/test/server/__mocks__/http-proxy.js +0 -105
- package/watchdog/index.js +0 -3
- package/watchdog/src/Watchdog.js +0 -156
- package/web/config.json +0 -5
- package/web/view/footer/main.html +0 -11
- /package/{framework/src → src}/Env.js +0 -0
- /package/{framework/src → src}/Route/Cron.js +0 -0
- /package/{framework/src → src}/Token.js +0 -0
package/test/core/Config.test.js
CHANGED
|
@@ -5,8 +5,8 @@ const os = require('os')
|
|
|
5
5
|
jest.mock('fs')
|
|
6
6
|
jest.mock('os')
|
|
7
7
|
|
|
8
|
-
// Mock global
|
|
9
|
-
global.
|
|
8
|
+
// Mock global Odac object
|
|
9
|
+
global.Odac = {
|
|
10
10
|
core: jest.fn(name => {
|
|
11
11
|
if (name === 'Log') {
|
|
12
12
|
return {
|
|
@@ -109,7 +109,7 @@ describe('Config', () => {
|
|
|
109
109
|
global.setInterval = jest.fn().mockReturnValue({unref: jest.fn()})
|
|
110
110
|
|
|
111
111
|
// Set default process.mainModule
|
|
112
|
-
process.mainModule = {path: '/mock/node_modules/
|
|
112
|
+
process.mainModule = {path: '/mock/node_modules/odac/bin'}
|
|
113
113
|
})
|
|
114
114
|
|
|
115
115
|
afterEach(() => {
|
|
@@ -125,7 +125,7 @@ describe('Config', () => {
|
|
|
125
125
|
config = new ConfigClass()
|
|
126
126
|
config.init()
|
|
127
127
|
|
|
128
|
-
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.
|
|
128
|
+
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.odac')
|
|
129
129
|
})
|
|
130
130
|
|
|
131
131
|
it('should handle missing config file during init', () => {
|
|
@@ -150,7 +150,7 @@ describe('Config', () => {
|
|
|
150
150
|
config = new ConfigClass()
|
|
151
151
|
config.init()
|
|
152
152
|
|
|
153
|
-
expect(mockFs.readFileSync).toHaveBeenCalledWith('/home/user/.
|
|
153
|
+
expect(mockFs.readFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', 'utf8')
|
|
154
154
|
expect(config.config.server.pid).toBe(123)
|
|
155
155
|
})
|
|
156
156
|
|
|
@@ -166,7 +166,7 @@ describe('Config', () => {
|
|
|
166
166
|
expect(config.config.server.arch).toBe('x64')
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
it('should setup auto-save interval when not in
|
|
169
|
+
it('should setup auto-save interval when not in odac bin', () => {
|
|
170
170
|
process.mainModule = {path: '/mock/project'}
|
|
171
171
|
mockFs.existsSync.mockReturnValue(true)
|
|
172
172
|
mockFs.readFileSync.mockReturnValue(createValidConfig())
|
|
@@ -178,8 +178,8 @@ describe('Config', () => {
|
|
|
178
178
|
expect(global.setInterval).toHaveBeenCalledWith(expect.any(Function), 500)
|
|
179
179
|
})
|
|
180
180
|
|
|
181
|
-
it('should not setup auto-save interval when in
|
|
182
|
-
process.mainModule = {path: '/mock/node_modules/
|
|
181
|
+
it('should not setup auto-save interval when in odac bin', () => {
|
|
182
|
+
process.mainModule = {path: '/mock/node_modules/odac/bin'}
|
|
183
183
|
mockFs.existsSync.mockReturnValue(true)
|
|
184
184
|
mockFs.readFileSync.mockReturnValue(createValidConfig())
|
|
185
185
|
|
|
@@ -244,7 +244,7 @@ describe('Config', () => {
|
|
|
244
244
|
config = new ConfigClass()
|
|
245
245
|
config.init()
|
|
246
246
|
|
|
247
|
-
expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.
|
|
247
|
+
expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.odac/config.json')
|
|
248
248
|
})
|
|
249
249
|
|
|
250
250
|
it.skip('should handle corrupted JSON file', () => {
|
|
@@ -258,7 +258,7 @@ describe('Config', () => {
|
|
|
258
258
|
config.init()
|
|
259
259
|
}).not.toThrow()
|
|
260
260
|
|
|
261
|
-
expect(console.log).toHaveBeenCalledWith('Error parsing config file:', '/home/user/.
|
|
261
|
+
expect(console.log).toHaveBeenCalledWith('Error parsing config file:', '/home/user/.odac/config.json')
|
|
262
262
|
|
|
263
263
|
// Should still initialize with default server config
|
|
264
264
|
expect(config.config.server).toBeDefined()
|
|
@@ -363,7 +363,7 @@ describe('Config', () => {
|
|
|
363
363
|
config.force()
|
|
364
364
|
|
|
365
365
|
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
|
|
366
|
-
'/home/user/.
|
|
366
|
+
'/home/user/.odac/config.json',
|
|
367
367
|
expect.stringContaining('"testSave": "value"'),
|
|
368
368
|
'utf8'
|
|
369
369
|
)
|
|
@@ -377,7 +377,7 @@ describe('Config', () => {
|
|
|
377
377
|
jest.advanceTimersByTime(5000)
|
|
378
378
|
|
|
379
379
|
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
|
|
380
|
-
'/home/user/.
|
|
380
|
+
'/home/user/.odac/.bak/config.json.bak',
|
|
381
381
|
expect.stringContaining('"testBackup": "value"'),
|
|
382
382
|
'utf8'
|
|
383
383
|
)
|
|
@@ -387,14 +387,14 @@ describe('Config', () => {
|
|
|
387
387
|
config.config = {}
|
|
388
388
|
config.force()
|
|
389
389
|
|
|
390
|
-
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.
|
|
390
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', '{}', 'utf8')
|
|
391
391
|
})
|
|
392
392
|
|
|
393
393
|
it.skip('should handle null config during save', () => {
|
|
394
394
|
config.config = null
|
|
395
395
|
config.force()
|
|
396
396
|
|
|
397
|
-
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.
|
|
397
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', 'null', 'utf8')
|
|
398
398
|
})
|
|
399
399
|
|
|
400
400
|
it('should respect changed flag when saving', () => {
|
|
@@ -415,7 +415,7 @@ describe('Config', () => {
|
|
|
415
415
|
config.config = {a: 1}
|
|
416
416
|
config.force()
|
|
417
417
|
|
|
418
|
-
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.
|
|
418
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', expect.stringContaining('"a": 1'), 'utf8')
|
|
419
419
|
})
|
|
420
420
|
|
|
421
421
|
it.skip('should handle writeFileSync errors during save', () => {
|
|
@@ -605,7 +605,7 @@ describe('Config', () => {
|
|
|
605
605
|
intervalCallback()
|
|
606
606
|
|
|
607
607
|
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
|
|
608
|
-
'/home/user/.
|
|
608
|
+
'/home/user/.odac/config.json',
|
|
609
609
|
expect.stringContaining('"autoSaveTest": "value"'),
|
|
610
610
|
'utf8'
|
|
611
611
|
)
|
|
@@ -637,7 +637,7 @@ describe('Config', () => {
|
|
|
637
637
|
// Should only save once despite multiple changes
|
|
638
638
|
expect(mockFs.writeFileSync).toHaveBeenCalledTimes(1)
|
|
639
639
|
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
|
|
640
|
-
'/home/user/.
|
|
640
|
+
'/home/user/.odac/config.json',
|
|
641
641
|
expect.stringContaining('"change1": "value1"'),
|
|
642
642
|
'utf8'
|
|
643
643
|
)
|
|
@@ -678,7 +678,7 @@ describe('Config', () => {
|
|
|
678
678
|
config.config = {a: 1}
|
|
679
679
|
config.force()
|
|
680
680
|
|
|
681
|
-
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.
|
|
681
|
+
expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', expect.any(String), 'utf8')
|
|
682
682
|
})
|
|
683
683
|
|
|
684
684
|
it('should handle backup creation timeout', () => {
|
|
@@ -691,7 +691,7 @@ describe('Config', () => {
|
|
|
691
691
|
jest.advanceTimersByTime(5000)
|
|
692
692
|
|
|
693
693
|
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
|
|
694
|
-
'/home/user/.
|
|
694
|
+
'/home/user/.odac/.bak/config.json.bak',
|
|
695
695
|
expect.stringContaining('"backupTest": "value"'),
|
|
696
696
|
'utf8'
|
|
697
697
|
)
|
|
@@ -722,7 +722,7 @@ describe('Config', () => {
|
|
|
722
722
|
|
|
723
723
|
config.init()
|
|
724
724
|
|
|
725
|
-
expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.
|
|
725
|
+
expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.odac/config.json')
|
|
726
726
|
})
|
|
727
727
|
})
|
|
728
728
|
|
|
@@ -735,8 +735,8 @@ describe('Config', () => {
|
|
|
735
735
|
describe('format detection', () => {
|
|
736
736
|
it('should detect modular format when config directory exists', () => {
|
|
737
737
|
mockFs.existsSync.mockImplementation(path => {
|
|
738
|
-
if (path === '/home/user/.
|
|
739
|
-
if (path === '/home/user/.
|
|
738
|
+
if (path === '/home/user/.odac') return true
|
|
739
|
+
if (path === '/home/user/.odac/config') return true
|
|
740
740
|
return false
|
|
741
741
|
})
|
|
742
742
|
mockFs.readFileSync.mockReturnValue(JSON.stringify({server: {}}))
|
|
@@ -750,9 +750,9 @@ describe('Config', () => {
|
|
|
750
750
|
|
|
751
751
|
it('should detect single-file format when only config.json exists', () => {
|
|
752
752
|
mockFs.existsSync.mockImplementation(path => {
|
|
753
|
-
if (path === '/home/user/.
|
|
754
|
-
if (path === '/home/user/.
|
|
755
|
-
if (path === '/home/user/.
|
|
753
|
+
if (path === '/home/user/.odac') return true
|
|
754
|
+
if (path === '/home/user/.odac/config') return false
|
|
755
|
+
if (path === '/home/user/.odac/config.json') return true
|
|
756
756
|
return false
|
|
757
757
|
})
|
|
758
758
|
mockFs.readFileSync.mockReturnValue(createValidConfig())
|
|
@@ -766,7 +766,7 @@ describe('Config', () => {
|
|
|
766
766
|
|
|
767
767
|
it('should detect new installation when neither exists', () => {
|
|
768
768
|
mockFs.existsSync.mockImplementation(path => {
|
|
769
|
-
if (path === '/home/user/.
|
|
769
|
+
if (path === '/home/user/.odac') return false
|
|
770
770
|
return false
|
|
771
771
|
})
|
|
772
772
|
|
|
@@ -782,8 +782,8 @@ describe('Config', () => {
|
|
|
782
782
|
describe('modular loading', () => {
|
|
783
783
|
it('should load all module files correctly', () => {
|
|
784
784
|
mockFs.existsSync.mockImplementation(path => {
|
|
785
|
-
if (path === '/home/user/.
|
|
786
|
-
if (path === '/home/user/.
|
|
785
|
+
if (path === '/home/user/.odac') return true
|
|
786
|
+
if (path === '/home/user/.odac/config') return true
|
|
787
787
|
if (path.includes('/config/server.json')) return true
|
|
788
788
|
if (path.includes('/config/web.json')) return true
|
|
789
789
|
return false
|
|
@@ -809,8 +809,8 @@ describe('Config', () => {
|
|
|
809
809
|
|
|
810
810
|
it('should handle missing module files gracefully', () => {
|
|
811
811
|
mockFs.existsSync.mockImplementation(path => {
|
|
812
|
-
if (path === '/home/user/.
|
|
813
|
-
if (path === '/home/user/.
|
|
812
|
+
if (path === '/home/user/.odac') return true
|
|
813
|
+
if (path === '/home/user/.odac/config') return true
|
|
814
814
|
return false
|
|
815
815
|
})
|
|
816
816
|
|
|
@@ -823,8 +823,8 @@ describe('Config', () => {
|
|
|
823
823
|
|
|
824
824
|
it('should recover from corrupted module file using backup', () => {
|
|
825
825
|
mockFs.existsSync.mockImplementation(path => {
|
|
826
|
-
if (path === '/home/user/.
|
|
827
|
-
if (path === '/home/user/.
|
|
826
|
+
if (path === '/home/user/.odac') return true
|
|
827
|
+
if (path === '/home/user/.odac/config') return true
|
|
828
828
|
if (path.includes('/config/server.json')) return true
|
|
829
829
|
if (path.includes('/.bak/server.json.bak')) return true
|
|
830
830
|
return false
|
|
@@ -854,8 +854,8 @@ describe('Config', () => {
|
|
|
854
854
|
|
|
855
855
|
it('should handle empty module file', () => {
|
|
856
856
|
mockFs.existsSync.mockImplementation(path => {
|
|
857
|
-
if (path === '/home/user/.
|
|
858
|
-
if (path === '/home/user/.
|
|
857
|
+
if (path === '/home/user/.odac') return true
|
|
858
|
+
if (path === '/home/user/.odac/config') return true
|
|
859
859
|
if (path.includes('/config/server.json')) return true
|
|
860
860
|
return false
|
|
861
861
|
})
|
|
@@ -876,9 +876,9 @@ describe('Config', () => {
|
|
|
876
876
|
describe('migration from single-file to modular', () => {
|
|
877
877
|
it('should migrate single-file config to modular format', () => {
|
|
878
878
|
mockFs.existsSync.mockImplementation(path => {
|
|
879
|
-
if (path === '/home/user/.
|
|
880
|
-
if (path === '/home/user/.
|
|
881
|
-
if (path === '/home/user/.
|
|
879
|
+
if (path === '/home/user/.odac') return true
|
|
880
|
+
if (path === '/home/user/.odac/config.json') return true
|
|
881
|
+
if (path === '/home/user/.odac/config') return false
|
|
882
882
|
if (path.includes('.pre-modular')) return false
|
|
883
883
|
return false
|
|
884
884
|
})
|
|
@@ -891,13 +891,13 @@ describe('Config', () => {
|
|
|
891
891
|
|
|
892
892
|
let configDirCreated = false
|
|
893
893
|
mockFs.mkdirSync.mockImplementation(path => {
|
|
894
|
-
if (path === '/home/user/.
|
|
894
|
+
if (path === '/home/user/.odac/config') {
|
|
895
895
|
configDirCreated = true
|
|
896
896
|
}
|
|
897
897
|
})
|
|
898
898
|
|
|
899
899
|
mockFs.readFileSync.mockImplementation(path => {
|
|
900
|
-
if (path === '/home/user/.
|
|
900
|
+
if (path === '/home/user/.odac/config.json') {
|
|
901
901
|
return JSON.stringify(singleFileConfig)
|
|
902
902
|
}
|
|
903
903
|
if (configDirCreated && path.includes('/config/')) {
|
|
@@ -913,30 +913,27 @@ describe('Config', () => {
|
|
|
913
913
|
config = new ConfigClass()
|
|
914
914
|
config.init()
|
|
915
915
|
|
|
916
|
-
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.
|
|
917
|
-
expect(mockFs.copyFileSync).toHaveBeenCalledWith(
|
|
918
|
-
'/home/user/.candypack/config.json',
|
|
919
|
-
'/home/user/.candypack/config.json.pre-modular'
|
|
920
|
-
)
|
|
916
|
+
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.odac/config', {recursive: true})
|
|
917
|
+
expect(mockFs.copyFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', '/home/user/.odac/config.json.pre-modular')
|
|
921
918
|
})
|
|
922
919
|
|
|
923
920
|
it('should rollback migration on failure', () => {
|
|
924
921
|
mockFs.existsSync.mockImplementation(path => {
|
|
925
|
-
if (path === '/home/user/.
|
|
926
|
-
if (path === '/home/user/.
|
|
927
|
-
if (path === '/home/user/.
|
|
922
|
+
if (path === '/home/user/.odac') return true
|
|
923
|
+
if (path === '/home/user/.odac/config.json') return true
|
|
924
|
+
if (path === '/home/user/.odac/config') return false
|
|
928
925
|
return false
|
|
929
926
|
})
|
|
930
927
|
|
|
931
928
|
mockFs.readFileSync.mockReturnValue(createValidConfig())
|
|
932
929
|
|
|
933
930
|
mockFs.mkdirSync.mockImplementation(path => {
|
|
934
|
-
if (path === '/home/user/.
|
|
931
|
+
if (path === '/home/user/.odac/config') {
|
|
935
932
|
// Simulate successful directory creation
|
|
936
933
|
mockFs.existsSync.mockImplementation(p => {
|
|
937
934
|
if (p === path) return true
|
|
938
|
-
if (p === '/home/user/.
|
|
939
|
-
if (p === '/home/user/.
|
|
935
|
+
if (p === '/home/user/.odac') return true
|
|
936
|
+
if (p === '/home/user/.odac/config.json') return true
|
|
940
937
|
return false
|
|
941
938
|
})
|
|
942
939
|
}
|
|
@@ -950,15 +947,15 @@ describe('Config', () => {
|
|
|
950
947
|
config = new ConfigClass()
|
|
951
948
|
config.init()
|
|
952
949
|
|
|
953
|
-
expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.
|
|
950
|
+
expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.odac/config', {recursive: true, force: true})
|
|
954
951
|
expect(config.config.server).toBeDefined()
|
|
955
952
|
})
|
|
956
953
|
|
|
957
954
|
it('should handle migration with permission errors', () => {
|
|
958
955
|
mockFs.existsSync.mockImplementation(path => {
|
|
959
|
-
if (path === '/home/user/.
|
|
960
|
-
if (path === '/home/user/.
|
|
961
|
-
if (path === '/home/user/.
|
|
956
|
+
if (path === '/home/user/.odac') return true
|
|
957
|
+
if (path === '/home/user/.odac/config.json') return true
|
|
958
|
+
if (path === '/home/user/.odac/config') return false
|
|
962
959
|
return false
|
|
963
960
|
})
|
|
964
961
|
|
|
@@ -979,9 +976,9 @@ describe('Config', () => {
|
|
|
979
976
|
|
|
980
977
|
it('should use rmSync with recursive and force options during rollback', () => {
|
|
981
978
|
mockFs.existsSync.mockImplementation(path => {
|
|
982
|
-
if (path === '/home/user/.
|
|
983
|
-
if (path === '/home/user/.
|
|
984
|
-
if (path === '/home/user/.
|
|
979
|
+
if (path === '/home/user/.odac') return true
|
|
980
|
+
if (path === '/home/user/.odac/config.json') return true
|
|
981
|
+
if (path === '/home/user/.odac/config') return false
|
|
985
982
|
return false
|
|
986
983
|
})
|
|
987
984
|
|
|
@@ -989,12 +986,12 @@ describe('Config', () => {
|
|
|
989
986
|
|
|
990
987
|
let configDirCreated = false
|
|
991
988
|
mockFs.mkdirSync.mockImplementation(path => {
|
|
992
|
-
if (path === '/home/user/.
|
|
989
|
+
if (path === '/home/user/.odac/config') {
|
|
993
990
|
configDirCreated = true
|
|
994
991
|
mockFs.existsSync.mockImplementation(p => {
|
|
995
992
|
if (p === path) return configDirCreated
|
|
996
|
-
if (p === '/home/user/.
|
|
997
|
-
if (p === '/home/user/.
|
|
993
|
+
if (p === '/home/user/.odac') return true
|
|
994
|
+
if (p === '/home/user/.odac/config.json') return true
|
|
998
995
|
return false
|
|
999
996
|
})
|
|
1000
997
|
}
|
|
@@ -1008,7 +1005,7 @@ describe('Config', () => {
|
|
|
1008
1005
|
config = new ConfigClass()
|
|
1009
1006
|
config.init()
|
|
1010
1007
|
|
|
1011
|
-
expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.
|
|
1008
|
+
expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.odac/config', {
|
|
1012
1009
|
recursive: true,
|
|
1013
1010
|
force: true
|
|
1014
1011
|
})
|
|
@@ -1016,20 +1013,20 @@ describe('Config', () => {
|
|
|
1016
1013
|
|
|
1017
1014
|
it('should handle rmSync errors gracefully during rollback', () => {
|
|
1018
1015
|
mockFs.existsSync.mockImplementation(path => {
|
|
1019
|
-
if (path === '/home/user/.
|
|
1020
|
-
if (path === '/home/user/.
|
|
1021
|
-
if (path === '/home/user/.
|
|
1016
|
+
if (path === '/home/user/.odac') return true
|
|
1017
|
+
if (path === '/home/user/.odac/config.json') return true
|
|
1018
|
+
if (path === '/home/user/.odac/config') return false
|
|
1022
1019
|
return false
|
|
1023
1020
|
})
|
|
1024
1021
|
|
|
1025
1022
|
mockFs.readFileSync.mockReturnValue(createValidConfig())
|
|
1026
1023
|
|
|
1027
1024
|
mockFs.mkdirSync.mockImplementation(path => {
|
|
1028
|
-
if (path === '/home/user/.
|
|
1025
|
+
if (path === '/home/user/.odac/config') {
|
|
1029
1026
|
mockFs.existsSync.mockImplementation(p => {
|
|
1030
1027
|
if (p === path) return true
|
|
1031
|
-
if (p === '/home/user/.
|
|
1032
|
-
if (p === '/home/user/.
|
|
1028
|
+
if (p === '/home/user/.odac') return true
|
|
1029
|
+
if (p === '/home/user/.odac/config.json') return true
|
|
1033
1030
|
return false
|
|
1034
1031
|
})
|
|
1035
1032
|
}
|
|
@@ -1061,8 +1058,8 @@ describe('Config', () => {
|
|
|
1061
1058
|
|
|
1062
1059
|
it('should save only changed modules', () => {
|
|
1063
1060
|
mockFs.existsSync.mockImplementation(path => {
|
|
1064
|
-
if (path === '/home/user/.
|
|
1065
|
-
if (path === '/home/user/.
|
|
1061
|
+
if (path === '/home/user/.odac') return true
|
|
1062
|
+
if (path === '/home/user/.odac/config') return true
|
|
1066
1063
|
return false
|
|
1067
1064
|
})
|
|
1068
1065
|
|
|
@@ -1089,8 +1086,8 @@ describe('Config', () => {
|
|
|
1089
1086
|
|
|
1090
1087
|
it('should use atomic writes for module files', () => {
|
|
1091
1088
|
mockFs.existsSync.mockImplementation(path => {
|
|
1092
|
-
if (path === '/home/user/.
|
|
1093
|
-
if (path === '/home/user/.
|
|
1089
|
+
if (path === '/home/user/.odac') return true
|
|
1090
|
+
if (path === '/home/user/.odac/config') return true
|
|
1094
1091
|
if (path.includes('/.bak')) return true
|
|
1095
1092
|
return false
|
|
1096
1093
|
})
|
|
@@ -1115,8 +1112,8 @@ describe('Config', () => {
|
|
|
1115
1112
|
|
|
1116
1113
|
it('should create backups before overwriting module files', () => {
|
|
1117
1114
|
mockFs.existsSync.mockImplementation(path => {
|
|
1118
|
-
if (path === '/home/user/.
|
|
1119
|
-
if (path === '/home/user/.
|
|
1115
|
+
if (path === '/home/user/.odac') return true
|
|
1116
|
+
if (path === '/home/user/.odac/config') return true
|
|
1120
1117
|
if (path.includes('/config/server.json')) return true
|
|
1121
1118
|
return false
|
|
1122
1119
|
})
|
|
@@ -1138,8 +1135,8 @@ describe('Config', () => {
|
|
|
1138
1135
|
|
|
1139
1136
|
it('should fallback to single-file on modular save failure', () => {
|
|
1140
1137
|
mockFs.existsSync.mockImplementation(path => {
|
|
1141
|
-
if (path === '/home/user/.
|
|
1142
|
-
if (path === '/home/user/.
|
|
1138
|
+
if (path === '/home/user/.odac') return true
|
|
1139
|
+
if (path === '/home/user/.odac/config') return true
|
|
1143
1140
|
return false
|
|
1144
1141
|
})
|
|
1145
1142
|
|
|
@@ -1228,8 +1225,8 @@ describe('Config', () => {
|
|
|
1228
1225
|
|
|
1229
1226
|
it('should write to temp file first', () => {
|
|
1230
1227
|
mockFs.existsSync.mockImplementation(path => {
|
|
1231
|
-
if (path === '/home/user/.
|
|
1232
|
-
if (path === '/home/user/.
|
|
1228
|
+
if (path === '/home/user/.odac') return true
|
|
1229
|
+
if (path === '/home/user/.odac/config') return true
|
|
1233
1230
|
return false
|
|
1234
1231
|
})
|
|
1235
1232
|
|
|
@@ -1250,8 +1247,8 @@ describe('Config', () => {
|
|
|
1250
1247
|
|
|
1251
1248
|
it('should cleanup temp file on write failure', () => {
|
|
1252
1249
|
mockFs.existsSync.mockImplementation(path => {
|
|
1253
|
-
if (path === '/home/user/.
|
|
1254
|
-
if (path === '/home/user/.
|
|
1250
|
+
if (path === '/home/user/.odac') return true
|
|
1251
|
+
if (path === '/home/user/.odac/config') return true
|
|
1255
1252
|
if (path.includes('.tmp')) return true
|
|
1256
1253
|
return false
|
|
1257
1254
|
})
|
|
@@ -1280,8 +1277,8 @@ describe('Config', () => {
|
|
|
1280
1277
|
|
|
1281
1278
|
it('should handle ENOSPC error gracefully', () => {
|
|
1282
1279
|
mockFs.existsSync.mockImplementation(path => {
|
|
1283
|
-
if (path === '/home/user/.
|
|
1284
|
-
if (path === '/home/user/.
|
|
1280
|
+
if (path === '/home/user/.odac') return true
|
|
1281
|
+
if (path === '/home/user/.odac/config') return true
|
|
1285
1282
|
return false
|
|
1286
1283
|
})
|
|
1287
1284
|
|
|
@@ -1386,8 +1383,8 @@ describe('Config', () => {
|
|
|
1386
1383
|
describe('corruption recovery', () => {
|
|
1387
1384
|
it('should create .corrupted backup when recovering from corruption', () => {
|
|
1388
1385
|
mockFs.existsSync.mockImplementation(path => {
|
|
1389
|
-
if (path === '/home/user/.
|
|
1390
|
-
if (path === '/home/user/.
|
|
1386
|
+
if (path === '/home/user/.odac') return true
|
|
1387
|
+
if (path === '/home/user/.odac/config') return true
|
|
1391
1388
|
if (path.includes('/config/dns.json')) return true
|
|
1392
1389
|
if (path.includes('/.bak/dns.json.bak')) return true
|
|
1393
1390
|
return false
|
|
@@ -1413,8 +1410,8 @@ describe('Config', () => {
|
|
|
1413
1410
|
|
|
1414
1411
|
it('should handle both main and backup being corrupted', () => {
|
|
1415
1412
|
mockFs.existsSync.mockImplementation(path => {
|
|
1416
|
-
if (path === '/home/user/.
|
|
1417
|
-
if (path === '/home/user/.
|
|
1413
|
+
if (path === '/home/user/.odac') return true
|
|
1414
|
+
if (path === '/home/user/.odac/config') return true
|
|
1418
1415
|
if (path.includes('/config/mail.json')) return true
|
|
1419
1416
|
if (path.includes('/.bak/mail.json.bak')) return true
|
|
1420
1417
|
return false
|
package/test/core/Lang.test.js
CHANGED
|
@@ -28,9 +28,9 @@ describe('Lang', () => {
|
|
|
28
28
|
console.error = originalConsoleError
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
it("should return '
|
|
31
|
+
it("should return 'Odac' for the 'Odac' key", () => {
|
|
32
32
|
const lang = new Lang()
|
|
33
|
-
expect(lang.get('
|
|
33
|
+
expect(lang.get('Odac')).toBe('Odac')
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
it('should return the translation for an existing key', () => {
|
|
@@ -7,7 +7,7 @@ const findProcess = require('find-process')
|
|
|
7
7
|
jest.mock('find-process')
|
|
8
8
|
const processKillSpy = jest.spyOn(process, 'kill').mockImplementation(() => {})
|
|
9
9
|
|
|
10
|
-
global.
|
|
10
|
+
global.Odac = {
|
|
11
11
|
core: () => ({
|
|
12
12
|
config: {}
|
|
13
13
|
})
|
|
@@ -21,7 +21,7 @@ describe('Process', () => {
|
|
|
21
21
|
// The mock function is on the .default property of the module mock
|
|
22
22
|
findProcess.default.mockClear()
|
|
23
23
|
processKillSpy.mockClear()
|
|
24
|
-
global.
|
|
24
|
+
global.Odac.core = () => ({config: {}})
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
it('should be defined', () => {
|
|
@@ -81,7 +81,7 @@ describe('Process', () => {
|
|
|
81
81
|
})
|
|
82
82
|
|
|
83
83
|
it('should call stop for all configured pids', async () => {
|
|
84
|
-
global.
|
|
84
|
+
global.Odac.core = () => ({
|
|
85
85
|
config: {
|
|
86
86
|
server: {
|
|
87
87
|
watchdog: 100,
|
|
@@ -110,7 +110,7 @@ describe('Process', () => {
|
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
it('should handle partial configurations gracefully', async () => {
|
|
113
|
-
global.
|
|
113
|
+
global.Odac.core = () => ({
|
|
114
114
|
config: {
|
|
115
115
|
server: {
|
|
116
116
|
pid: 200
|
|
@@ -130,7 +130,7 @@ describe('Process', () => {
|
|
|
130
130
|
})
|
|
131
131
|
|
|
132
132
|
it('should not call stop if no pids are configured', async () => {
|
|
133
|
-
global.
|
|
133
|
+
global.Odac.core = () => ({
|
|
134
134
|
config: {
|
|
135
135
|
server: {},
|
|
136
136
|
websites: {},
|
|
@@ -144,7 +144,7 @@ describe('Process', () => {
|
|
|
144
144
|
})
|
|
145
145
|
|
|
146
146
|
it('should handle missing top-level config keys', async () => {
|
|
147
|
-
global.
|
|
147
|
+
global.Odac.core = () => ({
|
|
148
148
|
config: {}
|
|
149
149
|
})
|
|
150
150
|
|