@heyputer/shell 2.1.0 → 3.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/npm-build.yml +0 -2
- package/.github/workflows/npm-publish.yml +0 -4
- package/bin/index.js +22 -202
- package/package.json +3 -5
- package/src/commands/auth.js +7 -12
- package/src/commands/files.js +16 -18
- package/src/commands/shell.js +5 -3
- package/src/commons.js +81 -256
- package/src/executor.js +12 -129
- package/src/modules/ErrorModule.js +62 -1
- package/src/modules/ProfileModule.js +154 -38
- package/src/utils.js +0 -91
- package/tests/ErrorModule.test.js +73 -1
- package/tests/ProfileModule.test.js +58 -44
- package/tests/commons.test.js +66 -124
- package/tests/executor.test.js +8 -0
- package/tests/files.test.js +23 -3
- package/tests/shell.test.js +2 -1
- package/tests/utils.test.js +1 -141
- package/src/commands/apps.js +0 -356
- package/src/commands/deploy.js +0 -54
- package/src/commands/init.js +0 -322
- package/src/commands/sites.js +0 -169
- package/src/commands/subdomains.js +0 -95
- package/tests/apps.test.js +0 -194
- package/tests/deploy.test.js +0 -84
- package/tests/sites.test.js +0 -67
- package/tests/subdomains.test.js +0 -90
package/tests/commons.test.js
CHANGED
|
@@ -29,11 +29,7 @@ beforeEach(async () => {
|
|
|
29
29
|
|
|
30
30
|
describe('constants', () => {
|
|
31
31
|
it('should export PROJECT_NAME', () => {
|
|
32
|
-
expect(commons.PROJECT_NAME).toBe('puter-
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should export NULL_UUID', () => {
|
|
36
|
-
expect(commons.NULL_UUID).toBe('00000000-0000-0000-0000-000000000000');
|
|
32
|
+
expect(commons.PROJECT_NAME).toBe('puter-sh');
|
|
37
33
|
});
|
|
38
34
|
|
|
39
35
|
it('should have default API_BASE', () => {
|
|
@@ -86,65 +82,6 @@ describe('getHeaders', () => {
|
|
|
86
82
|
});
|
|
87
83
|
});
|
|
88
84
|
|
|
89
|
-
describe('generateAppName', () => {
|
|
90
|
-
it('should generate a name with default separator', () => {
|
|
91
|
-
const name = commons.generateAppName();
|
|
92
|
-
|
|
93
|
-
expect(name).toMatch(/^[a-z]+-[a-z]+-\d+$/);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should generate a name with custom separator', () => {
|
|
97
|
-
const name = commons.generateAppName('_');
|
|
98
|
-
|
|
99
|
-
expect(name).toMatch(/^[a-z]+_[a-z]+_\d+$/);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('displayTable', () => {
|
|
104
|
-
it('should display a table with headers and data', () => {
|
|
105
|
-
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
106
|
-
|
|
107
|
-
const data = [
|
|
108
|
-
{ name: 'App1', status: 'running' },
|
|
109
|
-
{ name: 'App2', status: 'stopped' },
|
|
110
|
-
];
|
|
111
|
-
|
|
112
|
-
commons.displayTable(data, {
|
|
113
|
-
headers: ['Name', 'Status'],
|
|
114
|
-
columns: ['name', 'status'],
|
|
115
|
-
columnWidth: 15,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
expect(consoleLogSpy).toHaveBeenCalled();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should handle empty data', () => {
|
|
122
|
-
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
123
|
-
|
|
124
|
-
commons.displayTable([], {
|
|
125
|
-
headers: ['Name'],
|
|
126
|
-
columns: ['name'],
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
expect(consoleLogSpy).toHaveBeenCalledTimes(2); // header + separator
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should display N/A for missing values', () => {
|
|
133
|
-
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
134
|
-
|
|
135
|
-
const data = [{ name: 'App1' }];
|
|
136
|
-
|
|
137
|
-
commons.displayTable(data, {
|
|
138
|
-
headers: ['Name', 'Status'],
|
|
139
|
-
columns: ['name', 'status'],
|
|
140
|
-
columnWidth: 10,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const calls = consoleLogSpy.mock.calls.flat();
|
|
144
|
-
expect(calls.some(call => call.includes('N/A'))).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
85
|
describe('showDiskSpaceUsage', () => {
|
|
149
86
|
it('should display disk usage information', () => {
|
|
150
87
|
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
@@ -209,94 +146,99 @@ describe('resolvePath', () => {
|
|
|
209
146
|
it('should normalize duplicate slashes', () => {
|
|
210
147
|
expect(commons.resolvePath('/home//user', 'documents')).toBe('/home/user/documents');
|
|
211
148
|
});
|
|
212
|
-
});
|
|
213
149
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
expect(commons.resolveRemotePath('/home/user', '/absolute/path')).toBe('/absolute/path');
|
|
150
|
+
it('should re-root on an absolute path instead of appending it', () => {
|
|
151
|
+
expect(commons.resolvePath('/home/user', '/other/place')).toBe('/other/place');
|
|
217
152
|
});
|
|
218
153
|
|
|
219
|
-
it('should
|
|
220
|
-
|
|
154
|
+
it('should leave "~" unexpanded when no home is resolved yet', () => {
|
|
155
|
+
commons.setHomePath(null);
|
|
156
|
+
expect(commons.resolvePath('/home/user', '~/Desktop')).toBe('~/Desktop');
|
|
157
|
+
expect(commons.resolvePath('/home/user', '~')).toBe('~');
|
|
221
158
|
});
|
|
222
|
-
});
|
|
223
159
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
expect(commons.
|
|
160
|
+
it('should walk up within a home-anchored path', () => {
|
|
161
|
+
commons.setHomePath(null);
|
|
162
|
+
expect(commons.resolvePath('~/Desktop/notes', '..')).toBe('~/Desktop');
|
|
227
163
|
});
|
|
228
164
|
|
|
229
|
-
it('should
|
|
230
|
-
|
|
165
|
+
it('should clamp at the home anchor rather than escaping to root', () => {
|
|
166
|
+
commons.setHomePath(null);
|
|
167
|
+
expect(commons.resolvePath('~/Desktop', '../../..')).toBe('~');
|
|
231
168
|
});
|
|
169
|
+
});
|
|
232
170
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
});
|
|
171
|
+
describe('expandHome', () => {
|
|
172
|
+
afterEach(() => commons.setHomePath(null));
|
|
236
173
|
|
|
237
|
-
it('should
|
|
238
|
-
|
|
174
|
+
it('should expand "~" and "~/..." to the resolved home', () => {
|
|
175
|
+
commons.setHomePath('/alice');
|
|
176
|
+
expect(commons.expandHome('~')).toBe('/alice');
|
|
177
|
+
expect(commons.expandHome('~/Desktop')).toBe('/alice/Desktop');
|
|
239
178
|
});
|
|
240
179
|
|
|
241
|
-
it('should
|
|
242
|
-
|
|
180
|
+
it('should leave non-home paths untouched', () => {
|
|
181
|
+
commons.setHomePath('/alice');
|
|
182
|
+
expect(commons.expandHome('/bob/public')).toBe('/bob/public');
|
|
183
|
+
expect(commons.expandHome('notes.txt')).toBe('notes.txt');
|
|
243
184
|
});
|
|
244
185
|
|
|
245
|
-
it('should
|
|
246
|
-
|
|
186
|
+
it('should be a no-op before a home is resolved', () => {
|
|
187
|
+
commons.setHomePath(null);
|
|
188
|
+
expect(commons.expandHome('~/Desktop')).toBe('~/Desktop');
|
|
247
189
|
});
|
|
248
190
|
|
|
249
|
-
it('should
|
|
250
|
-
|
|
191
|
+
it('should follow a username change without touching stored paths', () => {
|
|
192
|
+
commons.setHomePath('/alice');
|
|
193
|
+
expect(commons.expandHome('~/Desktop')).toBe('/alice/Desktop');
|
|
194
|
+
commons.setHomePath('/alice-renamed');
|
|
195
|
+
expect(commons.expandHome('~/Desktop')).toBe('/alice-renamed/Desktop');
|
|
251
196
|
});
|
|
197
|
+
});
|
|
252
198
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
});
|
|
199
|
+
describe('resolvePath with a resolved home', () => {
|
|
200
|
+
afterEach(() => commons.setHomePath(null));
|
|
256
201
|
|
|
257
|
-
it('should
|
|
258
|
-
|
|
202
|
+
it('should resolve "~" to the concrete home directory', () => {
|
|
203
|
+
commons.setHomePath('/alice');
|
|
204
|
+
expect(commons.resolvePath('/anywhere', '~')).toBe('/alice');
|
|
205
|
+
expect(commons.resolvePath('/anywhere', '~/Desktop')).toBe('/alice/Desktop');
|
|
259
206
|
});
|
|
260
207
|
|
|
261
|
-
it('should
|
|
262
|
-
|
|
263
|
-
expect(commons.
|
|
264
|
-
expect(commons.
|
|
208
|
+
it('should resolve relative paths against a concrete cwd', () => {
|
|
209
|
+
commons.setHomePath('/alice');
|
|
210
|
+
expect(commons.resolvePath('/alice', 'Desktop')).toBe('/alice/Desktop');
|
|
211
|
+
expect(commons.resolvePath('/alice/Desktop', '..')).toBe('/alice');
|
|
265
212
|
});
|
|
266
213
|
});
|
|
267
214
|
|
|
268
|
-
describe('
|
|
269
|
-
it('should
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
expect(
|
|
273
|
-
expect(html).toContain('Welcome to TestApp!');
|
|
215
|
+
describe('isAbsolutePath', () => {
|
|
216
|
+
it('should accept root- and home-anchored paths', () => {
|
|
217
|
+
expect(commons.isAbsolutePath('/a/b')).toBe(true);
|
|
218
|
+
expect(commons.isAbsolutePath('~')).toBe(true);
|
|
219
|
+
expect(commons.isAbsolutePath('~/a')).toBe(true);
|
|
274
220
|
});
|
|
275
221
|
|
|
276
|
-
it('should
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
expect(
|
|
280
|
-
expect(html).toContain('<link href="theme.css" rel="stylesheet">');
|
|
222
|
+
it('should reject relative paths and non-strings', () => {
|
|
223
|
+
expect(commons.isAbsolutePath('a/b')).toBe(false);
|
|
224
|
+
expect(commons.isAbsolutePath('..')).toBe(false);
|
|
225
|
+
expect(commons.isAbsolutePath(undefined)).toBe(false);
|
|
281
226
|
});
|
|
227
|
+
});
|
|
282
228
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
expect(html).toContain('<script type="text/babel" src="app.js"></script>');
|
|
287
|
-
expect(html).toContain('<script src="utils.js"></script>');
|
|
229
|
+
describe('resolveRemotePath', () => {
|
|
230
|
+
it('should return absolute path as-is', () => {
|
|
231
|
+
expect(commons.resolveRemotePath('/home/user', '/absolute/path')).toBe('/absolute/path');
|
|
288
232
|
});
|
|
289
233
|
|
|
290
|
-
it('should
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
expect(html).toContain('id="root"');
|
|
234
|
+
it('should resolve relative path', () => {
|
|
235
|
+
expect(commons.resolveRemotePath('/home/user', 'relative/path')).toBe('/home/user/relative/path');
|
|
294
236
|
});
|
|
295
237
|
|
|
296
|
-
it('should
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
238
|
+
it('should expand a home-anchored path against the resolved home', () => {
|
|
239
|
+
commons.setHomePath('/alice');
|
|
240
|
+
expect(commons.resolveRemotePath('/home/user', '~/site')).toBe('/alice/site');
|
|
241
|
+
commons.setHomePath(null);
|
|
300
242
|
});
|
|
301
243
|
});
|
|
302
244
|
|
|
@@ -340,7 +282,7 @@ describe('getLatestVersion', () => {
|
|
|
340
282
|
json: () => Promise.resolve({ version: '1.0.0' }),
|
|
341
283
|
});
|
|
342
284
|
|
|
343
|
-
const result = await commons.getLatestVersion('puter-
|
|
285
|
+
const result = await commons.getLatestVersion('puter-sh');
|
|
344
286
|
|
|
345
287
|
expect(result).toBe('v1.0.0 (up-to-date)');
|
|
346
288
|
});
|
|
@@ -352,7 +294,7 @@ describe('getLatestVersion', () => {
|
|
|
352
294
|
json: () => Promise.resolve({ version: '2.0.0' }),
|
|
353
295
|
});
|
|
354
296
|
|
|
355
|
-
const result = await commons.getLatestVersion('puter-
|
|
297
|
+
const result = await commons.getLatestVersion('puter-sh');
|
|
356
298
|
|
|
357
299
|
expect(result).toBe('v1.0.0 (latest: 2.0.0)');
|
|
358
300
|
});
|
|
@@ -361,7 +303,7 @@ describe('getLatestVersion', () => {
|
|
|
361
303
|
vi.mocked(readFile).mockResolvedValueOnce(JSON.stringify({ version: '1.0.0' }));
|
|
362
304
|
vi.mocked(global.fetch).mockRejectedValueOnce(new Error('Network error'));
|
|
363
305
|
|
|
364
|
-
const result = await commons.getLatestVersion('puter-
|
|
306
|
+
const result = await commons.getLatestVersion('puter-sh');
|
|
365
307
|
|
|
366
308
|
expect(result).toBe('v1.0.0 (offline)');
|
|
367
309
|
});
|
|
@@ -373,7 +315,7 @@ describe('getLatestVersion', () => {
|
|
|
373
315
|
json: () => Promise.resolve({ version: '2.0.0' }),
|
|
374
316
|
});
|
|
375
317
|
|
|
376
|
-
const result = await commons.getLatestVersion('puter-
|
|
318
|
+
const result = await commons.getLatestVersion('puter-sh');
|
|
377
319
|
|
|
378
320
|
expect(result).toBe('vunknown (latest: 2.0.0)');
|
|
379
321
|
});
|
package/tests/executor.test.js
CHANGED
|
@@ -11,6 +11,14 @@ vi.mock('conf', () => ({
|
|
|
11
11
|
|
|
12
12
|
vi.mock('node:child_process');
|
|
13
13
|
|
|
14
|
+
vi.mock('../src/modules/ProfileModule.js', () => ({
|
|
15
|
+
initProfileModule: vi.fn(),
|
|
16
|
+
getProfileModule: vi.fn(() => ({
|
|
17
|
+
getCwd: () => '/mockuser',
|
|
18
|
+
setCwd: vi.fn(),
|
|
19
|
+
})),
|
|
20
|
+
}));
|
|
21
|
+
|
|
14
22
|
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
15
23
|
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
16
24
|
|
package/tests/files.test.js
CHANGED
|
@@ -459,6 +459,9 @@ describe("renameFileOrDirectory", () => {
|
|
|
459
459
|
describe("getInfo", () => {
|
|
460
460
|
beforeEach(() => {
|
|
461
461
|
vi.clearAllMocks();
|
|
462
|
+
// Undo the naive resolvePath stub other suites install: getInfo relies on
|
|
463
|
+
// real "~" and absolute-path resolution.
|
|
464
|
+
if (vi.isMockFunction(commons.resolvePath)) commons.resolvePath.mockRestore();
|
|
462
465
|
vi.spyOn(PuterModule, "getPuter").mockReturnValue(mockPuter);
|
|
463
466
|
vi.spyOn(auth, "getCurrentDirectory").mockReturnValue("/testuser/files");
|
|
464
467
|
vi.spyOn(utils, "formatSize").mockImplementation((size) => `${size}B`);
|
|
@@ -515,6 +518,23 @@ describe("getInfo", () => {
|
|
|
515
518
|
);
|
|
516
519
|
});
|
|
517
520
|
|
|
521
|
+
it("should resolve the argument instead of concatenating it", async () => {
|
|
522
|
+
mockPuter.fs.stat.mockResolvedValue({
|
|
523
|
+
name: "files",
|
|
524
|
+
path: "/testuser/files",
|
|
525
|
+
is_dir: true,
|
|
526
|
+
owner: { username: "testuser" },
|
|
527
|
+
});
|
|
528
|
+
// Raw concatenation used to produce "/testuser/files/~/Desktop"; delegating
|
|
529
|
+
// to resolvePath is what makes "~" and absolute paths work here.
|
|
530
|
+
vi.spyOn(commons, "resolvePath").mockReturnValue("/resolved/path");
|
|
531
|
+
|
|
532
|
+
await getInfo(["~/Desktop"]);
|
|
533
|
+
|
|
534
|
+
expect(commons.resolvePath).toHaveBeenCalledWith("/testuser/files", "~/Desktop");
|
|
535
|
+
expect(mockPuter.fs.stat).toHaveBeenCalledWith("/resolved/path");
|
|
536
|
+
});
|
|
537
|
+
|
|
518
538
|
it("should use current directory with default argument", async () => {
|
|
519
539
|
mockPuter.fs.stat.mockResolvedValue({
|
|
520
540
|
name: "files",
|
|
@@ -529,7 +549,7 @@ describe("getInfo", () => {
|
|
|
529
549
|
|
|
530
550
|
await getInfo([]);
|
|
531
551
|
|
|
532
|
-
expect(mockPuter.fs.stat).toHaveBeenCalledWith("/testuser/files
|
|
552
|
+
expect(mockPuter.fs.stat).toHaveBeenCalledWith("/testuser/files");
|
|
533
553
|
});
|
|
534
554
|
});
|
|
535
555
|
|
|
@@ -538,8 +558,8 @@ describe("showCwd", () => {
|
|
|
538
558
|
vi.clearAllMocks();
|
|
539
559
|
});
|
|
540
560
|
|
|
541
|
-
it("should display
|
|
542
|
-
|
|
561
|
+
it("should display the selected profile's working directory", async () => {
|
|
562
|
+
vi.spyOn(auth, "getCurrentDirectory").mockReturnValue("/testuser/documents");
|
|
543
563
|
|
|
544
564
|
await showCwd();
|
|
545
565
|
|
package/tests/shell.test.js
CHANGED
|
@@ -27,6 +27,7 @@ beforeEach(async () => {
|
|
|
27
27
|
vi.mocked(getPrompt).mockReturnValue('puter@/> ');
|
|
28
28
|
vi.mocked(getProfileModule).mockReturnValue({
|
|
29
29
|
checkLogin: vi.fn(),
|
|
30
|
+
setCwd: vi.fn(),
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
const mockOn = vi.fn().mockReturnThis();
|
|
@@ -97,7 +98,7 @@ describe('startShell', () => {
|
|
|
97
98
|
|
|
98
99
|
it('should display welcome message', async () => {
|
|
99
100
|
await startShell();
|
|
100
|
-
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Welcome to Puter
|
|
101
|
+
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Welcome to Puter Shell'));
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
it('should set prompt and call prompt()', async () => {
|
package/tests/utils.test.js
CHANGED
|
@@ -1,30 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
describe('formatDate', () => {
|
|
5
|
-
it('should format a date string correctly', () => {
|
|
6
|
-
const dateString = '2024-10-07T15:03:53.000Z';
|
|
7
|
-
const expected = '10/07/2024, 15:03:53';
|
|
8
|
-
expect(formatDate(dateString)).toBe(expected);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should format a date object correctly', () => {
|
|
12
|
-
const dateObject = new Date(Date.UTC(2024, 9, 7, 15, 3, 53)); // Month is 0-indexed
|
|
13
|
-
const expected = '10/07/2024, 15:03:53';
|
|
14
|
-
expect(formatDate(dateObject)).toBe(expected);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should handle different date and time', () => {
|
|
18
|
-
const dateString = '2023-01-01T01:30:05.000Z';
|
|
19
|
-
const expected = '01/01/2023, 01:30:05';
|
|
20
|
-
expect(formatDate(dateString)).toBe(expected);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should handle invalid date', () => {
|
|
24
|
-
const dateString = 'invalid-date';
|
|
25
|
-
expect(formatDate(dateString)).toBe('Invalid Date');
|
|
26
|
-
});
|
|
27
|
-
});
|
|
2
|
+
import { formatDateTime, formatSize } from '../src/utils.js';
|
|
28
3
|
|
|
29
4
|
describe('formatDateTime', () => {
|
|
30
5
|
it('should format as time if within 24 hours', () => {
|
|
@@ -76,118 +51,3 @@ describe('formatSize', () => {
|
|
|
76
51
|
expect(formatSize(undefined)).toBe('0');
|
|
77
52
|
});
|
|
78
53
|
});
|
|
79
|
-
|
|
80
|
-
describe('displayNonNullValues', () => {
|
|
81
|
-
it('should display non-null values in a formatted table', () => {
|
|
82
|
-
const data = {
|
|
83
|
-
name: 'John Doe',
|
|
84
|
-
age: 30,
|
|
85
|
-
address: {
|
|
86
|
-
street: '123 Main St',
|
|
87
|
-
city: 'Anytown',
|
|
88
|
-
zip: null
|
|
89
|
-
},
|
|
90
|
-
email: null
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
94
|
-
displayNonNullValues(data);
|
|
95
|
-
expect(consoleLogSpy).toHaveBeenCalled();
|
|
96
|
-
consoleLogSpy.mockRestore();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should handle empty object', () => {
|
|
100
|
-
const data = {};
|
|
101
|
-
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
102
|
-
displayNonNullValues(data);
|
|
103
|
-
expect(consoleLogSpy).toHaveBeenCalled();
|
|
104
|
-
consoleLogSpy.mockRestore();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should handle nested objects with all null values', () => {
|
|
108
|
-
const data = { a: null, b: { c: null, d: null } };
|
|
109
|
-
const consoleLogSpy = vi.spyOn(console, 'log');
|
|
110
|
-
displayNonNullValues(data);
|
|
111
|
-
expect(consoleLogSpy).toHaveBeenCalledTimes(5);
|
|
112
|
-
consoleLogSpy.mockRestore();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should handle non-object input', () => {
|
|
116
|
-
const data = "not an object";
|
|
117
|
-
const consoleErrorSpy = vi.spyOn(console, 'error');
|
|
118
|
-
displayNonNullValues(data);
|
|
119
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith("Invalid input: Input must be a non-null object.");
|
|
120
|
-
consoleErrorSpy.mockRestore();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe('parseArgs', () => {
|
|
125
|
-
it('should parse simple arguments', () => {
|
|
126
|
-
const input = 'command --arg1 val1 --arg2 val2';
|
|
127
|
-
const expected = { _: ['command'], arg1: 'val1', arg2: 'val2' };
|
|
128
|
-
expect(parseArgs(input)).toEqual(expect.objectContaining(expected));
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should parse command line arguments with different types', () => {
|
|
132
|
-
const input = 'command --name="John Doe" --age=30';
|
|
133
|
-
const result = parseArgs(input);
|
|
134
|
-
expect(result).toEqual({ _: ['command'], name: 'John Doe', age: 30 });
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('should parse quoted arguments', () => {
|
|
138
|
-
const input = 'command --arg "quoted value"';
|
|
139
|
-
const expected = { _: ['command'], arg: 'quoted value' };
|
|
140
|
-
expect(parseArgs(input)).toEqual(expect.objectContaining(expected));
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should parse arguments with equals sign', () => {
|
|
144
|
-
const input = 'command --arg1=val1 --arg2=val2';
|
|
145
|
-
const expected = { _: ['command'], arg1: 'val1', arg2: 'val2' };
|
|
146
|
-
expect(parseArgs(input)).toEqual(expect.objectContaining(expected));
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should handle empty input', () => {
|
|
150
|
-
const result = parseArgs('');
|
|
151
|
-
expect(result).toEqual({ _: []});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('should parse empty arguments', () => {
|
|
155
|
-
const input = '';
|
|
156
|
-
const expected = { _: [] };
|
|
157
|
-
expect(parseArgs(input)).toEqual(expect.objectContaining(expected));
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe('isValidAppUuid', () => {
|
|
162
|
-
it('should return true for a valid app UUID', () => {
|
|
163
|
-
const uuid = 'app-a1b2c3d4-e5f6-4789-8abc-def012345678';
|
|
164
|
-
expect(isValidAppUuid(uuid)).toBe(true);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should return false if UUID does not start with "app-"', () => {
|
|
168
|
-
const uuid = 'a1b2c3d4-e5f6-4789-8abc-def012345678';
|
|
169
|
-
expect(isValidAppUuid(uuid)).toBe(false);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should return false for an invalid UUID after "app-"', () => {
|
|
173
|
-
const uuid = 'app-invalid-uuid';
|
|
174
|
-
expect(isValidAppUuid(uuid)).toBe(false);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('is_valid_uuid4', () => {
|
|
179
|
-
it('should return true for a valid UUID v4', () => {
|
|
180
|
-
const uuid = 'a1b2c3d4-e5f6-4789-8abc-def012345678';
|
|
181
|
-
expect(is_valid_uuid4(uuid)).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should return false for an invalid UUID v4', () => {
|
|
185
|
-
const uuid = 'a1b2c3d4-e5f6-5789-8abc-def012345678'; // Invalid version
|
|
186
|
-
expect(is_valid_uuid4(uuid)).toBe(false);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should return false for a completely invalid UUID', () => {
|
|
190
|
-
const uuid = 'invalid-uuid';
|
|
191
|
-
expect(is_valid_uuid4(uuid)).toBe(false);
|
|
192
|
-
});
|
|
193
|
-
});
|