@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.
- package/dist/commands/export/index.js +6 -4
- package/dist/commands/generate/generators/agent.js +2 -0
- package/dist/commands/generate/generators/marketplace.js +2 -0
- package/dist/commands/generate/generators/mcpserver.js +2 -0
- package/dist/commands/generate/generators/project.js +9 -2
- package/dist/commands/generate/generators/query.js +2 -0
- package/dist/commands/generate/generators/team.js +2 -0
- package/dist/commands/generate/templateDiscovery.js +1 -0
- package/dist/commands/generate/templateEngine.js +1 -3
- package/dist/commands/import/index.js +1 -1
- package/dist/commands/install/index.js +2 -1
- package/dist/commands/models/kubernetes/manifest-builder.js +27 -10
- package/dist/commands/models/providers/azure.d.ts +10 -7
- package/dist/commands/models/providers/azure.js +83 -21
- package/dist/commands/uninstall/index.js +1 -1
- package/dist/components/ChatUI.js +17 -16
- package/dist/components/statusChecker.js +3 -3
- package/dist/lib/arkApiClient.js +11 -9
- package/dist/lib/arkApiProxy.js +1 -0
- package/dist/lib/arkServiceProxy.js +5 -1
- package/dist/lib/chatClient.js +9 -0
- package/dist/lib/config.js +8 -3
- package/dist/lib/errors.js +3 -0
- package/dist/ui/asyncOperations/connectingToArk.js +2 -2
- package/package.json +16 -12
- package/dist/arkServices.spec.d.ts +0 -1
- package/dist/arkServices.spec.js +0 -138
- package/dist/commands/agents/index.spec.d.ts +0 -1
- package/dist/commands/agents/index.spec.js +0 -67
- package/dist/commands/cluster/get.spec.d.ts +0 -1
- package/dist/commands/cluster/get.spec.js +0 -92
- package/dist/commands/cluster/index.spec.d.ts +0 -1
- package/dist/commands/cluster/index.spec.js +0 -24
- package/dist/commands/completion/index.spec.d.ts +0 -1
- package/dist/commands/completion/index.spec.js +0 -34
- package/dist/commands/config/index.spec.d.ts +0 -1
- package/dist/commands/config/index.spec.js +0 -78
- package/dist/commands/evaluation/index.spec.d.ts +0 -1
- package/dist/commands/evaluation/index.spec.js +0 -161
- package/dist/commands/export/index.spec.d.ts +0 -1
- package/dist/commands/export/index.spec.js +0 -145
- package/dist/commands/import/index.spec.d.ts +0 -1
- package/dist/commands/import/index.spec.js +0 -46
- package/dist/commands/install/index.spec.d.ts +0 -1
- package/dist/commands/install/index.spec.js +0 -286
- package/dist/commands/marketplace/index.spec.d.ts +0 -1
- package/dist/commands/marketplace/index.spec.js +0 -88
- package/dist/commands/memory/index.spec.d.ts +0 -1
- package/dist/commands/memory/index.spec.js +0 -124
- package/dist/commands/models/create.spec.d.ts +0 -1
- package/dist/commands/models/create.spec.js +0 -167
- package/dist/commands/models/index.spec.d.ts +0 -1
- package/dist/commands/models/index.spec.js +0 -96
- package/dist/commands/models/providers/azure.spec.d.ts +0 -1
- package/dist/commands/models/providers/azure.spec.js +0 -232
- package/dist/commands/models/providers/bedrock.spec.d.ts +0 -1
- package/dist/commands/models/providers/bedrock.spec.js +0 -241
- package/dist/commands/models/providers/openai.spec.d.ts +0 -1
- package/dist/commands/models/providers/openai.spec.js +0 -180
- package/dist/commands/queries/delete.spec.d.ts +0 -1
- package/dist/commands/queries/delete.spec.js +0 -74
- package/dist/commands/queries/index.spec.d.ts +0 -1
- package/dist/commands/queries/index.spec.js +0 -167
- package/dist/commands/queries/list.spec.d.ts +0 -1
- package/dist/commands/queries/list.spec.js +0 -170
- package/dist/commands/queries/validation.spec.d.ts +0 -1
- package/dist/commands/queries/validation.spec.js +0 -27
- package/dist/commands/query/index.spec.d.ts +0 -1
- package/dist/commands/query/index.spec.js +0 -104
- package/dist/commands/targets/index.spec.d.ts +0 -1
- package/dist/commands/targets/index.spec.js +0 -154
- package/dist/commands/teams/index.spec.d.ts +0 -1
- package/dist/commands/teams/index.spec.js +0 -70
- package/dist/commands/tools/index.spec.d.ts +0 -1
- package/dist/commands/tools/index.spec.js +0 -70
- package/dist/commands/uninstall/index.spec.d.ts +0 -1
- package/dist/commands/uninstall/index.spec.js +0 -125
- package/dist/lib/arkServiceProxy.spec.d.ts +0 -1
- package/dist/lib/arkServiceProxy.spec.js +0 -100
- package/dist/lib/arkStatus.spec.d.ts +0 -1
- package/dist/lib/arkStatus.spec.js +0 -49
- package/dist/lib/chatClient.spec.d.ts +0 -1
- package/dist/lib/chatClient.spec.js +0 -108
- package/dist/lib/cluster.spec.d.ts +0 -1
- package/dist/lib/cluster.spec.js +0 -338
- package/dist/lib/commands.spec.d.ts +0 -1
- package/dist/lib/commands.spec.js +0 -146
- package/dist/lib/config.spec.d.ts +0 -1
- package/dist/lib/config.spec.js +0 -202
- package/dist/lib/duration.spec.d.ts +0 -1
- package/dist/lib/duration.spec.js +0 -13
- package/dist/lib/errors.spec.d.ts +0 -1
- package/dist/lib/errors.spec.js +0 -221
- package/dist/lib/executeQuery.spec.d.ts +0 -1
- package/dist/lib/executeQuery.spec.js +0 -325
- package/dist/lib/kubectl.spec.d.ts +0 -1
- package/dist/lib/kubectl.spec.js +0 -192
- package/dist/lib/marketplaceFetcher.spec.d.ts +0 -1
- package/dist/lib/marketplaceFetcher.spec.js +0 -225
- package/dist/lib/nextSteps.spec.d.ts +0 -1
- package/dist/lib/nextSteps.spec.js +0 -59
- package/dist/lib/output.spec.d.ts +0 -1
- package/dist/lib/output.spec.js +0 -123
- package/dist/lib/startup.spec.d.ts +0 -1
- package/dist/lib/startup.spec.js +0 -152
- package/dist/lib/stdin.spec.d.ts +0 -1
- package/dist/lib/stdin.spec.js +0 -82
- package/dist/lib/timeout.spec.d.ts +0 -1
- package/dist/lib/timeout.spec.js +0 -14
- package/dist/lib/waitForReady.spec.d.ts +0 -1
- package/dist/lib/waitForReady.spec.js +0 -104
- package/dist/marketplaceServices.spec.d.ts +0 -1
- package/dist/marketplaceServices.spec.js +0 -74
- package/dist/ui/statusFormatter.spec.d.ts +0 -1
- 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 {};
|