odac 1.1.0 → 1.3.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 (117) hide show
  1. package/.agent/rules/coding.md +27 -0
  2. package/.agent/rules/memory.md +42 -0
  3. package/.agent/rules/project.md +30 -0
  4. package/.agent/rules/workflow.md +16 -0
  5. package/.github/workflows/release.yml +68 -1
  6. package/.github/workflows/test-coverage.yml +6 -5
  7. package/.husky/pre-commit +10 -0
  8. package/.husky/pre-push +13 -0
  9. package/.releaserc.js +1 -1
  10. package/CHANGELOG.md +99 -0
  11. package/README.md +16 -0
  12. package/bin/odac.js +196 -38
  13. package/client/odac.js +238 -171
  14. package/docs/backend/01-overview/03-development-server.md +38 -45
  15. package/docs/backend/02-structure/01-typical-project-layout.md +59 -26
  16. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  17. package/docs/backend/03-config/01-database-connection.md +2 -2
  18. package/docs/backend/03-config/02-static-route-mapping-optional.md +1 -1
  19. package/docs/backend/03-config/03-request-timeout.md +1 -1
  20. package/docs/backend/03-config/04-environment-variables.md +4 -4
  21. package/docs/backend/03-config/05-early-hints.md +2 -2
  22. package/docs/backend/04-routing/03-api-and-data-routes.md +18 -0
  23. package/docs/backend/04-routing/07-cron-jobs.md +17 -1
  24. package/docs/backend/04-routing/09-websocket.md +14 -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 +18 -14
  38. package/src/Auth.js +58 -23
  39. package/src/Config.js +7 -7
  40. package/src/Database.js +1 -1
  41. package/src/Env.js +3 -1
  42. package/src/Ipc.js +7 -0
  43. package/src/Lang.js +9 -2
  44. package/src/Mail.js +19 -9
  45. package/src/Odac.js +56 -44
  46. package/src/Request.js +6 -2
  47. package/src/Route/Cron.js +58 -17
  48. package/src/Route/Internal.js +1 -1
  49. package/src/Route.js +375 -125
  50. package/src/Server.js +40 -3
  51. package/src/Storage.js +4 -0
  52. package/src/Token.js +6 -4
  53. package/src/Validator.js +24 -15
  54. package/src/Var.js +22 -6
  55. package/src/View/EarlyHints.js +43 -33
  56. package/src/View/Form.js +17 -11
  57. package/src/View.js +68 -12
  58. package/src/WebSocket.js +45 -12
  59. package/template/package.json +3 -1
  60. package/template/view/content/home.html +3 -3
  61. package/template/view/head/main.html +2 -2
  62. package/test/Client.test.js +168 -0
  63. package/test/Config.test.js +112 -0
  64. package/test/Lang.test.js +92 -0
  65. package/test/Odac.test.js +88 -0
  66. package/test/{framework/middleware.test.js → Route/Middleware.test.js} +2 -2
  67. package/test/{framework/Route.test.js → Route.test.js} +105 -1
  68. package/test/{framework/View → View}/EarlyHints.test.js +1 -1
  69. package/test/WebSocket.test.js +238 -0
  70. package/test/scripts/check-coverage.js +4 -4
  71. package/test/cli/Cli.test.js +0 -36
  72. package/test/core/Commands.test.js +0 -538
  73. package/test/core/Config.test.js +0 -1432
  74. package/test/core/Lang.test.js +0 -250
  75. package/test/core/Odac.test.js +0 -234
  76. package/test/core/Process.test.js +0 -156
  77. package/test/framework/WebSocket.test.js +0 -100
  78. package/test/server/Api.test.js +0 -647
  79. package/test/server/DNS.test.js +0 -2050
  80. package/test/server/DNS.test.js.bak +0 -2084
  81. package/test/server/Hub.test.js +0 -497
  82. package/test/server/Log.test.js +0 -73
  83. package/test/server/Mail.account.test_.js +0 -460
  84. package/test/server/Mail.init.test_.js +0 -411
  85. package/test/server/Mail.test_.js +0 -1340
  86. package/test/server/SSL.test_.js +0 -1491
  87. package/test/server/Server.test.js +0 -765
  88. package/test/server/Service.test_.js +0 -1127
  89. package/test/server/Subdomain.test.js +0 -440
  90. package/test/server/Web/Firewall.test.js +0 -175
  91. package/test/server/Web/Proxy.test.js +0 -397
  92. package/test/server/Web.test.js +0 -1494
  93. package/test/server/__mocks__/acme-client.js +0 -17
  94. package/test/server/__mocks__/bcrypt.js +0 -50
  95. package/test/server/__mocks__/child_process.js +0 -389
  96. package/test/server/__mocks__/crypto.js +0 -432
  97. package/test/server/__mocks__/fs.js +0 -450
  98. package/test/server/__mocks__/globalOdac.js +0 -227
  99. package/test/server/__mocks__/http.js +0 -575
  100. package/test/server/__mocks__/https.js +0 -272
  101. package/test/server/__mocks__/index.js +0 -249
  102. package/test/server/__mocks__/mail/server.js +0 -100
  103. package/test/server/__mocks__/mail/smtp.js +0 -31
  104. package/test/server/__mocks__/mailparser.js +0 -81
  105. package/test/server/__mocks__/net.js +0 -369
  106. package/test/server/__mocks__/node-forge.js +0 -328
  107. package/test/server/__mocks__/os.js +0 -320
  108. package/test/server/__mocks__/path.js +0 -291
  109. package/test/server/__mocks__/selfsigned.js +0 -8
  110. package/test/server/__mocks__/server/src/mail/server.js +0 -100
  111. package/test/server/__mocks__/server/src/mail/smtp.js +0 -31
  112. package/test/server/__mocks__/smtp-server.js +0 -106
  113. package/test/server/__mocks__/sqlite3.js +0 -394
  114. package/test/server/__mocks__/testFactories.js +0 -299
  115. package/test/server/__mocks__/testHelpers.js +0 -363
  116. package/test/server/__mocks__/tls.js +0 -229
  117. /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
- })