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.
Files changed (113) hide show
  1. package/.agent/rules/coding.md +27 -0
  2. package/.agent/rules/memory.md +33 -0
  3. package/.agent/rules/project.md +30 -0
  4. package/.agent/rules/workflow.md +16 -0
  5. package/.github/workflows/release.yml +42 -1
  6. package/.github/workflows/test-coverage.yml +6 -5
  7. package/.github/workflows/test-publish.yml +36 -0
  8. package/.husky/pre-commit +10 -0
  9. package/.husky/pre-push +13 -0
  10. package/.releaserc.js +3 -3
  11. package/CHANGELOG.md +67 -0
  12. package/README.md +16 -0
  13. package/bin/odac.js +182 -40
  14. package/client/odac.js +10 -4
  15. package/docs/backend/01-overview/03-development-server.md +38 -45
  16. package/docs/backend/02-structure/01-typical-project-layout.md +59 -26
  17. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  18. package/docs/backend/03-config/01-database-connection.md +2 -2
  19. package/docs/backend/03-config/02-static-route-mapping-optional.md +1 -1
  20. package/docs/backend/03-config/03-request-timeout.md +1 -1
  21. package/docs/backend/03-config/04-environment-variables.md +4 -4
  22. package/docs/backend/03-config/05-early-hints.md +2 -2
  23. package/docs/backend/04-routing/03-api-and-data-routes.md +18 -0
  24. package/docs/backend/04-routing/07-cron-jobs.md +17 -1
  25. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +48 -3
  26. package/docs/backend/05-controllers/03-controller-classes.md +40 -20
  27. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +17 -0
  28. package/docs/backend/07-views/10-styling-and-tailwind.md +93 -0
  29. package/docs/backend/08-database/01-getting-started.md +2 -2
  30. package/docs/backend/10-authentication/03-register.md +1 -1
  31. package/docs/backend/10-authentication/04-odac-register-forms.md +2 -2
  32. package/docs/backend/10-authentication/05-session-management.md +15 -1
  33. package/docs/backend/10-authentication/06-odac-login-forms.md +2 -2
  34. package/docs/backend/10-authentication/07-magic-links.md +1 -1
  35. package/docs/index.json +5 -1
  36. package/jest.config.js +1 -1
  37. package/package.json +9 -5
  38. package/src/Auth.js +58 -23
  39. package/src/Config.js +7 -7
  40. package/src/Env.js +3 -1
  41. package/src/Ipc.js +7 -0
  42. package/src/Lang.js +9 -2
  43. package/src/Odac.js +44 -35
  44. package/src/Request.js +1 -1
  45. package/src/Route/Cron.js +58 -17
  46. package/src/Route/Internal.js +1 -1
  47. package/src/Route.js +282 -99
  48. package/src/Server.js +40 -3
  49. package/src/Storage.js +4 -0
  50. package/src/Token.js +6 -4
  51. package/src/Validator.js +1 -1
  52. package/src/Var.js +22 -6
  53. package/src/View/EarlyHints.js +43 -33
  54. package/src/View/Form.js +17 -11
  55. package/src/View.js +62 -6
  56. package/template/package.json +3 -1
  57. package/template/view/content/home.html +3 -3
  58. package/template/view/head/main.html +2 -2
  59. package/test/Client.test.js +168 -0
  60. package/test/Config.test.js +112 -0
  61. package/test/Lang.test.js +92 -0
  62. package/test/Odac.test.js +86 -0
  63. package/test/{framework/middleware.test.js → Route/Middleware.test.js} +2 -2
  64. package/test/{framework/Route.test.js → Route.test.js} +1 -1
  65. package/test/{framework/View → View}/EarlyHints.test.js +1 -1
  66. package/test/{framework/WebSocket.test.js → WebSocket.test.js} +2 -2
  67. package/test/scripts/check-coverage.js +4 -4
  68. package/test/cli/Cli.test.js +0 -36
  69. package/test/core/Commands.test.js +0 -538
  70. package/test/core/Config.test.js +0 -1432
  71. package/test/core/Lang.test.js +0 -250
  72. package/test/core/Odac.test.js +0 -234
  73. package/test/core/Process.test.js +0 -156
  74. package/test/server/Api.test.js +0 -647
  75. package/test/server/DNS.test.js +0 -2050
  76. package/test/server/DNS.test.js.bak +0 -2084
  77. package/test/server/Hub.test.js +0 -497
  78. package/test/server/Log.test.js +0 -73
  79. package/test/server/Mail.account.test_.js +0 -460
  80. package/test/server/Mail.init.test_.js +0 -411
  81. package/test/server/Mail.test_.js +0 -1340
  82. package/test/server/SSL.test_.js +0 -1491
  83. package/test/server/Server.test.js +0 -765
  84. package/test/server/Service.test_.js +0 -1127
  85. package/test/server/Subdomain.test.js +0 -440
  86. package/test/server/Web/Firewall.test.js +0 -175
  87. package/test/server/Web/Proxy.test.js +0 -397
  88. package/test/server/Web.test.js +0 -1494
  89. package/test/server/__mocks__/acme-client.js +0 -17
  90. package/test/server/__mocks__/bcrypt.js +0 -50
  91. package/test/server/__mocks__/child_process.js +0 -389
  92. package/test/server/__mocks__/crypto.js +0 -432
  93. package/test/server/__mocks__/fs.js +0 -450
  94. package/test/server/__mocks__/globalOdac.js +0 -227
  95. package/test/server/__mocks__/http.js +0 -575
  96. package/test/server/__mocks__/https.js +0 -272
  97. package/test/server/__mocks__/index.js +0 -249
  98. package/test/server/__mocks__/mail/server.js +0 -100
  99. package/test/server/__mocks__/mail/smtp.js +0 -31
  100. package/test/server/__mocks__/mailparser.js +0 -81
  101. package/test/server/__mocks__/net.js +0 -369
  102. package/test/server/__mocks__/node-forge.js +0 -328
  103. package/test/server/__mocks__/os.js +0 -320
  104. package/test/server/__mocks__/path.js +0 -291
  105. package/test/server/__mocks__/selfsigned.js +0 -8
  106. package/test/server/__mocks__/server/src/mail/server.js +0 -100
  107. package/test/server/__mocks__/server/src/mail/smtp.js +0 -31
  108. package/test/server/__mocks__/smtp-server.js +0 -106
  109. package/test/server/__mocks__/sqlite3.js +0 -394
  110. package/test/server/__mocks__/testFactories.js +0 -299
  111. package/test/server/__mocks__/testHelpers.js +0 -363
  112. package/test/server/__mocks__/tls.js +0 -229
  113. /package/template/{config.json → odac.json} +0 -0
@@ -1,538 +0,0 @@
1
- // Mock the Odac global before requiring Commands
2
- global.Odac = {
3
- cli: jest.fn(),
4
- core: jest.fn()
5
- }
6
-
7
- // Mock the global __ function
8
- global.__ = jest.fn(key => key)
9
-
10
- const Commands = require('../../core/Commands')
11
-
12
- describe('Commands', () => {
13
- let mockCli, mockConnector, mockMonitor
14
-
15
- beforeEach(() => {
16
- mockCli = {
17
- parseArg: jest.fn(),
18
- question: jest.fn(),
19
- help: jest.fn(),
20
- boot: jest.fn()
21
- }
22
-
23
- mockConnector = {
24
- call: jest.fn()
25
- }
26
-
27
- mockMonitor = {
28
- debug: jest.fn(),
29
- monit: jest.fn()
30
- }
31
-
32
- global.Odac.cli.mockImplementation(name => {
33
- switch (name) {
34
- case 'Cli':
35
- return mockCli
36
- case 'Connector':
37
- return mockConnector
38
- case 'Monitor':
39
- return mockMonitor
40
- default:
41
- return {}
42
- }
43
- })
44
-
45
- global.Odac.core.mockImplementation(name => {
46
- if (name === 'Lang') {
47
- return {get: key => key}
48
- }
49
- return {}
50
- })
51
-
52
- // Clear all mocks
53
- jest.clearAllMocks()
54
- })
55
-
56
- describe('command structure', () => {
57
- it('should have all expected top-level commands', () => {
58
- expect(Commands.auth).toBeDefined()
59
- expect(Commands.debug).toBeDefined()
60
- expect(Commands.help).toBeDefined()
61
- expect(Commands.monit).toBeDefined()
62
- expect(Commands.restart).toBeDefined()
63
- expect(Commands.run).toBeDefined()
64
- expect(Commands.mail).toBeDefined()
65
- expect(Commands.ssl).toBeDefined()
66
- expect(Commands.subdomain).toBeDefined()
67
- expect(Commands.web).toBeDefined()
68
- })
69
-
70
- it('should have correct command descriptions', () => {
71
- expect(Commands.auth.description).toBe('Define your server to your Odac account')
72
- expect(Commands.debug.description).toBe('Debug Odac Server')
73
- expect(Commands.help.description).toBe('List all available commands')
74
- expect(Commands.monit.description).toBe('Monitor Website or Service')
75
- expect(Commands.restart.description).toBe('Restart Odac Server')
76
- expect(Commands.run.description).toBe('Add a new Service')
77
- })
78
- })
79
-
80
- describe('auth command', () => {
81
- it('should use provided key argument', async () => {
82
- const args = ['test-key']
83
- mockCli.parseArg.mockReturnValue('test-key')
84
-
85
- await Commands.auth.action(args)
86
-
87
- expect(mockConnector.call).toHaveBeenCalledWith({
88
- action: 'auth',
89
- data: ['test-key']
90
- })
91
- })
92
-
93
- it('should prompt for key if not provided', async () => {
94
- const args = []
95
- mockCli.parseArg.mockReturnValue(null)
96
- mockCli.question.mockResolvedValue('prompted-key')
97
-
98
- await Commands.auth.action(args)
99
-
100
- expect(mockCli.question).toHaveBeenCalledWith('Enter your authentication key: ')
101
- expect(mockConnector.call).toHaveBeenCalledWith({
102
- action: 'auth',
103
- data: ['prompted-key']
104
- })
105
- })
106
- })
107
-
108
- describe('debug command', () => {
109
- it('should call monitor debug', async () => {
110
- await Commands.debug.action()
111
- expect(mockMonitor.debug).toHaveBeenCalled()
112
- })
113
- })
114
-
115
- describe('help command', () => {
116
- it('should call cli help', async () => {
117
- await Commands.help.action()
118
- expect(mockCli.help).toHaveBeenCalled()
119
- })
120
- })
121
-
122
- describe('monit command', () => {
123
- it('should call monitor monit', async () => {
124
- await Commands.monit.action()
125
- expect(mockMonitor.monit).toHaveBeenCalled()
126
- })
127
- })
128
-
129
- describe('restart command', () => {
130
- it('should call cli boot', async () => {
131
- await Commands.restart.action()
132
- expect(mockCli.boot).toHaveBeenCalled()
133
- })
134
- })
135
-
136
- describe('run command', () => {
137
- it('should handle absolute path', async () => {
138
- const args = ['/absolute/path/service.js']
139
-
140
- await Commands.run.action(args)
141
-
142
- expect(mockConnector.call).toHaveBeenCalledWith({
143
- action: 'service.start',
144
- data: ['/absolute/path/service.js']
145
- })
146
- })
147
-
148
- it('should convert relative path to absolute', async () => {
149
- const args = ['relative/service.js']
150
-
151
- await Commands.run.action(args)
152
-
153
- expect(mockConnector.call).toHaveBeenCalledWith({
154
- action: 'service.start',
155
- data: [expect.stringContaining('relative/service.js')]
156
- })
157
- })
158
- })
159
-
160
- describe('mail commands', () => {
161
- describe('create', () => {
162
- it('should create mail account with provided credentials', async () => {
163
- const args = []
164
- mockCli.parseArg.mockImplementation((args, flags) => {
165
- if (flags.includes('--email')) return 'test@example.com'
166
- if (flags.includes('--password')) return 'password123'
167
- return null
168
- })
169
-
170
- await Commands.mail.sub.create.action(args)
171
-
172
- expect(mockConnector.call).toHaveBeenCalledWith({
173
- action: 'mail.create',
174
- data: ['test@example.com', 'password123', 'password123']
175
- })
176
- })
177
-
178
- it('should prompt for missing credentials', async () => {
179
- const args = []
180
- mockCli.parseArg.mockReturnValue(null)
181
- mockCli.question.mockImplementation(prompt => {
182
- if (prompt.includes('e-mail')) return Promise.resolve('prompted@example.com')
183
- if (prompt.includes('password')) return Promise.resolve('prompted-pass')
184
- return Promise.resolve('')
185
- })
186
-
187
- await Commands.mail.sub.create.action(args)
188
-
189
- expect(mockCli.question).toHaveBeenCalledWith('Enter the e-mail address: ')
190
- expect(mockCli.question).toHaveBeenCalledWith('Enter the password: ')
191
- expect(mockCli.question).toHaveBeenCalledWith('Re-enter the password: ')
192
- })
193
-
194
- it('should prompt for email when not provided via flag', async () => {
195
- const args = []
196
- mockCli.parseArg.mockImplementation((args, flags) => {
197
- if (flags.includes('--password')) return 'password123'
198
- return null
199
- })
200
- mockCli.question.mockResolvedValue('prompted@example.com')
201
-
202
- await Commands.mail.sub.create.action(args)
203
-
204
- expect(mockCli.question).toHaveBeenCalledWith('Enter the e-mail address: ')
205
- })
206
-
207
- it('should prompt for password when not provided via flag', async () => {
208
- const args = []
209
- mockCli.parseArg.mockImplementation((args, flags) => {
210
- if (flags.includes('--email')) return 'test@example.com'
211
- return null
212
- })
213
- mockCli.question.mockImplementation(prompt => {
214
- if (prompt.includes('Enter the password:')) return Promise.resolve('prompted-pass')
215
- if (prompt.includes('Re-enter the password:')) return Promise.resolve('prompted-pass')
216
- return Promise.resolve('')
217
- })
218
-
219
- await Commands.mail.sub.create.action(args)
220
-
221
- expect(mockCli.question).toHaveBeenCalledWith('Enter the password: ')
222
- expect(mockCli.question).toHaveBeenCalledWith('Re-enter the password: ')
223
- })
224
- })
225
-
226
- describe('delete', () => {
227
- it('should delete mail account', async () => {
228
- const args = []
229
- mockCli.parseArg.mockReturnValue('delete@example.com')
230
-
231
- await Commands.mail.sub.delete.action(args)
232
-
233
- expect(mockConnector.call).toHaveBeenCalledWith({
234
- action: 'mail.delete',
235
- data: ['delete@example.com']
236
- })
237
- })
238
-
239
- it('should prompt for email when not provided', async () => {
240
- const args = []
241
- mockCli.parseArg.mockReturnValue(null)
242
- mockCli.question.mockResolvedValue('prompted@example.com')
243
-
244
- await Commands.mail.sub.delete.action(args)
245
-
246
- expect(mockCli.question).toHaveBeenCalledWith('Enter the e-mail address: ')
247
- })
248
- })
249
-
250
- describe('list', () => {
251
- it('should list mail accounts for domain', async () => {
252
- const args = []
253
- mockCli.parseArg.mockReturnValue('example.com')
254
-
255
- await Commands.mail.sub.list.action(args)
256
-
257
- expect(mockConnector.call).toHaveBeenCalledWith({
258
- action: 'mail.list',
259
- data: ['example.com']
260
- })
261
- })
262
-
263
- it('should prompt for domain when not provided', async () => {
264
- const args = []
265
- mockCli.parseArg.mockReturnValue(null)
266
- mockCli.question.mockResolvedValue('example.com')
267
-
268
- await Commands.mail.sub.list.action(args)
269
-
270
- expect(mockCli.question).toHaveBeenCalledWith('Enter the domain name: ')
271
- })
272
- })
273
-
274
- describe('password', () => {
275
- it('should change mail account password with provided credentials', async () => {
276
- const args = []
277
- mockCli.parseArg.mockImplementation((args, flags) => {
278
- if (flags.includes('--email')) return 'test@example.com'
279
- if (flags.includes('--password')) return 'newpass123'
280
- return null
281
- })
282
-
283
- await Commands.mail.sub.password.action(args)
284
-
285
- expect(mockConnector.call).toHaveBeenCalledWith({
286
- action: 'mail.password',
287
- data: ['test@example.com', 'newpass123', 'newpass123']
288
- })
289
- })
290
-
291
- it('should prompt for missing email and password', async () => {
292
- const args = []
293
- mockCli.parseArg.mockReturnValue(null)
294
- mockCli.question.mockImplementation(prompt => {
295
- if (prompt.includes('e-mail')) return Promise.resolve('test@example.com')
296
- if (prompt.includes('new password:')) return Promise.resolve('newpass')
297
- if (prompt.includes('Re-enter')) return Promise.resolve('newpass')
298
- return Promise.resolve('')
299
- })
300
-
301
- await Commands.mail.sub.password.action(args)
302
-
303
- expect(mockCli.question).toHaveBeenCalledWith('Enter the e-mail address: ')
304
- expect(mockCli.question).toHaveBeenCalledWith('Enter the new password: ')
305
- expect(mockCli.question).toHaveBeenCalledWith('Re-enter the new password: ')
306
- })
307
- })
308
- })
309
-
310
- describe('ssl commands', () => {
311
- describe('renew', () => {
312
- it('should renew SSL certificate', async () => {
313
- const args = []
314
- mockCli.parseArg.mockReturnValue('example.com')
315
-
316
- await Commands.ssl.sub.renew.action(args)
317
-
318
- expect(mockConnector.call).toHaveBeenCalledWith({
319
- action: 'ssl.renew',
320
- data: ['example.com']
321
- })
322
- })
323
-
324
- it('should prompt for domain when not provided', async () => {
325
- const args = []
326
- mockCli.parseArg.mockReturnValue(null)
327
- mockCli.question.mockResolvedValue('example.com')
328
-
329
- await Commands.ssl.sub.renew.action(args)
330
-
331
- expect(mockCli.question).toHaveBeenCalledWith('Enter the domain name: ')
332
- })
333
- })
334
- })
335
-
336
- describe('subdomain commands', () => {
337
- describe('create', () => {
338
- it('should create subdomain', async () => {
339
- const args = []
340
- mockCli.parseArg.mockReturnValue('sub.example.com')
341
-
342
- await Commands.subdomain.sub.create.action(args)
343
-
344
- expect(mockConnector.call).toHaveBeenCalledWith({
345
- action: 'subdomain.create',
346
- data: ['sub.example.com']
347
- })
348
- })
349
-
350
- it('should prompt for subdomain when not provided', async () => {
351
- const args = []
352
- mockCli.parseArg.mockReturnValue(null)
353
- mockCli.question.mockResolvedValue('sub.example.com')
354
-
355
- await Commands.subdomain.sub.create.action(args)
356
-
357
- expect(mockCli.question).toHaveBeenCalledWith('Enter the subdomain name (subdomain.example.com): ')
358
- })
359
- })
360
-
361
- describe('delete', () => {
362
- it('should delete subdomain', async () => {
363
- mockCli.question.mockResolvedValue('sub.example.com')
364
-
365
- await Commands.subdomain.sub.delete.action()
366
-
367
- expect(mockCli.question).toHaveBeenCalledWith('Enter the subdomain name (subdomain.example.com): ')
368
- expect(mockConnector.call).toHaveBeenCalledWith({
369
- action: 'subdomain.delete',
370
- data: ['sub.example.com']
371
- })
372
- })
373
-
374
- it('should prompt for subdomain when not provided', async () => {
375
- const args = []
376
- mockCli.parseArg.mockReturnValue(null)
377
- mockCli.question.mockResolvedValue('sub.example.com')
378
-
379
- await Commands.subdomain.sub.delete.action(args)
380
-
381
- expect(mockCli.question).toHaveBeenCalledWith('Enter the subdomain name (subdomain.example.com): ')
382
- })
383
- })
384
-
385
- describe('list', () => {
386
- it('should list subdomains for domain', async () => {
387
- const args = []
388
- mockCli.parseArg.mockReturnValue('example.com')
389
-
390
- await Commands.subdomain.sub.list.action(args)
391
-
392
- expect(mockConnector.call).toHaveBeenCalledWith({
393
- action: 'subdomain.list',
394
- data: ['example.com']
395
- })
396
- })
397
-
398
- it('should prompt for domain when not provided', async () => {
399
- const args = []
400
- mockCli.parseArg.mockReturnValue(null)
401
- mockCli.question.mockResolvedValue('example.com')
402
-
403
- await Commands.subdomain.sub.list.action(args)
404
-
405
- expect(mockCli.question).toHaveBeenCalledWith('Enter the domain name: ')
406
- })
407
- })
408
- })
409
-
410
- describe('web commands', () => {
411
- describe('create', () => {
412
- it('should create website', async () => {
413
- const args = []
414
- mockCli.parseArg.mockReturnValue('newsite.com')
415
-
416
- await Commands.web.sub.create.action(args)
417
-
418
- expect(mockConnector.call).toHaveBeenCalledWith({
419
- action: 'web.create',
420
- data: ['newsite.com']
421
- })
422
- })
423
-
424
- it('should prompt for domain when not provided', async () => {
425
- const args = []
426
- mockCli.parseArg.mockReturnValue(null)
427
- mockCli.question.mockResolvedValue('newsite.com')
428
-
429
- await Commands.web.sub.create.action(args)
430
-
431
- expect(mockCli.question).toHaveBeenCalledWith('Enter the domain name: ')
432
- })
433
- })
434
-
435
- describe('delete', () => {
436
- it('should delete website', async () => {
437
- const args = []
438
- mockCli.parseArg.mockReturnValue('oldsite.com')
439
-
440
- await Commands.web.sub.delete.action(args)
441
-
442
- expect(mockConnector.call).toHaveBeenCalledWith({
443
- action: 'web.delete',
444
- data: ['oldsite.com']
445
- })
446
- })
447
-
448
- it('should prompt for domain when not provided', async () => {
449
- const args = []
450
- mockCli.parseArg.mockReturnValue(null)
451
- mockCli.question.mockResolvedValue('oldsite.com')
452
-
453
- await Commands.web.sub.delete.action(args)
454
-
455
- expect(mockCli.question).toHaveBeenCalledWith('Enter the domain name: ')
456
- })
457
- })
458
-
459
- describe('list', () => {
460
- it('should list all websites', async () => {
461
- await Commands.web.sub.list.action()
462
-
463
- expect(mockConnector.call).toHaveBeenCalledWith({
464
- action: 'web.list'
465
- })
466
- })
467
- })
468
- })
469
-
470
- describe('command structure validation', () => {
471
- it('should have correct mail command structure', () => {
472
- expect(Commands.mail.title).toBe('MAIL')
473
- expect(Commands.mail.sub).toBeDefined()
474
- expect(Commands.mail.sub.create).toBeDefined()
475
- expect(Commands.mail.sub.delete).toBeDefined()
476
- expect(Commands.mail.sub.list).toBeDefined()
477
- expect(Commands.mail.sub.password).toBeDefined()
478
- })
479
-
480
- it('should have correct ssl command structure', () => {
481
- expect(Commands.ssl.title).toBe('SSL')
482
- expect(Commands.ssl.sub).toBeDefined()
483
- expect(Commands.ssl.sub.renew).toBeDefined()
484
- })
485
-
486
- it('should have correct subdomain command structure', () => {
487
- expect(Commands.subdomain.title).toBe('SUBDOMAIN')
488
- expect(Commands.subdomain.sub).toBeDefined()
489
- expect(Commands.subdomain.sub.create).toBeDefined()
490
- expect(Commands.subdomain.sub.delete).toBeDefined()
491
- expect(Commands.subdomain.sub.list).toBeDefined()
492
- })
493
-
494
- it('should have correct web command structure', () => {
495
- expect(Commands.web.title).toBe('WEBSITE')
496
- expect(Commands.web.sub).toBeDefined()
497
- expect(Commands.web.sub.create).toBeDefined()
498
- expect(Commands.web.sub.delete).toBeDefined()
499
- expect(Commands.web.sub.list).toBeDefined()
500
- })
501
- })
502
-
503
- describe('auth command edge cases', () => {
504
- it('should use key from args[0] when parseArg returns null', async () => {
505
- const args = ['direct-key']
506
- mockCli.parseArg.mockReturnValue(null)
507
-
508
- await Commands.auth.action(args)
509
-
510
- expect(mockConnector.call).toHaveBeenCalledWith({
511
- action: 'auth',
512
- data: ['direct-key']
513
- })
514
- })
515
-
516
- it('should handle Windows path format in run command', async () => {
517
- const args = ['C:\\Windows\\service.js']
518
-
519
- await Commands.run.action(args)
520
-
521
- expect(mockConnector.call).toHaveBeenCalledWith({
522
- action: 'service.start',
523
- data: ['C:\\Windows\\service.js']
524
- })
525
- })
526
-
527
- it('should handle UNC path format in run command', async () => {
528
- const args = ['\\\\server\\share\\service.js']
529
-
530
- await Commands.run.action(args)
531
-
532
- expect(mockConnector.call).toHaveBeenCalledWith({
533
- action: 'service.start',
534
- data: ['\\\\server\\share\\service.js']
535
- })
536
- })
537
- })
538
- })