@nestbox-ai/cli 1.0.19 → 1.0.21

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.
@@ -0,0 +1,135 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { Command } from 'commander';
3
+ import { registerComputeProgram } from '../src/commands/compute';
4
+
5
+ describe('Compute Commands', () => {
6
+ let program: Command;
7
+
8
+ beforeEach(() => {
9
+ program = new Command();
10
+ vi.clearAllMocks();
11
+ });
12
+
13
+ describe('registerComputeProgram', () => {
14
+ it('should register compute command group', () => {
15
+ registerComputeProgram(program);
16
+
17
+ const commands = program.commands;
18
+ const computeCommand = commands.find(cmd => cmd.name() === 'compute');
19
+
20
+ expect(computeCommand).toBeDefined();
21
+ expect(computeCommand?.description()).toBe('Manage Nestbox computes');
22
+ });
23
+
24
+ it('should register compute list subcommand', () => {
25
+ registerComputeProgram(program);
26
+
27
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
28
+ const subCommands = computeCommand?.commands || [];
29
+ const listCommand = subCommands.find(cmd => cmd.name() === 'list');
30
+
31
+ expect(listCommand).toBeDefined();
32
+ expect(listCommand?.description()).toBe('List all compute instances');
33
+
34
+ // Check options
35
+ const options = listCommand?.options || [];
36
+ const projectOption = options.find(opt => opt.long === '--project');
37
+ expect(projectOption).toBeDefined();
38
+ expect(projectOption?.description).toBe('Project ID or name (defaults to the current project)');
39
+ });
40
+
41
+ it('should register compute create subcommand', () => {
42
+ registerComputeProgram(program);
43
+
44
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
45
+ const subCommands = computeCommand?.commands || [];
46
+ const createCommand = subCommands.find(cmd => cmd.name() === 'create');
47
+
48
+ expect(createCommand).toBeDefined();
49
+ expect(createCommand?.description()).toBe('Create a new compute instance');
50
+
51
+ // Check options
52
+ const options = createCommand?.options || [];
53
+ const projectOption = options.find(opt => opt.long === '--project');
54
+ const imageOption = options.find(opt => opt.long === '--image');
55
+
56
+ expect(projectOption).toBeDefined();
57
+ expect(imageOption).toBeDefined();
58
+ });
59
+
60
+ it('should register compute delete subcommand', () => {
61
+ registerComputeProgram(program);
62
+
63
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
64
+ const subCommands = computeCommand?.commands || [];
65
+ const deleteCommand = subCommands.find(cmd => cmd.name() === 'delete');
66
+
67
+ expect(deleteCommand).toBeDefined();
68
+ expect(deleteCommand?.description()).toBe('Delete one or more compute instances');
69
+
70
+ // Check options
71
+ const options = deleteCommand?.options || [];
72
+ const projectOption = options.find(opt => opt.long === '--project');
73
+ const forceOption = options.find(opt => opt.long === '--force');
74
+
75
+ expect(projectOption).toBeDefined();
76
+ expect(forceOption).toBeDefined();
77
+ });
78
+
79
+ it('should have all expected compute subcommands', () => {
80
+ registerComputeProgram(program);
81
+
82
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
83
+ const subCommandNames = computeCommand?.commands.map(cmd => cmd.name()) || [];
84
+
85
+ expect(subCommandNames).toContain('list');
86
+ expect(subCommandNames).toContain('create');
87
+ expect(subCommandNames).toContain('delete');
88
+ expect(subCommandNames).toHaveLength(3);
89
+ });
90
+
91
+ it('should have proper action functions for all subcommands', () => {
92
+ registerComputeProgram(program);
93
+
94
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
95
+ const subCommands = computeCommand?.commands || [];
96
+
97
+ subCommands.forEach(cmd => {
98
+ expect(typeof cmd.action).toBe('function');
99
+ });
100
+ });
101
+
102
+ it('should register compute list command with correct structure', () => {
103
+ registerComputeProgram(program);
104
+
105
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
106
+ const listCommand = computeCommand?.commands.find(cmd => cmd.name() === 'list');
107
+
108
+ expect(listCommand?.name()).toBe('list');
109
+ expect(listCommand?.description()).toBe('List all compute instances');
110
+ expect(typeof listCommand?.action).toBe('function');
111
+ });
112
+
113
+ it('should register compute create command with correct structure', () => {
114
+ registerComputeProgram(program);
115
+
116
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
117
+ const createCommand = computeCommand?.commands.find(cmd => cmd.name() === 'create');
118
+
119
+ expect(createCommand?.name()).toBe('create');
120
+ expect(createCommand?.description()).toBe('Create a new compute instance');
121
+ expect(typeof createCommand?.action).toBe('function');
122
+ });
123
+
124
+ it('should register compute delete command with correct structure', () => {
125
+ registerComputeProgram(program);
126
+
127
+ const computeCommand = program.commands.find(cmd => cmd.name() === 'compute');
128
+ const deleteCommand = computeCommand?.commands.find(cmd => cmd.name() === 'delete');
129
+
130
+ expect(deleteCommand?.name()).toBe('delete');
131
+ expect(deleteCommand?.description()).toBe('Delete one or more compute instances');
132
+ expect(typeof deleteCommand?.action).toBe('function');
133
+ });
134
+ });
135
+ });
@@ -0,0 +1,217 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { Command } from 'commander';
3
+ import { registerDocumentCommands } from '../src/commands/document';
4
+
5
+ describe('Document Commands', () => {
6
+ let program: Command;
7
+
8
+ beforeEach(() => {
9
+ program = new Command();
10
+ vi.clearAllMocks();
11
+ });
12
+
13
+ describe('registerDocumentCommands', () => {
14
+ it('should register document command group', () => {
15
+ registerDocumentCommands(program);
16
+
17
+ const commands = program.commands;
18
+ const documentCommand = commands.find(cmd => cmd.name() === 'document');
19
+
20
+ expect(documentCommand).toBeDefined();
21
+ expect(documentCommand?.description()).toBe('Manage Nestbox documents');
22
+ });
23
+
24
+ it('should register doc subcommand group', () => {
25
+ registerDocumentCommands(program);
26
+
27
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
28
+ const subCommands = documentCommand?.commands || [];
29
+ const docCommand = subCommands.find(cmd => cmd.name() === 'doc');
30
+
31
+ expect(docCommand).toBeDefined();
32
+ expect(docCommand?.description()).toBe('Manage individual documents');
33
+ });
34
+
35
+ it('should register collection subcommand group', () => {
36
+ registerDocumentCommands(program);
37
+
38
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
39
+ const subCommands = documentCommand?.commands || [];
40
+ const collectionCommand = subCommands.find(cmd => cmd.name() === 'collection');
41
+
42
+ expect(collectionCommand).toBeDefined();
43
+ expect(collectionCommand?.description()).toBe('Manage document collections');
44
+ });
45
+
46
+ it('should register doc list subcommand', () => {
47
+ registerDocumentCommands(program);
48
+
49
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
50
+ const collectionCommand = documentCommand?.commands.find(cmd => cmd.name() === 'collection');
51
+ const collectionSubCommands = collectionCommand?.commands || [];
52
+ const listCommand = collectionSubCommands.find(cmd => cmd.name() === 'list');
53
+
54
+ expect(listCommand).toBeDefined();
55
+ expect(listCommand?.description()).toBe('List document collections for a specific instance');
56
+
57
+ // Check options
58
+ const options = listCommand?.options || [];
59
+ const instanceOption = options.find(opt => opt.long === '--instance');
60
+ const projectOption = options.find(opt => opt.long === '--project');
61
+
62
+ expect(instanceOption).toBeDefined();
63
+ expect(projectOption).toBeDefined();
64
+ });
65
+
66
+ it('should register doc create subcommand', () => {
67
+ registerDocumentCommands(program);
68
+
69
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
70
+ const collectionCommand = documentCommand?.commands.find(cmd => cmd.name() === 'collection');
71
+ const collectionSubCommands = collectionCommand?.commands || [];
72
+ const createCommand = collectionSubCommands.find(cmd => cmd.name() === 'create');
73
+
74
+ expect(createCommand).toBeDefined();
75
+ expect(createCommand?.description()).toBe('Create a new document collection for a specific instance');
76
+
77
+ // Check options
78
+ const options = createCommand?.options || [];
79
+ const instanceOption = options.find(opt => opt.long === '--instance');
80
+ const projectOption = options.find(opt => opt.long === '--project');
81
+
82
+ expect(instanceOption).toBeDefined();
83
+ expect(projectOption).toBeDefined();
84
+ });
85
+
86
+ it('should register doc get subcommand', () => {
87
+ registerDocumentCommands(program);
88
+
89
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
90
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
91
+ const docSubCommands = docCommand?.commands || [];
92
+ const getCommand = docSubCommands.find(cmd => cmd.name() === 'get');
93
+
94
+ expect(getCommand).toBeDefined();
95
+ expect(getCommand?.description()).toBe('Get a document from a collection');
96
+
97
+ // Arguments are part of the command definition, not in args array
98
+ expect(getCommand?.name()).toBe('get');
99
+ });
100
+
101
+ it('should register doc delete subcommand', () => {
102
+ registerDocumentCommands(program);
103
+
104
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
105
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
106
+ const docSubCommands = docCommand?.commands || [];
107
+ const deleteCommand = docSubCommands.find(cmd => cmd.name() === 'delete');
108
+
109
+ expect(deleteCommand).toBeDefined();
110
+ expect(deleteCommand?.description()).toBe('Delete a document from a collection');
111
+
112
+ // Arguments are part of the command definition, not in args array
113
+ expect(deleteCommand?.name()).toBe('delete');
114
+ });
115
+
116
+ it('should register doc update subcommand', () => {
117
+ registerDocumentCommands(program);
118
+
119
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
120
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
121
+ const docSubCommands = docCommand?.commands || [];
122
+ const updateCommand = docSubCommands.find(cmd => cmd.name() === 'update');
123
+
124
+ expect(updateCommand).toBeDefined();
125
+ expect(updateCommand?.description()).toBe('Update a document in a collection');
126
+
127
+ // Arguments are part of the command definition, not in args array
128
+ expect(updateCommand?.name()).toBe('update');
129
+ });
130
+
131
+ it('should register collection add subcommand', () => {
132
+ registerDocumentCommands(program);
133
+
134
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
135
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
136
+ const docSubCommands = docCommand?.commands || [];
137
+ const addCommand = docSubCommands.find(cmd => cmd.name() === 'add');
138
+
139
+ expect(addCommand).toBeDefined();
140
+ expect(addCommand?.description()).toBe('Add a new document to a collection');
141
+
142
+ // Check options
143
+ const options = addCommand?.options || [];
144
+ const instanceOption = options.find(opt => opt.long === '--instance');
145
+ const collectionOption = options.find(opt => opt.long === '--collection');
146
+
147
+ expect(instanceOption).toBeDefined();
148
+ expect(collectionOption).toBeDefined();
149
+ });
150
+
151
+ it('should register collection search subcommand', () => {
152
+ registerDocumentCommands(program);
153
+
154
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
155
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
156
+ const docSubCommands = docCommand?.commands || [];
157
+ const searchCommand = docSubCommands.find(cmd => cmd.name() === 'search');
158
+
159
+ expect(searchCommand).toBeDefined();
160
+ expect(searchCommand?.description()).toBe('Search for documents in a collection');
161
+
162
+ // Check arguments and options
163
+ expect(searchCommand?.name()).toBe('search');
164
+ });
165
+
166
+ it('should have all expected doc subcommands', () => {
167
+ registerDocumentCommands(program);
168
+
169
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
170
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
171
+ const docSubCommandNames = docCommand?.commands.map(cmd => cmd.name()) || [];
172
+
173
+ expect(docSubCommandNames).toContain('add');
174
+ expect(docSubCommandNames).toContain('get');
175
+ expect(docSubCommandNames).toContain('delete');
176
+ expect(docSubCommandNames).toContain('update');
177
+ expect(docSubCommandNames).toContain('upload-file');
178
+ expect(docSubCommandNames).toContain('search');
179
+ expect(docSubCommandNames).toHaveLength(6);
180
+ });
181
+
182
+ it('should have all expected collection subcommands', () => {
183
+ registerDocumentCommands(program);
184
+
185
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
186
+ const collectionCommand = documentCommand?.commands.find(cmd => cmd.name() === 'collection');
187
+ const collectionSubCommandNames = collectionCommand?.commands.map(cmd => cmd.name()) || [];
188
+
189
+ expect(collectionSubCommandNames).toContain('list');
190
+ expect(collectionSubCommandNames).toContain('create');
191
+ expect(collectionSubCommandNames).toContain('get');
192
+ expect(collectionSubCommandNames).toContain('delete');
193
+ expect(collectionSubCommandNames).toContain('update');
194
+ expect(collectionSubCommandNames).toHaveLength(5);
195
+ });
196
+
197
+ it('should have proper action functions for all subcommands', () => {
198
+ registerDocumentCommands(program);
199
+
200
+ const documentCommand = program.commands.find(cmd => cmd.name() === 'document');
201
+ const docCommand = documentCommand?.commands.find(cmd => cmd.name() === 'doc');
202
+ const collectionCommand = documentCommand?.commands.find(cmd => cmd.name() === 'collection');
203
+
204
+ // Check doc subcommands
205
+ const docSubCommands = docCommand?.commands || [];
206
+ docSubCommands.forEach(cmd => {
207
+ expect(typeof cmd.action).toBe('function');
208
+ });
209
+
210
+ // Check collection subcommands
211
+ const collectionSubCommands = collectionCommand?.commands || [];
212
+ collectionSubCommands.forEach(cmd => {
213
+ expect(typeof cmd.action).toBe('function');
214
+ });
215
+ });
216
+ });
217
+ });
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { Command } from 'commander';
3
+ import { registerImageCommands } from '../src/commands/image';
4
+
5
+ describe('Image Commands', () => {
6
+ let program: Command;
7
+
8
+ beforeEach(() => {
9
+ program = new Command();
10
+ vi.clearAllMocks();
11
+ });
12
+
13
+ describe('registerImageCommands', () => {
14
+ it('should register image command group', () => {
15
+ registerImageCommands(program);
16
+
17
+ const commands = program.commands;
18
+ const imageCommand = commands.find(cmd => cmd.name() === 'image');
19
+
20
+ expect(imageCommand).toBeDefined();
21
+ expect(imageCommand?.description()).toBe('Manage Nestbox images');
22
+ });
23
+
24
+ it('should register image list subcommand', () => {
25
+ registerImageCommands(program);
26
+
27
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
28
+ const subCommands = imageCommand?.commands || [];
29
+ const listCommand = subCommands.find(cmd => cmd.name() === 'list');
30
+
31
+ expect(listCommand).toBeDefined();
32
+ expect(listCommand?.description()).toBe('List images for a project');
33
+
34
+ // Check options
35
+ const options = listCommand?.options || [];
36
+ const projectOption = options.find(opt => opt.long === '--project');
37
+ expect(projectOption).toBeDefined();
38
+ expect(projectOption?.description).toBe('Project ID or name (defaults to the current project)');
39
+ });
40
+
41
+ it('should have all expected image subcommands', () => {
42
+ registerImageCommands(program);
43
+
44
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
45
+ const subCommandNames = imageCommand?.commands.map(cmd => cmd.name()) || [];
46
+
47
+ expect(subCommandNames).toContain('list');
48
+ expect(subCommandNames).toHaveLength(1);
49
+ });
50
+
51
+ it('should have proper action functions for all subcommands', () => {
52
+ registerImageCommands(program);
53
+
54
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
55
+ const subCommands = imageCommand?.commands || [];
56
+
57
+ subCommands.forEach(cmd => {
58
+ expect(typeof cmd.action).toBe('function');
59
+ });
60
+ });
61
+
62
+ it('should register image list command with correct structure', () => {
63
+ registerImageCommands(program);
64
+
65
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
66
+ const listCommand = imageCommand?.commands.find(cmd => cmd.name() === 'list');
67
+
68
+ expect(listCommand?.name()).toBe('list');
69
+ expect(listCommand?.description()).toBe('List images for a project');
70
+ expect(typeof listCommand?.action).toBe('function');
71
+ });
72
+
73
+ it('should have project option with correct configuration', () => {
74
+ registerImageCommands(program);
75
+
76
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
77
+ const listCommand = imageCommand?.commands.find(cmd => cmd.name() === 'list');
78
+ const options = listCommand?.options || [];
79
+ const projectOption = options.find(opt => opt.long === '--project');
80
+
81
+ expect(projectOption).toBeDefined();
82
+ expect(projectOption?.description).toBe('Project ID or name (defaults to the current project)');
83
+ expect(projectOption?.long).toBe('--project');
84
+ });
85
+
86
+ it('should properly initialize when auth token is available', () => {
87
+ // This test verifies that the command registration doesn't fail when auth token is available
88
+ expect(() => {
89
+ registerImageCommands(program);
90
+ }).not.toThrow();
91
+
92
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
93
+ expect(imageCommand).toBeDefined();
94
+ });
95
+
96
+ it('should handle the case when createApis function is called within command actions', () => {
97
+ registerImageCommands(program);
98
+
99
+ const imageCommand = program.commands.find(cmd => cmd.name() === 'image');
100
+ const listCommand = imageCommand?.commands.find(cmd => cmd.name() === 'list');
101
+
102
+ // Verify that the action function exists and is callable
103
+ expect(listCommand?.action).toBeDefined();
104
+ expect(typeof listCommand?.action).toBe('function');
105
+ });
106
+ });
107
+ });
package/test/mocks.ts ADDED
@@ -0,0 +1,122 @@
1
+ import { vi } from 'vitest';
2
+
3
+ // Mock external dependencies that are commonly used across all test files
4
+ export const setupCommonMocks = () => {
5
+ // Mock utils/error
6
+ vi.mock('../src/utils/error', () => ({
7
+ withTokenRefresh: vi.fn(),
8
+ handle401Error: vi.fn()
9
+ }));
10
+
11
+ // Mock utils/auth
12
+ vi.mock('../src/utils/auth', () => ({
13
+ getAuthToken: vi.fn().mockReturnValue({
14
+ token: 'mock-token',
15
+ serverUrl: 'http://localhost:3000'
16
+ }),
17
+ listCredentials: vi.fn(),
18
+ removeCredentials: vi.fn()
19
+ }));
20
+
21
+ // Mock @nestbox-ai/admin
22
+ vi.mock('@nestbox-ai/admin', () => ({
23
+ Configuration: vi.fn(),
24
+ AuthApi: vi.fn(),
25
+ ProjectsApi: vi.fn(),
26
+ MachineAgentApi: vi.fn(),
27
+ MachineInstancesApi: vi.fn(),
28
+ MiscellaneousApi: vi.fn(),
29
+ DocumentsApi: vi.fn(),
30
+ OAuthLoginRequestDTOTypeEnum: {}
31
+ }));
32
+
33
+ // Mock utils/project
34
+ vi.mock('../src/utils/project', () => ({
35
+ resolveProject: vi.fn()
36
+ }));
37
+
38
+ // Mock utils/agent
39
+ vi.mock('../src/utils/agent', () => ({
40
+ createNestboxConfig: vi.fn(),
41
+ createZipFromDirectory: vi.fn(),
42
+ extractZip: vi.fn(),
43
+ findProjectRoot: vi.fn(),
44
+ isTypeScriptProject: vi.fn(),
45
+ loadNestboxConfig: vi.fn(),
46
+ runPredeployScripts: vi.fn()
47
+ }));
48
+
49
+ // Mock utils/user
50
+ vi.mock('../src/utils/user', () => ({
51
+ userData: vi.fn()
52
+ }));
53
+
54
+ // Mock chalk
55
+ vi.mock('chalk', () => ({
56
+ default: {
57
+ green: vi.fn((text) => text),
58
+ red: vi.fn((text) => text),
59
+ yellow: vi.fn((text) => text),
60
+ blue: vi.fn((text) => text)
61
+ }
62
+ }));
63
+
64
+ // Mock ora
65
+ vi.mock('ora', () => ({
66
+ default: vi.fn(() => ({
67
+ start: vi.fn().mockReturnThis(),
68
+ succeed: vi.fn().mockReturnThis(),
69
+ fail: vi.fn().mockReturnThis(),
70
+ text: ''
71
+ }))
72
+ }));
73
+
74
+ // Mock cli-table3
75
+ vi.mock('cli-table3', () => ({
76
+ default: vi.fn()
77
+ }));
78
+
79
+ // Mock inquirer
80
+ vi.mock('inquirer', () => ({
81
+ default: {
82
+ prompt: vi.fn()
83
+ }
84
+ }));
85
+
86
+ // Mock fs
87
+ vi.mock('fs', () => ({
88
+ default: {
89
+ existsSync: vi.fn(),
90
+ readFileSync: vi.fn(),
91
+ writeFileSync: vi.fn()
92
+ }
93
+ }));
94
+
95
+ // Mock yaml
96
+ vi.mock('yaml', () => ({
97
+ default: {
98
+ load: vi.fn()
99
+ }
100
+ }));
101
+
102
+ // Mock axios
103
+ vi.mock('axios', () => ({
104
+ default: {
105
+ get: vi.fn(),
106
+ post: vi.fn()
107
+ }
108
+ }));
109
+
110
+ // Mock open
111
+ vi.mock('open', () => ({
112
+ default: vi.fn()
113
+ }));
114
+
115
+ // Mock path
116
+ vi.mock('path', () => ({
117
+ default: {
118
+ join: vi.fn(),
119
+ resolve: vi.fn()
120
+ }
121
+ }));
122
+ };
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { Command } from 'commander';
3
+ import { registerProjectCommands } from '../src/commands/projects';
4
+
5
+ describe('Project Commands', () => {
6
+ let program: Command;
7
+
8
+ beforeEach(() => {
9
+ program = new Command();
10
+ vi.clearAllMocks();
11
+ });
12
+
13
+ describe('registerProjectCommands', () => {
14
+ it('should register project command group', () => {
15
+ registerProjectCommands(program);
16
+
17
+ const commands = program.commands;
18
+ const projectCommand = commands.find(cmd => cmd.name() === 'project');
19
+
20
+ expect(projectCommand).toBeDefined();
21
+ expect(projectCommand?.description()).toBe('Manage Nestbox projects');
22
+ });
23
+
24
+ it('should register project use subcommand', () => {
25
+ registerProjectCommands(program);
26
+
27
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
28
+ const subCommands = projectCommand?.commands || [];
29
+ const useCommand = subCommands.find(cmd => cmd.name() === 'use');
30
+
31
+ expect(useCommand).toBeDefined();
32
+ expect(useCommand?.description()).toBe('Set default project for all commands');
33
+
34
+ // The command uses a required argument <project-name> which is part of the command definition
35
+ expect(useCommand?.name()).toBe('use');
36
+ });
37
+
38
+ it('should register project add subcommand', () => {
39
+ registerProjectCommands(program);
40
+
41
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
42
+ const subCommands = projectCommand?.commands || [];
43
+ const addCommand = subCommands.find(cmd => cmd.name() === 'add');
44
+
45
+ expect(addCommand).toBeDefined();
46
+ expect(addCommand?.description()).toBe('Add a project with optional alias');
47
+
48
+ // The command uses required and optional arguments which are part of the command definition
49
+ expect(addCommand?.name()).toBe('add');
50
+ });
51
+
52
+ it('should register project list subcommand', () => {
53
+ registerProjectCommands(program);
54
+
55
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
56
+ const subCommands = projectCommand?.commands || [];
57
+ const listCommand = subCommands.find(cmd => cmd.name() === 'list');
58
+
59
+ expect(listCommand).toBeDefined();
60
+ expect(listCommand?.description()).toBe('List all projects');
61
+ });
62
+
63
+ it('should have all expected project subcommands', () => {
64
+ registerProjectCommands(program);
65
+
66
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
67
+ const subCommandNames = projectCommand?.commands.map(cmd => cmd.name()) || [];
68
+
69
+ expect(subCommandNames).toContain('use');
70
+ expect(subCommandNames).toContain('add');
71
+ expect(subCommandNames).toContain('list');
72
+ expect(subCommandNames).toHaveLength(3);
73
+ });
74
+
75
+ it('should have proper action functions for all commands', () => {
76
+ registerProjectCommands(program);
77
+
78
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
79
+ const subCommands = projectCommand?.commands || [];
80
+
81
+ subCommands.forEach(cmd => {
82
+ expect(typeof cmd.action).toBe('function');
83
+ });
84
+ });
85
+
86
+ it('should register project use command with correct structure', () => {
87
+ registerProjectCommands(program);
88
+
89
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
90
+ const useCommand = projectCommand?.commands.find(cmd => cmd.name() === 'use');
91
+
92
+ expect(useCommand?.name()).toBe('use');
93
+ expect(useCommand?.description()).toBe('Set default project for all commands');
94
+ expect(typeof useCommand?.action).toBe('function');
95
+ });
96
+
97
+ it('should register project add command with correct structure', () => {
98
+ registerProjectCommands(program);
99
+
100
+ const projectCommand = program.commands.find(cmd => cmd.name() === 'project');
101
+ const addCommand = projectCommand?.commands.find(cmd => cmd.name() === 'add');
102
+
103
+ expect(addCommand?.name()).toBe('add');
104
+ expect(addCommand?.description()).toBe('Add a project with optional alias');
105
+ expect(typeof addCommand?.action).toBe('function');
106
+ });
107
+ });
108
+ });