@agents-at-scale/ark 0.1.52 → 0.1.55

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 (116) hide show
  1. package/dist/arkServices.js +11 -7
  2. package/dist/commands/export/index.js +6 -4
  3. package/dist/commands/generate/generators/agent.js +2 -0
  4. package/dist/commands/generate/generators/marketplace.js +2 -0
  5. package/dist/commands/generate/generators/mcpserver.js +2 -0
  6. package/dist/commands/generate/generators/project.js +9 -2
  7. package/dist/commands/generate/generators/query.js +2 -0
  8. package/dist/commands/generate/generators/team.js +2 -0
  9. package/dist/commands/generate/templateDiscovery.js +1 -0
  10. package/dist/commands/generate/templateEngine.js +1 -3
  11. package/dist/commands/import/index.js +1 -1
  12. package/dist/commands/install/index.js +2 -1
  13. package/dist/commands/models/kubernetes/manifest-builder.js +27 -10
  14. package/dist/commands/models/providers/azure.d.ts +10 -7
  15. package/dist/commands/models/providers/azure.js +83 -21
  16. package/dist/commands/uninstall/index.js +1 -1
  17. package/dist/components/ChatUI.js +17 -16
  18. package/dist/components/statusChecker.js +3 -3
  19. package/dist/lib/arkApiClient.js +11 -9
  20. package/dist/lib/arkApiProxy.js +1 -0
  21. package/dist/lib/arkServiceProxy.js +5 -1
  22. package/dist/lib/chatClient.js +9 -0
  23. package/dist/lib/config.js +8 -3
  24. package/dist/lib/errors.js +3 -0
  25. package/dist/ui/asyncOperations/connectingToArk.js +2 -2
  26. package/package.json +17 -13
  27. package/dist/arkServices.spec.d.ts +0 -1
  28. package/dist/arkServices.spec.js +0 -138
  29. package/dist/commands/agents/index.spec.d.ts +0 -1
  30. package/dist/commands/agents/index.spec.js +0 -67
  31. package/dist/commands/cluster/get.spec.d.ts +0 -1
  32. package/dist/commands/cluster/get.spec.js +0 -92
  33. package/dist/commands/cluster/index.spec.d.ts +0 -1
  34. package/dist/commands/cluster/index.spec.js +0 -24
  35. package/dist/commands/completion/index.spec.d.ts +0 -1
  36. package/dist/commands/completion/index.spec.js +0 -34
  37. package/dist/commands/config/index.spec.d.ts +0 -1
  38. package/dist/commands/config/index.spec.js +0 -78
  39. package/dist/commands/evaluation/index.spec.d.ts +0 -1
  40. package/dist/commands/evaluation/index.spec.js +0 -161
  41. package/dist/commands/export/index.spec.d.ts +0 -1
  42. package/dist/commands/export/index.spec.js +0 -145
  43. package/dist/commands/import/index.spec.d.ts +0 -1
  44. package/dist/commands/import/index.spec.js +0 -46
  45. package/dist/commands/install/index.spec.d.ts +0 -1
  46. package/dist/commands/install/index.spec.js +0 -286
  47. package/dist/commands/marketplace/index.spec.d.ts +0 -1
  48. package/dist/commands/marketplace/index.spec.js +0 -88
  49. package/dist/commands/memory/index.spec.d.ts +0 -1
  50. package/dist/commands/memory/index.spec.js +0 -124
  51. package/dist/commands/models/create.spec.d.ts +0 -1
  52. package/dist/commands/models/create.spec.js +0 -167
  53. package/dist/commands/models/index.spec.d.ts +0 -1
  54. package/dist/commands/models/index.spec.js +0 -96
  55. package/dist/commands/models/providers/azure.spec.d.ts +0 -1
  56. package/dist/commands/models/providers/azure.spec.js +0 -232
  57. package/dist/commands/models/providers/bedrock.spec.d.ts +0 -1
  58. package/dist/commands/models/providers/bedrock.spec.js +0 -241
  59. package/dist/commands/models/providers/openai.spec.d.ts +0 -1
  60. package/dist/commands/models/providers/openai.spec.js +0 -180
  61. package/dist/commands/queries/delete.spec.d.ts +0 -1
  62. package/dist/commands/queries/delete.spec.js +0 -74
  63. package/dist/commands/queries/index.spec.d.ts +0 -1
  64. package/dist/commands/queries/index.spec.js +0 -167
  65. package/dist/commands/queries/list.spec.d.ts +0 -1
  66. package/dist/commands/queries/list.spec.js +0 -170
  67. package/dist/commands/queries/validation.spec.d.ts +0 -1
  68. package/dist/commands/queries/validation.spec.js +0 -27
  69. package/dist/commands/query/index.spec.d.ts +0 -1
  70. package/dist/commands/query/index.spec.js +0 -104
  71. package/dist/commands/targets/index.spec.d.ts +0 -1
  72. package/dist/commands/targets/index.spec.js +0 -154
  73. package/dist/commands/teams/index.spec.d.ts +0 -1
  74. package/dist/commands/teams/index.spec.js +0 -70
  75. package/dist/commands/tools/index.spec.d.ts +0 -1
  76. package/dist/commands/tools/index.spec.js +0 -70
  77. package/dist/commands/uninstall/index.spec.d.ts +0 -1
  78. package/dist/commands/uninstall/index.spec.js +0 -125
  79. package/dist/lib/arkServiceProxy.spec.d.ts +0 -1
  80. package/dist/lib/arkServiceProxy.spec.js +0 -100
  81. package/dist/lib/arkStatus.spec.d.ts +0 -1
  82. package/dist/lib/arkStatus.spec.js +0 -49
  83. package/dist/lib/chatClient.spec.d.ts +0 -1
  84. package/dist/lib/chatClient.spec.js +0 -108
  85. package/dist/lib/cluster.spec.d.ts +0 -1
  86. package/dist/lib/cluster.spec.js +0 -338
  87. package/dist/lib/commands.spec.d.ts +0 -1
  88. package/dist/lib/commands.spec.js +0 -146
  89. package/dist/lib/config.spec.d.ts +0 -1
  90. package/dist/lib/config.spec.js +0 -202
  91. package/dist/lib/duration.spec.d.ts +0 -1
  92. package/dist/lib/duration.spec.js +0 -13
  93. package/dist/lib/errors.spec.d.ts +0 -1
  94. package/dist/lib/errors.spec.js +0 -221
  95. package/dist/lib/executeQuery.spec.d.ts +0 -1
  96. package/dist/lib/executeQuery.spec.js +0 -325
  97. package/dist/lib/kubectl.spec.d.ts +0 -1
  98. package/dist/lib/kubectl.spec.js +0 -192
  99. package/dist/lib/marketplaceFetcher.spec.d.ts +0 -1
  100. package/dist/lib/marketplaceFetcher.spec.js +0 -225
  101. package/dist/lib/nextSteps.spec.d.ts +0 -1
  102. package/dist/lib/nextSteps.spec.js +0 -59
  103. package/dist/lib/output.spec.d.ts +0 -1
  104. package/dist/lib/output.spec.js +0 -123
  105. package/dist/lib/startup.spec.d.ts +0 -1
  106. package/dist/lib/startup.spec.js +0 -152
  107. package/dist/lib/stdin.spec.d.ts +0 -1
  108. package/dist/lib/stdin.spec.js +0 -82
  109. package/dist/lib/timeout.spec.d.ts +0 -1
  110. package/dist/lib/timeout.spec.js +0 -14
  111. package/dist/lib/waitForReady.spec.d.ts +0 -1
  112. package/dist/lib/waitForReady.spec.js +0 -104
  113. package/dist/marketplaceServices.spec.d.ts +0 -1
  114. package/dist/marketplaceServices.spec.js +0 -74
  115. package/dist/ui/statusFormatter.spec.d.ts +0 -1
  116. package/dist/ui/statusFormatter.spec.js +0 -58
@@ -1,225 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- const mockAxiosGet = jest.fn();
3
- jest.unstable_mockModule('axios', () => ({
4
- default: {
5
- get: mockAxiosGet,
6
- isAxiosError: (error) => {
7
- return (typeof error === 'object' &&
8
- error !== null &&
9
- 'isAxiosError' in error &&
10
- error.isAxiosError === true);
11
- },
12
- },
13
- }));
14
- const mockGetMarketplaceRepoUrl = jest.fn();
15
- const mockGetMarketplaceRegistry = jest.fn();
16
- jest.unstable_mockModule('./config.js', () => ({
17
- getMarketplaceRepoUrl: mockGetMarketplaceRepoUrl,
18
- getMarketplaceRegistry: mockGetMarketplaceRegistry,
19
- }));
20
- const { fetchMarketplaceManifest, mapMarketplaceItemToArkService, getMarketplaceServicesFromManifest, } = await import('./marketplaceFetcher.js');
21
- describe('marketplaceFetcher', () => {
22
- beforeEach(() => {
23
- jest.clearAllMocks();
24
- mockAxiosGet.mockClear();
25
- mockGetMarketplaceRepoUrl.mockReturnValue('https://test-repo.example.com/marketplace');
26
- mockGetMarketplaceRegistry.mockReturnValue('oci://test-registry.example.com/charts');
27
- });
28
- describe('fetchMarketplaceManifest', () => {
29
- it('fetches and returns manifest successfully', async () => {
30
- const mockManifest = {
31
- version: '1.0.0',
32
- marketplace: 'ARK Marketplace',
33
- items: [
34
- {
35
- name: 'test-service',
36
- description: 'Test service',
37
- ark: {
38
- chartPath: 'oci://registry/test-service',
39
- namespace: 'test',
40
- },
41
- },
42
- ],
43
- };
44
- mockAxiosGet.mockResolvedValue({
45
- data: mockManifest,
46
- status: 200,
47
- statusText: 'OK',
48
- headers: {},
49
- config: {},
50
- });
51
- const result = await fetchMarketplaceManifest();
52
- expect(result).toEqual(mockManifest);
53
- expect(mockAxiosGet).toHaveBeenCalledWith(expect.stringContaining('marketplace.json'), expect.objectContaining({
54
- timeout: 10000,
55
- headers: { Accept: 'application/json' },
56
- }));
57
- });
58
- it('returns null on network error', async () => {
59
- const networkError = new Error('Network error');
60
- networkError.code = 'ENOTFOUND';
61
- networkError.isAxiosError = true;
62
- mockAxiosGet.mockRejectedValue(networkError);
63
- const result = await fetchMarketplaceManifest();
64
- expect(result).toBeNull();
65
- });
66
- it('returns null on connection refused', async () => {
67
- const connectionError = Object.assign(new Error('Connection refused'), {
68
- code: 'ECONNREFUSED',
69
- isAxiosError: true,
70
- });
71
- mockAxiosGet.mockRejectedValue(connectionError);
72
- const result = await fetchMarketplaceManifest();
73
- expect(result).toBeNull();
74
- });
75
- });
76
- describe('mapMarketplaceItemToArkService', () => {
77
- it('maps marketplace item to ARK service correctly', () => {
78
- const item = {
79
- name: 'test-service',
80
- description: 'Test description',
81
- ark: {
82
- chartPath: 'oci://registry/test',
83
- namespace: 'test-ns',
84
- helmReleaseName: 'test-release',
85
- installArgs: ['--create-namespace'],
86
- k8sServiceName: 'test-svc',
87
- k8sServicePort: 8080,
88
- k8sDeploymentName: 'test-deploy',
89
- },
90
- };
91
- const result = mapMarketplaceItemToArkService(item);
92
- expect(result).toEqual({
93
- name: 'test-service',
94
- helmReleaseName: 'test-release',
95
- description: 'Test description',
96
- enabled: true,
97
- category: 'marketplace',
98
- namespace: 'test-ns',
99
- chartPath: 'oci://registry/test',
100
- installArgs: ['--create-namespace'],
101
- k8sServiceName: 'test-svc',
102
- k8sServicePort: 8080,
103
- k8sDeploymentName: 'test-deploy',
104
- });
105
- });
106
- it('uses defaults when ark fields are missing', () => {
107
- const item = {
108
- name: 'simple-service',
109
- description: 'Simple service',
110
- ark: {},
111
- };
112
- const result = mapMarketplaceItemToArkService(item);
113
- expect(result.name).toBe('simple-service');
114
- expect(result.helmReleaseName).toBe('simple-service');
115
- expect(result.namespace).toBe('simple-service');
116
- expect(result.chartPath).toContain('simple-service');
117
- });
118
- it('sanitizes service name', () => {
119
- const item = {
120
- name: 'Test Service 123!',
121
- description: 'Test',
122
- ark: {},
123
- };
124
- const result = mapMarketplaceItemToArkService(item);
125
- expect(result.name).toBe('test-service-123');
126
- });
127
- });
128
- describe('getMarketplaceServicesFromManifest', () => {
129
- it('converts manifest items to service collection', async () => {
130
- const mockManifest = {
131
- version: '1.0.0',
132
- marketplace: 'ARK Marketplace',
133
- items: [
134
- {
135
- name: 'service1',
136
- description: 'Service 1',
137
- type: 'service',
138
- ark: {
139
- chartPath: 'oci://registry/service1',
140
- namespace: 'ns1',
141
- },
142
- },
143
- {
144
- name: 'service2',
145
- description: 'Service 2',
146
- type: 'service',
147
- ark: {
148
- chartPath: 'oci://registry/service2',
149
- namespace: 'ns2',
150
- },
151
- },
152
- ],
153
- };
154
- mockAxiosGet.mockResolvedValue({
155
- data: mockManifest,
156
- status: 200,
157
- statusText: 'OK',
158
- headers: {},
159
- config: {},
160
- });
161
- const result = await getMarketplaceServicesFromManifest();
162
- expect(result).not.toBeNull();
163
- expect(result?.['service1']).toBeDefined();
164
- expect(result?.['service2']).toBeDefined();
165
- expect(result?.['service1']?.description).toBe('Service 1');
166
- expect(result?.['service2']?.description).toBe('Service 2');
167
- });
168
- it('filters out items without ark field', async () => {
169
- const mockManifest = {
170
- version: '1.0.0',
171
- marketplace: 'ARK Marketplace',
172
- items: [
173
- {
174
- name: 'service1',
175
- description: 'Service 1',
176
- type: 'service',
177
- ark: {
178
- chartPath: 'oci://registry/service1',
179
- },
180
- },
181
- {
182
- name: 'service2',
183
- description: 'Service 2',
184
- type: 'service',
185
- },
186
- ],
187
- };
188
- mockAxiosGet.mockResolvedValue({
189
- data: mockManifest,
190
- status: 200,
191
- statusText: 'OK',
192
- headers: {},
193
- config: {},
194
- });
195
- const result = await getMarketplaceServicesFromManifest();
196
- expect(result).not.toBeNull();
197
- expect(result?.['service1']).toBeDefined();
198
- expect(result?.['service2']).toBeUndefined();
199
- });
200
- it('returns null when manifest fetch fails', async () => {
201
- const networkError = new Error('Network error');
202
- networkError.code = 'ENOTFOUND';
203
- networkError.isAxiosError = true;
204
- mockAxiosGet.mockRejectedValue(networkError);
205
- const result = await getMarketplaceServicesFromManifest();
206
- expect(result).toBeNull();
207
- });
208
- it('returns null when manifest has no items', async () => {
209
- const mockManifest = {
210
- version: '1.0.0',
211
- marketplace: 'ARK Marketplace',
212
- items: [],
213
- };
214
- mockAxiosGet.mockResolvedValue({
215
- data: mockManifest,
216
- status: 200,
217
- statusText: 'OK',
218
- headers: {},
219
- config: {},
220
- });
221
- const result = await getMarketplaceServicesFromManifest();
222
- expect(result).toBeNull();
223
- });
224
- });
225
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,59 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { printNextSteps } from './nextSteps.js';
3
- describe('printNextSteps', () => {
4
- let consoleLogSpy;
5
- let output = [];
6
- beforeEach(() => {
7
- output = [];
8
- consoleLogSpy = jest.spyOn(console, 'log').mockImplementation((...args) => {
9
- output.push(args.join(' '));
10
- });
11
- });
12
- afterEach(() => {
13
- consoleLogSpy.mockRestore();
14
- });
15
- it('prints successful installation message', () => {
16
- printNextSteps();
17
- const fullOutput = output.join('\n');
18
- expect(fullOutput).toContain('✓ Installation complete');
19
- });
20
- it('includes all required commands', () => {
21
- printNextSteps();
22
- const fullOutput = output.join('\n');
23
- // Check for each command
24
- expect(fullOutput).toContain('ark models create default');
25
- expect(fullOutput).toContain('ark dashboard');
26
- expect(fullOutput).toContain('kubectl get agents');
27
- expect(fullOutput).toContain('ark query model/default "What are large language models?"');
28
- expect(fullOutput).toContain('ark');
29
- expect(fullOutput).toContain("# then choose 'Chat'");
30
- expect(fullOutput).toContain('ark generate project my-agents');
31
- });
32
- it('includes all required links', () => {
33
- printNextSteps();
34
- const fullOutput = output.join('\n');
35
- // Check for documentation links
36
- expect(fullOutput).toContain('https://mckinsey.github.io/agents-at-scale-ark/');
37
- expect(fullOutput).toContain('https://mckinsey.github.io/agents-at-scale-ark/developer-guide/cli-tools/');
38
- });
39
- it('includes all section labels', () => {
40
- printNextSteps();
41
- const fullOutput = output.join('\n');
42
- // Check for labels
43
- expect(fullOutput).toContain('Next steps:');
44
- expect(fullOutput).toContain('docs:');
45
- expect(fullOutput).toContain('create model:');
46
- expect(fullOutput).toContain('open dashboard:');
47
- expect(fullOutput).toContain('show agents:');
48
- expect(fullOutput).toContain('run a query:');
49
- expect(fullOutput).toContain('interactive chat:');
50
- expect(fullOutput).toContain('new project:');
51
- expect(fullOutput).toContain('install fark:');
52
- });
53
- it('has correct structure with empty lines', () => {
54
- printNextSteps();
55
- // Should have empty lines for formatting
56
- expect(output[0]).toBe('');
57
- expect(output[output.length - 1]).toBe('');
58
- });
59
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,123 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
2
- import chalk from 'chalk';
3
- import output from './output.js';
4
- describe('output', () => {
5
- let consoleErrorSpy;
6
- let consoleLogSpy;
7
- beforeEach(() => {
8
- consoleErrorSpy = jest
9
- .spyOn(console, 'error')
10
- .mockImplementation(() => undefined);
11
- consoleLogSpy = jest
12
- .spyOn(console, 'log')
13
- .mockImplementation(() => undefined);
14
- });
15
- afterEach(() => {
16
- jest.clearAllMocks();
17
- });
18
- describe('error', () => {
19
- it('should output error message with red cross and prefix', () => {
20
- output.error('Something went wrong');
21
- expect(consoleErrorSpy).toHaveBeenCalledWith(chalk.red('✗'), chalk.red('error:'), 'Something went wrong');
22
- });
23
- it('should handle additional arguments', () => {
24
- const error = new Error('Test error');
25
- output.error('Failed to connect', error, 123);
26
- expect(consoleErrorSpy).toHaveBeenCalledWith(chalk.red('✗'), chalk.red('error:'), 'Failed to connect', error, 123);
27
- });
28
- });
29
- describe('success', () => {
30
- it('should output success message with green checkmark', () => {
31
- output.success('Operation completed');
32
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), 'Operation completed');
33
- });
34
- it('should handle additional arguments', () => {
35
- output.success('Deployed', 'v1.0.0', { status: 'ok' });
36
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), 'Deployed', 'v1.0.0', { status: 'ok' });
37
- });
38
- });
39
- describe('info', () => {
40
- it('should output info message in gray', () => {
41
- output.info('Processing request...');
42
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.gray('Processing request...'));
43
- });
44
- it('should handle additional arguments', () => {
45
- output.info('Status:', 'running', 42);
46
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.gray('Status:'), 'running', 42);
47
- });
48
- });
49
- describe('warning', () => {
50
- it('should output warning message with yellow exclamation and prefix', () => {
51
- output.warning('Resource limit approaching');
52
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.yellow.bold('!'), chalk.yellow('warning:'), 'Resource limit approaching');
53
- });
54
- it('should handle additional arguments', () => {
55
- const details = { cpu: '85%', memory: '92%' };
56
- output.warning('High resource usage', details);
57
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.yellow.bold('!'), chalk.yellow('warning:'), 'High resource usage', details);
58
- });
59
- });
60
- describe('statusCheck', () => {
61
- it('should display found status with value and details', () => {
62
- output.statusCheck('found', 'platform', 'python3', 'v3.10');
63
- expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.green('✓')} ${chalk.green('platform')} ${chalk.bold.white('python3')}${chalk.gray(' v3.10')}`);
64
- });
65
- it('should display found status with value only', () => {
66
- output.statusCheck('found', 'fastmcp', 'v0.1.0');
67
- expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.green('✓')} ${chalk.green('fastmcp')} ${chalk.bold.white('v0.1.0')}`);
68
- });
69
- it('should display found status with label only', () => {
70
- output.statusCheck('found', '3 tools');
71
- expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.green('✓')} ${chalk.green('3 tools')}`);
72
- });
73
- it('should display missing status with details', () => {
74
- output.statusCheck('missing', 'fastmcp', undefined, 'not in dependencies');
75
- expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.yellow('?')} ${chalk.yellow('fastmcp')}${chalk.gray(' not in dependencies')}`);
76
- });
77
- it('should display warning status', () => {
78
- output.statusCheck('warning', 'version', '0.0.1', 'pre-release');
79
- expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.yellow('!')} ${chalk.yellow('version')} ${chalk.bold.white('0.0.1')}${chalk.gray(' pre-release')}`);
80
- });
81
- it('should display error status', () => {
82
- output.statusCheck('error', 'tools', undefined, 'discovery failed');
83
- expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.red('✗')} ${chalk.red('tools')}${chalk.gray(' discovery failed')}`);
84
- });
85
- });
86
- describe('section', () => {
87
- it('should display section header with colon', () => {
88
- output.section('dev-tests');
89
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.cyan.bold('dev-tests:'));
90
- });
91
- it('should display section header with custom text', () => {
92
- output.section('ark services');
93
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.cyan.bold('ark services:'));
94
- });
95
- });
96
- describe('statusMessage', () => {
97
- it('should output success status with title and message', () => {
98
- output.statusMessage('success', 'fastmcp', 'installed (v0.1.0)');
99
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), chalk.green('fastmcp:'), 'installed (v0.1.0)');
100
- });
101
- it('should output warning status with title and message', () => {
102
- output.statusMessage('warning', 'virtual environment', 'not found');
103
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.yellow.bold('!'), chalk.yellow('virtual environment:'), 'not found');
104
- });
105
- it('should output error status with title and message', () => {
106
- output.statusMessage('error', 'fastmcp', 'not found in dependencies');
107
- expect(consoleErrorSpy).toHaveBeenCalledWith(chalk.red('✗'), chalk.red('fastmcp:'), 'not found in dependencies');
108
- });
109
- it('should output info status with title and message', () => {
110
- output.statusMessage('info', 'platform', 'python3');
111
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.blue('ℹ'), chalk.blue('platform:'), 'python3');
112
- });
113
- it('should handle title-only messages', () => {
114
- output.statusMessage('success', 'Operation completed');
115
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), 'Operation completed');
116
- });
117
- it('should handle additional arguments', () => {
118
- const extra = { version: '1.0' };
119
- output.statusMessage('info', 'status', 'running', extra, 42);
120
- expect(consoleLogSpy).toHaveBeenCalledWith(chalk.blue('ℹ'), chalk.blue('status:'), 'running', extra, 42);
121
- });
122
- });
123
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,152 +0,0 @@
1
- import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
2
- // Mock chalk to avoid ANSI codes in tests
3
- jest.unstable_mockModule('chalk', () => ({
4
- default: {
5
- red: (str) => str,
6
- yellow: (str) => str,
7
- gray: (str) => str,
8
- blue: (str) => str,
9
- },
10
- }));
11
- // Mock commands module
12
- jest.unstable_mockModule('./commands.js', () => ({
13
- checkCommandExists: jest.fn(),
14
- }));
15
- // Mock config module
16
- jest.unstable_mockModule('./config.js', () => ({
17
- loadConfig: jest.fn(),
18
- }));
19
- // Mock execa module
20
- jest.unstable_mockModule('execa', () => ({
21
- execa: jest.fn(),
22
- }));
23
- // Dynamic imports after mocks
24
- const { checkCommandExists } = await import('./commands.js');
25
- const { loadConfig } = await import('./config.js');
26
- const { execa } = await import('execa');
27
- const { startup } = await import('./startup.js');
28
- // Type the mocks
29
- const mockCheckCommandExists = checkCommandExists;
30
- const mockLoadConfig = loadConfig;
31
- const mockExeca = execa;
32
- describe('startup', () => {
33
- let mockExit;
34
- let mockConsoleError;
35
- beforeEach(() => {
36
- jest.clearAllMocks();
37
- // Mock execa to reject by default (no kubectl context)
38
- mockExeca.mockRejectedValue(new Error('No context'));
39
- mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
40
- throw new Error('process.exit');
41
- });
42
- mockConsoleError = jest
43
- .spyOn(console, 'error')
44
- .mockImplementation(() => { });
45
- });
46
- afterEach(() => {
47
- mockExit.mockRestore();
48
- mockConsoleError.mockRestore();
49
- });
50
- it('returns config when all required commands are installed', async () => {
51
- const expectedConfig = {
52
- chat: {
53
- streaming: true,
54
- outputFormat: 'text',
55
- },
56
- };
57
- // Mock all commands as available
58
- mockCheckCommandExists.mockResolvedValue(true);
59
- mockLoadConfig.mockReturnValue(expectedConfig);
60
- const config = await startup();
61
- expect(config).toEqual(expectedConfig);
62
- expect(mockCheckCommandExists).toHaveBeenCalledWith('kubectl', [
63
- 'version',
64
- '--client',
65
- ]);
66
- expect(mockCheckCommandExists).toHaveBeenCalledWith('helm', [
67
- 'version',
68
- '--short',
69
- ]);
70
- expect(mockLoadConfig).toHaveBeenCalledTimes(1);
71
- expect(mockExit).not.toHaveBeenCalled();
72
- });
73
- it('exits with error when kubectl is missing', async () => {
74
- // Mock kubectl as missing, helm as available
75
- mockCheckCommandExists
76
- .mockResolvedValueOnce(false) // kubectl
77
- .mockResolvedValueOnce(true); // helm
78
- await expect(startup()).rejects.toThrow('process.exit');
79
- expect(mockConsoleError).toHaveBeenCalledWith('error: kubectl is required');
80
- expect(mockConsoleError).toHaveBeenCalledWith(' https://kubernetes.io/docs/tasks/tools/');
81
- expect(mockExit).toHaveBeenCalledWith(1);
82
- });
83
- it('exits with error when helm is missing', async () => {
84
- // Mock kubectl as available, helm as missing
85
- mockCheckCommandExists
86
- .mockResolvedValueOnce(true) // kubectl
87
- .mockResolvedValueOnce(false); // helm
88
- await expect(startup()).rejects.toThrow('process.exit');
89
- expect(mockConsoleError).toHaveBeenCalledWith('error: helm is required');
90
- expect(mockConsoleError).toHaveBeenCalledWith(' https://helm.sh/docs/intro/install/');
91
- expect(mockExit).toHaveBeenCalledWith(1);
92
- });
93
- it('exits with error when both commands are missing', async () => {
94
- // Mock both commands as missing
95
- mockCheckCommandExists.mockResolvedValue(false);
96
- await expect(startup()).rejects.toThrow('process.exit');
97
- expect(mockConsoleError).toHaveBeenCalledWith('error: kubectl is required');
98
- expect(mockConsoleError).toHaveBeenCalledWith(' https://kubernetes.io/docs/tasks/tools/');
99
- expect(mockConsoleError).toHaveBeenCalledWith('error: helm is required');
100
- expect(mockConsoleError).toHaveBeenCalledWith(' https://helm.sh/docs/intro/install/');
101
- expect(mockExit).toHaveBeenCalledWith(1);
102
- });
103
- it('checks commands with correct arguments', async () => {
104
- mockCheckCommandExists.mockResolvedValue(true);
105
- mockLoadConfig.mockReturnValue({ chat: {} });
106
- await startup();
107
- expect(mockCheckCommandExists).toHaveBeenCalledTimes(2);
108
- expect(mockCheckCommandExists).toHaveBeenNthCalledWith(1, 'kubectl', [
109
- 'version',
110
- '--client',
111
- ]);
112
- expect(mockCheckCommandExists).toHaveBeenNthCalledWith(2, 'helm', [
113
- 'version',
114
- '--short',
115
- ]);
116
- });
117
- it('loads config after checking requirements', async () => {
118
- mockCheckCommandExists.mockResolvedValue(true);
119
- const expectedConfig = { chat: { streaming: false } };
120
- mockLoadConfig.mockReturnValue(expectedConfig);
121
- const config = await startup();
122
- // Verify order - checkCommandExists should be called before loadConfig
123
- const checkCallOrder = mockCheckCommandExists.mock.invocationCallOrder[0];
124
- const loadCallOrder = mockLoadConfig.mock.invocationCallOrder[0];
125
- expect(checkCallOrder).toBeLessThan(loadCallOrder);
126
- expect(config).toEqual(expectedConfig);
127
- });
128
- it('includes cluster context when available', async () => {
129
- mockCheckCommandExists.mockResolvedValue(true);
130
- mockLoadConfig.mockReturnValue({ chat: { streaming: true } });
131
- // Mock successful kubectl context check
132
- mockExeca.mockResolvedValue({
133
- stdout: 'minikube',
134
- stderr: '',
135
- });
136
- const config = await startup();
137
- expect(config.clusterInfo).toEqual({
138
- type: 'unknown',
139
- context: 'minikube',
140
- });
141
- expect(mockExeca).toHaveBeenCalledWith('kubectl', ['config', 'current-context'], { timeout: 5000 });
142
- });
143
- it('handles missing kubectl context gracefully', async () => {
144
- mockCheckCommandExists.mockResolvedValue(true);
145
- const expectedConfig = { chat: { streaming: false } };
146
- mockLoadConfig.mockReturnValue(expectedConfig);
147
- // mockExeca already mocked to reject in beforeEach
148
- const config = await startup();
149
- expect(config).toEqual(expectedConfig);
150
- expect(config.clusterInfo).toBeUndefined();
151
- });
152
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,82 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
2
- import { readStdin } from './stdin.js';
3
- import { Readable } from 'stream';
4
- describe('readStdin', () => {
5
- let originalStdin;
6
- let mockStdin;
7
- beforeEach(() => {
8
- originalStdin = process.stdin;
9
- mockStdin = new Readable({
10
- read() { },
11
- });
12
- Object.defineProperty(process, 'stdin', {
13
- value: mockStdin,
14
- writable: true,
15
- configurable: true,
16
- });
17
- });
18
- afterEach(() => {
19
- Object.defineProperty(process, 'stdin', {
20
- value: originalStdin,
21
- writable: true,
22
- configurable: true,
23
- });
24
- jest.clearAllMocks();
25
- });
26
- it('should return empty string when stdin is TTY', async () => {
27
- mockStdin.isTTY = true;
28
- const result = await readStdin();
29
- expect(result).toBe('');
30
- });
31
- it('should read single chunk from stdin', async () => {
32
- mockStdin.isTTY = false;
33
- const promise = readStdin();
34
- mockStdin.push('test-query');
35
- mockStdin.push(null);
36
- const result = await promise;
37
- expect(result).toBe('test-query');
38
- });
39
- it('should read multiple chunks from stdin', async () => {
40
- mockStdin.isTTY = false;
41
- const promise = readStdin();
42
- mockStdin.push('test-');
43
- mockStdin.push('query-');
44
- mockStdin.push('name');
45
- mockStdin.push(null);
46
- const result = await promise;
47
- expect(result).toBe('test-query-name');
48
- });
49
- it('should trim whitespace from stdin input', async () => {
50
- mockStdin.isTTY = false;
51
- const promise = readStdin();
52
- mockStdin.push(' test-query \n');
53
- mockStdin.push(null);
54
- const result = await promise;
55
- expect(result).toBe('test-query');
56
- });
57
- it('should handle empty stdin input', async () => {
58
- mockStdin.isTTY = false;
59
- const promise = readStdin();
60
- mockStdin.push(null);
61
- const result = await promise;
62
- expect(result).toBe('');
63
- });
64
- it('should handle stdin with only whitespace', async () => {
65
- mockStdin.isTTY = false;
66
- const promise = readStdin();
67
- mockStdin.push(' \n\n ');
68
- mockStdin.push(null);
69
- const result = await promise;
70
- expect(result).toBe('');
71
- });
72
- it('should handle multiline input', async () => {
73
- mockStdin.isTTY = false;
74
- const promise = readStdin();
75
- mockStdin.push('line1\n');
76
- mockStdin.push('line2\n');
77
- mockStdin.push('line3');
78
- mockStdin.push(null);
79
- const result = await promise;
80
- expect(result).toBe('line1\nline2\nline3');
81
- });
82
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,14 +0,0 @@
1
- import { describe, it, expect } from '@jest/globals';
2
- import { parseTimeoutToSeconds } from './timeout.js';
3
- describe('parseTimeoutToSeconds', () => {
4
- it('should parse time units correctly', () => {
5
- expect(parseTimeoutToSeconds('30s')).toBe(30);
6
- expect(parseTimeoutToSeconds('2m')).toBe(120);
7
- expect(parseTimeoutToSeconds('1h')).toBe(3600);
8
- expect(parseTimeoutToSeconds('60')).toBe(60);
9
- });
10
- it('should throw error for invalid formats', () => {
11
- expect(() => parseTimeoutToSeconds('abc')).toThrow('Invalid timeout format');
12
- expect(() => parseTimeoutToSeconds('-5s')).toThrow('Invalid timeout format');
13
- });
14
- });