@agents-at-scale/ark 0.1.53 → 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 (115) hide show
  1. package/dist/commands/export/index.js +6 -4
  2. package/dist/commands/generate/generators/agent.js +2 -0
  3. package/dist/commands/generate/generators/marketplace.js +2 -0
  4. package/dist/commands/generate/generators/mcpserver.js +2 -0
  5. package/dist/commands/generate/generators/project.js +9 -2
  6. package/dist/commands/generate/generators/query.js +2 -0
  7. package/dist/commands/generate/generators/team.js +2 -0
  8. package/dist/commands/generate/templateDiscovery.js +1 -0
  9. package/dist/commands/generate/templateEngine.js +1 -3
  10. package/dist/commands/import/index.js +1 -1
  11. package/dist/commands/install/index.js +2 -1
  12. package/dist/commands/models/kubernetes/manifest-builder.js +27 -10
  13. package/dist/commands/models/providers/azure.d.ts +10 -7
  14. package/dist/commands/models/providers/azure.js +83 -21
  15. package/dist/commands/uninstall/index.js +1 -1
  16. package/dist/components/ChatUI.js +17 -16
  17. package/dist/components/statusChecker.js +3 -3
  18. package/dist/lib/arkApiClient.js +11 -9
  19. package/dist/lib/arkApiProxy.js +1 -0
  20. package/dist/lib/arkServiceProxy.js +5 -1
  21. package/dist/lib/chatClient.js +9 -0
  22. package/dist/lib/config.js +8 -3
  23. package/dist/lib/errors.js +3 -0
  24. package/dist/ui/asyncOperations/connectingToArk.js +2 -2
  25. package/package.json +16 -12
  26. package/dist/arkServices.spec.d.ts +0 -1
  27. package/dist/arkServices.spec.js +0 -138
  28. package/dist/commands/agents/index.spec.d.ts +0 -1
  29. package/dist/commands/agents/index.spec.js +0 -67
  30. package/dist/commands/cluster/get.spec.d.ts +0 -1
  31. package/dist/commands/cluster/get.spec.js +0 -92
  32. package/dist/commands/cluster/index.spec.d.ts +0 -1
  33. package/dist/commands/cluster/index.spec.js +0 -24
  34. package/dist/commands/completion/index.spec.d.ts +0 -1
  35. package/dist/commands/completion/index.spec.js +0 -34
  36. package/dist/commands/config/index.spec.d.ts +0 -1
  37. package/dist/commands/config/index.spec.js +0 -78
  38. package/dist/commands/evaluation/index.spec.d.ts +0 -1
  39. package/dist/commands/evaluation/index.spec.js +0 -161
  40. package/dist/commands/export/index.spec.d.ts +0 -1
  41. package/dist/commands/export/index.spec.js +0 -145
  42. package/dist/commands/import/index.spec.d.ts +0 -1
  43. package/dist/commands/import/index.spec.js +0 -46
  44. package/dist/commands/install/index.spec.d.ts +0 -1
  45. package/dist/commands/install/index.spec.js +0 -286
  46. package/dist/commands/marketplace/index.spec.d.ts +0 -1
  47. package/dist/commands/marketplace/index.spec.js +0 -88
  48. package/dist/commands/memory/index.spec.d.ts +0 -1
  49. package/dist/commands/memory/index.spec.js +0 -124
  50. package/dist/commands/models/create.spec.d.ts +0 -1
  51. package/dist/commands/models/create.spec.js +0 -167
  52. package/dist/commands/models/index.spec.d.ts +0 -1
  53. package/dist/commands/models/index.spec.js +0 -96
  54. package/dist/commands/models/providers/azure.spec.d.ts +0 -1
  55. package/dist/commands/models/providers/azure.spec.js +0 -232
  56. package/dist/commands/models/providers/bedrock.spec.d.ts +0 -1
  57. package/dist/commands/models/providers/bedrock.spec.js +0 -241
  58. package/dist/commands/models/providers/openai.spec.d.ts +0 -1
  59. package/dist/commands/models/providers/openai.spec.js +0 -180
  60. package/dist/commands/queries/delete.spec.d.ts +0 -1
  61. package/dist/commands/queries/delete.spec.js +0 -74
  62. package/dist/commands/queries/index.spec.d.ts +0 -1
  63. package/dist/commands/queries/index.spec.js +0 -167
  64. package/dist/commands/queries/list.spec.d.ts +0 -1
  65. package/dist/commands/queries/list.spec.js +0 -170
  66. package/dist/commands/queries/validation.spec.d.ts +0 -1
  67. package/dist/commands/queries/validation.spec.js +0 -27
  68. package/dist/commands/query/index.spec.d.ts +0 -1
  69. package/dist/commands/query/index.spec.js +0 -104
  70. package/dist/commands/targets/index.spec.d.ts +0 -1
  71. package/dist/commands/targets/index.spec.js +0 -154
  72. package/dist/commands/teams/index.spec.d.ts +0 -1
  73. package/dist/commands/teams/index.spec.js +0 -70
  74. package/dist/commands/tools/index.spec.d.ts +0 -1
  75. package/dist/commands/tools/index.spec.js +0 -70
  76. package/dist/commands/uninstall/index.spec.d.ts +0 -1
  77. package/dist/commands/uninstall/index.spec.js +0 -125
  78. package/dist/lib/arkServiceProxy.spec.d.ts +0 -1
  79. package/dist/lib/arkServiceProxy.spec.js +0 -100
  80. package/dist/lib/arkStatus.spec.d.ts +0 -1
  81. package/dist/lib/arkStatus.spec.js +0 -49
  82. package/dist/lib/chatClient.spec.d.ts +0 -1
  83. package/dist/lib/chatClient.spec.js +0 -108
  84. package/dist/lib/cluster.spec.d.ts +0 -1
  85. package/dist/lib/cluster.spec.js +0 -338
  86. package/dist/lib/commands.spec.d.ts +0 -1
  87. package/dist/lib/commands.spec.js +0 -146
  88. package/dist/lib/config.spec.d.ts +0 -1
  89. package/dist/lib/config.spec.js +0 -202
  90. package/dist/lib/duration.spec.d.ts +0 -1
  91. package/dist/lib/duration.spec.js +0 -13
  92. package/dist/lib/errors.spec.d.ts +0 -1
  93. package/dist/lib/errors.spec.js +0 -221
  94. package/dist/lib/executeQuery.spec.d.ts +0 -1
  95. package/dist/lib/executeQuery.spec.js +0 -325
  96. package/dist/lib/kubectl.spec.d.ts +0 -1
  97. package/dist/lib/kubectl.spec.js +0 -192
  98. package/dist/lib/marketplaceFetcher.spec.d.ts +0 -1
  99. package/dist/lib/marketplaceFetcher.spec.js +0 -225
  100. package/dist/lib/nextSteps.spec.d.ts +0 -1
  101. package/dist/lib/nextSteps.spec.js +0 -59
  102. package/dist/lib/output.spec.d.ts +0 -1
  103. package/dist/lib/output.spec.js +0 -123
  104. package/dist/lib/startup.spec.d.ts +0 -1
  105. package/dist/lib/startup.spec.js +0 -152
  106. package/dist/lib/stdin.spec.d.ts +0 -1
  107. package/dist/lib/stdin.spec.js +0 -82
  108. package/dist/lib/timeout.spec.d.ts +0 -1
  109. package/dist/lib/timeout.spec.js +0 -14
  110. package/dist/lib/waitForReady.spec.d.ts +0 -1
  111. package/dist/lib/waitForReady.spec.js +0 -104
  112. package/dist/marketplaceServices.spec.d.ts +0 -1
  113. package/dist/marketplaceServices.spec.js +0 -74
  114. package/dist/ui/statusFormatter.spec.d.ts +0 -1
  115. package/dist/ui/statusFormatter.spec.js +0 -58
@@ -1,161 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockExecuteDirectEvaluation = jest.fn();
4
- const mockExecuteQueryEvaluation = jest.fn();
5
- jest.unstable_mockModule('../../lib/executeEvaluation.js', () => ({
6
- executeDirectEvaluation: mockExecuteDirectEvaluation,
7
- executeQueryEvaluation: mockExecuteQueryEvaluation,
8
- }));
9
- const { createEvaluationCommand } = await import('./index.js');
10
- describe('createEvaluationCommand', () => {
11
- beforeEach(() => {
12
- jest.clearAllMocks();
13
- });
14
- it('should create an evaluation command', () => {
15
- const command = createEvaluationCommand({});
16
- expect(command).toBeInstanceOf(Command);
17
- expect(command.name()).toBe('evaluation');
18
- expect(command.description()).toBe('Execute evaluations against evaluators');
19
- });
20
- describe('direct evaluation', () => {
21
- it('should execute direct evaluation with required options', async () => {
22
- mockExecuteDirectEvaluation.mockResolvedValue(undefined);
23
- const command = createEvaluationCommand({});
24
- await command.parseAsync([
25
- 'node',
26
- 'test',
27
- 'my-evaluator',
28
- '--input',
29
- 'test-input',
30
- '--output',
31
- 'test-output',
32
- ]);
33
- expect(mockExecuteDirectEvaluation).toHaveBeenCalledWith({
34
- evaluatorName: 'my-evaluator',
35
- input: 'test-input',
36
- output: 'test-output',
37
- timeout: undefined,
38
- watchTimeout: undefined,
39
- });
40
- });
41
- it('should execute direct evaluation with timeout options', async () => {
42
- mockExecuteDirectEvaluation.mockResolvedValue(undefined);
43
- const command = createEvaluationCommand({});
44
- await command.parseAsync([
45
- 'node',
46
- 'test',
47
- 'my-evaluator',
48
- '--input',
49
- 'test-input',
50
- '--output',
51
- 'test-output',
52
- '--timeout',
53
- '10m',
54
- '--watch-timeout',
55
- '11m',
56
- ]);
57
- expect(mockExecuteDirectEvaluation).toHaveBeenCalledWith({
58
- evaluatorName: 'my-evaluator',
59
- input: 'test-input',
60
- output: 'test-output',
61
- timeout: '10m',
62
- watchTimeout: '11m',
63
- });
64
- });
65
- });
66
- describe('query evaluation', () => {
67
- it('should execute query evaluation with required arguments', async () => {
68
- mockExecuteQueryEvaluation.mockResolvedValue(undefined);
69
- const command = createEvaluationCommand({});
70
- await command.parseAsync(['node', 'test', 'my-evaluator', 'test-query']);
71
- expect(mockExecuteQueryEvaluation).toHaveBeenCalledWith({
72
- evaluatorName: 'my-evaluator',
73
- queryName: 'test-query',
74
- responseTarget: undefined,
75
- timeout: undefined,
76
- watchTimeout: undefined,
77
- });
78
- });
79
- it('should execute query evaluation from stdin', async () => {
80
- mockExecuteQueryEvaluation.mockResolvedValue(undefined);
81
- const command = createEvaluationCommand({});
82
- const mockStdin = {
83
- isTTY: false,
84
- setEncoding: jest.fn(),
85
- on: jest.fn((event, callback) => {
86
- if (event === 'data') {
87
- callback('piped-query-name');
88
- }
89
- else if (event === 'end') {
90
- callback();
91
- }
92
- }),
93
- };
94
- const originalStdin = process.stdin;
95
- Object.defineProperty(process, 'stdin', {
96
- value: mockStdin,
97
- writable: true,
98
- configurable: true,
99
- });
100
- try {
101
- await command.parseAsync(['node', 'test', 'my-evaluator']);
102
- expect(mockExecuteQueryEvaluation).toHaveBeenCalledWith({
103
- evaluatorName: 'my-evaluator',
104
- queryName: 'piped-query-name',
105
- responseTarget: undefined,
106
- timeout: undefined,
107
- watchTimeout: undefined,
108
- });
109
- }
110
- finally {
111
- Object.defineProperty(process, 'stdin', {
112
- value: originalStdin,
113
- writable: true,
114
- configurable: true,
115
- });
116
- }
117
- });
118
- it('should execute query evaluation with response-target option', async () => {
119
- mockExecuteQueryEvaluation.mockResolvedValue(undefined);
120
- const command = createEvaluationCommand({});
121
- await command.parseAsync([
122
- 'node',
123
- 'test',
124
- 'my-evaluator',
125
- 'test-query',
126
- '--response-target',
127
- 'agent:my-agent',
128
- ]);
129
- expect(mockExecuteQueryEvaluation).toHaveBeenCalledWith({
130
- evaluatorName: 'my-evaluator',
131
- queryName: 'test-query',
132
- responseTarget: 'agent:my-agent',
133
- timeout: undefined,
134
- watchTimeout: undefined,
135
- });
136
- });
137
- it('should execute query evaluation with all options', async () => {
138
- mockExecuteQueryEvaluation.mockResolvedValue(undefined);
139
- const command = createEvaluationCommand({});
140
- await command.parseAsync([
141
- 'node',
142
- 'test',
143
- 'my-evaluator',
144
- 'test-query',
145
- '--response-target',
146
- 'agent:my-agent',
147
- '--timeout',
148
- '10m',
149
- '--watch-timeout',
150
- '11m',
151
- ]);
152
- expect(mockExecuteQueryEvaluation).toHaveBeenCalledWith({
153
- evaluatorName: 'my-evaluator',
154
- queryName: 'test-query',
155
- responseTarget: 'agent:my-agent',
156
- timeout: '10m',
157
- watchTimeout: '11m',
158
- });
159
- });
160
- });
161
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,145 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockExeca = jest.fn();
4
- jest.unstable_mockModule('execa', () => ({
5
- execa: mockExeca,
6
- }));
7
- const mockWriteFile = jest.fn();
8
- jest.unstable_mockModule('fs/promises', () => ({
9
- writeFile: mockWriteFile,
10
- }));
11
- const mockOutput = {
12
- info: jest.fn(),
13
- success: jest.fn(),
14
- warning: jest.fn(),
15
- error: jest.fn(),
16
- };
17
- jest.unstable_mockModule('../../lib/output.js', () => ({
18
- default: mockOutput,
19
- }));
20
- const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
21
- throw new Error('process.exit called');
22
- }));
23
- const mockKubectlGetResponse = {
24
- "apiVersion": "v1",
25
- "items": [{ "spec": "foo" }],
26
- "kind": "List",
27
- "metadata": {
28
- "resourceVersion": ""
29
- }
30
- };
31
- const { createExportCommand } = await import('./index.js');
32
- describe('export command', () => {
33
- const mockConfig = {};
34
- beforeEach(() => {
35
- jest.clearAllMocks();
36
- });
37
- it('should create export command with correct description', () => {
38
- const command = createExportCommand(mockConfig);
39
- expect(command).toBeInstanceOf(Command);
40
- expect(command.name()).toBe('export');
41
- expect(command.description()).toBe('export ARK resources to a file');
42
- });
43
- it('should export all resource types by default', async () => {
44
- mockExeca.mockResolvedValue({
45
- stdout: JSON.stringify(mockKubectlGetResponse),
46
- });
47
- mockWriteFile.mockResolvedValue(undefined);
48
- const command = createExportCommand(mockConfig);
49
- await command.parseAsync(['node', 'test', '-o', 'test.yaml']);
50
- const expectedResourceTypes = [
51
- 'secrets',
52
- 'tools',
53
- 'models',
54
- 'agents',
55
- 'teams',
56
- 'evaluators',
57
- 'mcpservers',
58
- 'a2aservers',
59
- ];
60
- expect(mockExeca).toHaveBeenCalledTimes(expectedResourceTypes.length);
61
- for (const resourceType of expectedResourceTypes) {
62
- expect(mockExeca).toHaveBeenCalledWith('kubectl', expect.arrayContaining(['get', resourceType, '-o', 'json']), expect.any(Object));
63
- expect(mockOutput.success).toHaveBeenCalledWith(`found 1 ${resourceType}`);
64
- }
65
- expect(mockWriteFile).toHaveBeenCalledTimes(1);
66
- });
67
- it('should export types specified in config in dependency order', async () => {
68
- mockExeca.mockResolvedValue({
69
- stdout: JSON.stringify(mockKubectlGetResponse),
70
- });
71
- mockWriteFile.mockResolvedValue(undefined);
72
- const newDefaultTypes = ['teams', 'agents'];
73
- const modifiedConfig = { defaultExportTypes: newDefaultTypes };
74
- const command = createExportCommand(modifiedConfig);
75
- await command.parseAsync(['node', 'test', '-o', 'test.yaml']);
76
- expect(mockExeca.mock.calls).toEqual([
77
- ['kubectl', expect.arrayContaining(['get', 'agents']), expect.any(Object)],
78
- ['kubectl', expect.arrayContaining(['get', 'teams']), expect.any(Object)],
79
- ]);
80
- expect(mockWriteFile).toHaveBeenCalledTimes(1);
81
- });
82
- it('should filter by resource types when specified and export in order', async () => {
83
- mockExeca.mockResolvedValue({
84
- stdout: JSON.stringify(mockKubectlGetResponse),
85
- });
86
- mockWriteFile.mockResolvedValue(undefined);
87
- const command = createExportCommand(mockConfig);
88
- await command.parseAsync([
89
- 'node',
90
- 'test',
91
- '-t',
92
- 'agents,models',
93
- '-o',
94
- 'test.yaml',
95
- ]);
96
- expect(mockExeca.mock.calls).toEqual([
97
- ['kubectl', expect.arrayContaining(['get', 'models']), expect.any(Object)],
98
- ['kubectl', expect.arrayContaining(['get', 'agents']), expect.any(Object)],
99
- ]);
100
- });
101
- it('should use namespace filter when specified', async () => {
102
- mockExeca.mockResolvedValue({
103
- stdout: JSON.stringify(mockKubectlGetResponse),
104
- });
105
- mockWriteFile.mockResolvedValue(undefined);
106
- const command = createExportCommand(mockConfig);
107
- await command.parseAsync([
108
- 'node',
109
- 'test',
110
- '-n',
111
- 'custom-namespace',
112
- '-t',
113
- 'agents',
114
- '-o',
115
- 'test.yaml',
116
- ]);
117
- expect(mockExeca).toHaveBeenCalledWith('kubectl', expect.arrayContaining(['-n', 'custom-namespace']), expect.any(Object));
118
- expect(mockWriteFile).toHaveBeenCalledTimes(1);
119
- });
120
- it('should use label selector when specified', async () => {
121
- mockExeca.mockResolvedValue({
122
- stdout: JSON.stringify(mockKubectlGetResponse),
123
- });
124
- mockWriteFile.mockResolvedValue(undefined);
125
- const command = createExportCommand(mockConfig);
126
- await command.parseAsync([
127
- 'node',
128
- 'test',
129
- '-l',
130
- 'app=test',
131
- '-t',
132
- 'agents',
133
- '-o',
134
- 'test.yaml',
135
- ]);
136
- expect(mockExeca).toHaveBeenCalledWith('kubectl', expect.arrayContaining(['-l', 'app=test']), expect.any(Object));
137
- expect(mockWriteFile).toHaveBeenCalledTimes(1);
138
- });
139
- it('fails if kubectl get fails for a resource type', async () => {
140
- mockExeca.mockRejectedValue('Export broke');
141
- const command = createExportCommand(mockConfig);
142
- await command.parseAsync(['node', 'test']);
143
- expect(mockOutput.error).toHaveBeenCalledWith('export failed:', 'Export broke');
144
- });
145
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,46 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockExeca = jest.fn();
4
- jest.unstable_mockModule('execa', () => ({
5
- execa: mockExeca,
6
- }));
7
- const mockOutput = {
8
- info: jest.fn(),
9
- success: jest.fn(),
10
- warning: jest.fn(),
11
- error: jest.fn(),
12
- };
13
- jest.unstable_mockModule('../../lib/output.js', () => ({
14
- default: mockOutput,
15
- }));
16
- const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
17
- throw new Error('process.exit called');
18
- }));
19
- const { createImportCommand } = await import('./index.js');
20
- describe('import command', () => {
21
- const mockConfig = {};
22
- beforeEach(() => {
23
- jest.clearAllMocks();
24
- });
25
- it('should create import command with correct description', () => {
26
- const command = createImportCommand(mockConfig);
27
- expect(command).toBeInstanceOf(Command);
28
- expect(command.name()).toBe('import');
29
- expect(command.description()).toBe('import ARK resources from a file');
30
- });
31
- it('should use kubectl to import', async () => {
32
- mockExeca.mockResolvedValue({
33
- stdout: JSON.stringify({ items: [] }),
34
- });
35
- const command = createImportCommand(mockConfig);
36
- await command.parseAsync(['node', 'test', 'test.yaml']);
37
- expect(mockExeca).toHaveBeenCalledWith('kubectl', ['create', '-f', 'test.yaml'], expect.any(Object));
38
- });
39
- it('exits with error when kubectl create has error', async () => {
40
- mockExeca.mockRejectedValue('Import broke');
41
- const command = createImportCommand(mockConfig);
42
- await expect(command.parseAsync(['node', 'test', 'test.yaml'])).rejects.toThrow('process.exit called');
43
- expect(mockOutput.error).toHaveBeenCalledWith('import failed:', 'Import broke');
44
- expect(mockExit).toHaveBeenCalledWith(1);
45
- });
46
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,286 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockExeca = jest.fn(() => Promise.resolve());
4
- jest.unstable_mockModule('execa', () => ({
5
- execa: mockExeca,
6
- }));
7
- const mockGetClusterInfo = jest.fn();
8
- jest.unstable_mockModule('../../lib/cluster.js', () => ({
9
- getClusterInfo: mockGetClusterInfo,
10
- }));
11
- const mockGetInstallableServices = jest.fn();
12
- const mockArkServices = {};
13
- const mockArkDependencies = {};
14
- jest.unstable_mockModule('../../arkServices.js', () => ({
15
- getInstallableServices: mockGetInstallableServices,
16
- arkServices: mockArkServices,
17
- arkDependencies: mockArkDependencies,
18
- }));
19
- const mockOutput = {
20
- error: jest.fn(),
21
- info: jest.fn(),
22
- success: jest.fn(),
23
- warning: jest.fn(),
24
- };
25
- jest.unstable_mockModule('../../lib/output.js', () => ({
26
- default: mockOutput,
27
- }));
28
- const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
29
- throw new Error('process.exit called');
30
- }));
31
- jest.spyOn(console, 'log').mockImplementation(() => { });
32
- jest.spyOn(console, 'error').mockImplementation(() => { });
33
- const { createInstallCommand } = await import('./index.js');
34
- describe('install command', () => {
35
- const mockConfig = {
36
- clusterInfo: {
37
- context: 'test-cluster',
38
- type: 'minikube',
39
- namespace: 'default',
40
- },
41
- };
42
- beforeEach(() => {
43
- jest.clearAllMocks();
44
- mockGetClusterInfo.mockResolvedValue({
45
- context: 'test-cluster',
46
- type: 'minikube',
47
- namespace: 'default',
48
- });
49
- });
50
- it('creates command with correct structure', () => {
51
- const command = createInstallCommand(mockConfig);
52
- expect(command).toBeInstanceOf(Command);
53
- expect(command.name()).toBe('install');
54
- });
55
- it('installs single service with correct helm parameters', async () => {
56
- const mockService = {
57
- name: 'ark-api',
58
- helmReleaseName: 'ark-api',
59
- chartPath: './charts/ark-api',
60
- namespace: 'ark-system',
61
- installArgs: ['--set', 'image.tag=latest'],
62
- };
63
- mockGetInstallableServices.mockReturnValue({
64
- 'ark-api': mockService,
65
- });
66
- const command = createInstallCommand(mockConfig);
67
- await command.parseAsync(['node', 'test', 'ark-api']);
68
- expect(mockExeca).toHaveBeenCalledWith('helm', [
69
- 'upgrade',
70
- '--install',
71
- 'ark-api',
72
- './charts/ark-api',
73
- '--namespace',
74
- 'ark-system',
75
- '--set',
76
- 'image.tag=latest',
77
- ], { stdio: 'inherit' });
78
- expect(mockOutput.success).toHaveBeenCalledWith('ark-api installed successfully');
79
- });
80
- it('shows error when service not found', async () => {
81
- mockGetInstallableServices.mockReturnValue({
82
- 'ark-api': { name: 'ark-api' },
83
- 'ark-controller': { name: 'ark-controller' },
84
- });
85
- const command = createInstallCommand(mockConfig);
86
- await expect(command.parseAsync(['node', 'test', 'invalid-service'])).rejects.toThrow('process.exit called');
87
- expect(mockOutput.error).toHaveBeenCalledWith("service 'invalid-service' not found");
88
- expect(mockOutput.info).toHaveBeenCalledWith('available services:');
89
- expect(mockOutput.info).toHaveBeenCalledWith(' ark-api');
90
- expect(mockOutput.info).toHaveBeenCalledWith(' ark-controller');
91
- expect(mockExit).toHaveBeenCalledWith(1);
92
- });
93
- it('handles service without namespace (uses current context)', async () => {
94
- const mockService = {
95
- name: 'ark-dashboard',
96
- helmReleaseName: 'ark-dashboard',
97
- chartPath: './charts/ark-dashboard',
98
- // namespace is undefined - should use current context
99
- installArgs: ['--set', 'replicas=2'],
100
- };
101
- mockGetInstallableServices.mockReturnValue({
102
- 'ark-dashboard': mockService,
103
- });
104
- const command = createInstallCommand(mockConfig);
105
- await command.parseAsync(['node', 'test', 'ark-dashboard']);
106
- // Should NOT include --namespace flag
107
- expect(mockExeca).toHaveBeenCalledWith('helm', [
108
- 'upgrade',
109
- '--install',
110
- 'ark-dashboard',
111
- './charts/ark-dashboard',
112
- '--set',
113
- 'replicas=2',
114
- ], { stdio: 'inherit' });
115
- });
116
- it('handles service without installArgs', async () => {
117
- const mockService = {
118
- name: 'simple-service',
119
- helmReleaseName: 'simple-service',
120
- chartPath: './charts/simple',
121
- namespace: 'default',
122
- };
123
- mockGetInstallableServices.mockReturnValue({
124
- 'simple-service': mockService,
125
- });
126
- const command = createInstallCommand(mockConfig);
127
- await command.parseAsync(['node', 'test', 'simple-service']);
128
- expect(mockExeca).toHaveBeenCalledWith('helm', [
129
- 'upgrade',
130
- '--install',
131
- 'simple-service',
132
- './charts/simple',
133
- '--namespace',
134
- 'default',
135
- ], { stdio: 'inherit' });
136
- });
137
- it('exits when cluster not connected', async () => {
138
- mockGetClusterInfo.mockResolvedValue({ error: true });
139
- const command = createInstallCommand({});
140
- await expect(command.parseAsync(['node', 'test', 'ark-api'])).rejects.toThrow('process.exit called');
141
- expect(mockExit).toHaveBeenCalledWith(1);
142
- });
143
- describe('checkAndCleanFailedRelease', () => {
144
- it('uninstalls release in pending-install state', async () => {
145
- const mockService = {
146
- name: 'ark-api',
147
- helmReleaseName: 'ark-api',
148
- chartPath: './charts/ark-api',
149
- namespace: 'ark-system',
150
- };
151
- mockGetInstallableServices.mockReturnValue({
152
- 'ark-api': mockService,
153
- });
154
- mockExeca
155
- .mockResolvedValueOnce({
156
- stdout: 'NAME: ark-api\nSTATUS: pending-install\n',
157
- })
158
- .mockResolvedValueOnce({})
159
- .mockResolvedValueOnce({});
160
- const command = createInstallCommand(mockConfig);
161
- await command.parseAsync(['node', 'test', 'ark-api']);
162
- expect(mockExeca).toHaveBeenCalledWith('helm', ['status', 'ark-api', '--namespace', 'ark-system'], {});
163
- expect(mockExeca).toHaveBeenCalledWith('helm', ['uninstall', 'ark-api', '--namespace', 'ark-system'], { stdio: 'inherit' });
164
- expect(mockExeca).toHaveBeenCalledWith('helm', [
165
- 'upgrade',
166
- '--install',
167
- 'ark-api',
168
- './charts/ark-api',
169
- '--namespace',
170
- 'ark-system',
171
- ], { stdio: 'inherit' });
172
- });
173
- it('uninstalls release in failed state', async () => {
174
- const mockService = {
175
- name: 'ark-api',
176
- helmReleaseName: 'ark-api',
177
- chartPath: './charts/ark-api',
178
- namespace: 'ark-system',
179
- };
180
- mockGetInstallableServices.mockReturnValue({
181
- 'ark-api': mockService,
182
- });
183
- mockExeca
184
- .mockResolvedValueOnce({
185
- stdout: 'NAME: ark-api\nSTATUS: failed\n',
186
- })
187
- .mockResolvedValueOnce({})
188
- .mockResolvedValueOnce({});
189
- const command = createInstallCommand(mockConfig);
190
- await command.parseAsync(['node', 'test', 'ark-api']);
191
- expect(mockExeca).toHaveBeenCalledWith('helm', ['uninstall', 'ark-api', '--namespace', 'ark-system'], { stdio: 'inherit' });
192
- });
193
- it('uninstalls release in uninstalling state', async () => {
194
- const mockService = {
195
- name: 'ark-dashboard',
196
- helmReleaseName: 'ark-dashboard',
197
- chartPath: './charts/ark-dashboard',
198
- namespace: 'default',
199
- };
200
- mockGetInstallableServices.mockReturnValue({
201
- 'ark-dashboard': mockService,
202
- });
203
- mockExeca
204
- .mockResolvedValueOnce({
205
- stdout: 'NAME: ark-dashboard\nSTATUS: uninstalling\nREVISION: 2\n',
206
- })
207
- .mockResolvedValueOnce({})
208
- .mockResolvedValueOnce({});
209
- const command = createInstallCommand(mockConfig);
210
- await command.parseAsync(['node', 'test', 'ark-dashboard']);
211
- expect(mockExeca).toHaveBeenCalledWith('helm', ['uninstall', 'ark-dashboard', '--namespace', 'default'], { stdio: 'inherit' });
212
- });
213
- it('does not uninstall release in deployed state', async () => {
214
- const mockService = {
215
- name: 'ark-api',
216
- helmReleaseName: 'ark-api',
217
- chartPath: './charts/ark-api',
218
- namespace: 'ark-system',
219
- };
220
- mockGetInstallableServices.mockReturnValue({
221
- 'ark-api': mockService,
222
- });
223
- mockExeca
224
- .mockResolvedValueOnce({
225
- stdout: 'NAME: ark-api\nSTATUS: deployed\n',
226
- })
227
- .mockResolvedValueOnce({});
228
- const command = createInstallCommand(mockConfig);
229
- await command.parseAsync(['node', 'test', 'ark-api']);
230
- const uninstallCalls = mockExeca.mock.calls.filter((call) => call[0] === 'helm' && call[1][0] === 'uninstall');
231
- expect(uninstallCalls).toHaveLength(0);
232
- expect(mockExeca).toHaveBeenCalledWith('helm', [
233
- 'upgrade',
234
- '--install',
235
- 'ark-api',
236
- './charts/ark-api',
237
- '--namespace',
238
- 'ark-system',
239
- ], { stdio: 'inherit' });
240
- });
241
- it('handles helm status errors gracefully', async () => {
242
- const mockService = {
243
- name: 'ark-api',
244
- helmReleaseName: 'ark-api',
245
- chartPath: './charts/ark-api',
246
- namespace: 'ark-system',
247
- };
248
- mockGetInstallableServices.mockReturnValue({
249
- 'ark-api': mockService,
250
- });
251
- mockExeca
252
- .mockRejectedValueOnce(new Error('release not found'))
253
- .mockResolvedValueOnce({});
254
- const command = createInstallCommand(mockConfig);
255
- await command.parseAsync(['node', 'test', 'ark-api']);
256
- expect(mockExeca).toHaveBeenCalledWith('helm', [
257
- 'upgrade',
258
- '--install',
259
- 'ark-api',
260
- './charts/ark-api',
261
- '--namespace',
262
- 'ark-system',
263
- ], { stdio: 'inherit' });
264
- });
265
- it('handles service without namespace', async () => {
266
- const mockService = {
267
- name: 'ark-dashboard',
268
- helmReleaseName: 'ark-dashboard',
269
- chartPath: './charts/ark-dashboard',
270
- };
271
- mockGetInstallableServices.mockReturnValue({
272
- 'ark-dashboard': mockService,
273
- });
274
- mockExeca
275
- .mockResolvedValueOnce({
276
- stdout: 'NAME: ark-dashboard\nSTATUS: failed\n',
277
- })
278
- .mockResolvedValueOnce({})
279
- .mockResolvedValueOnce({});
280
- const command = createInstallCommand(mockConfig);
281
- await command.parseAsync(['node', 'test', 'ark-dashboard']);
282
- expect(mockExeca).toHaveBeenCalledWith('helm', ['status', 'ark-dashboard'], {});
283
- expect(mockExeca).toHaveBeenCalledWith('helm', ['uninstall', 'ark-dashboard'], { stdio: 'inherit' });
284
- });
285
- });
286
- });
@@ -1 +0,0 @@
1
- export {};