@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,108 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { QUERY_ANNOTATIONS } from './constants.js';
|
|
3
|
-
const mockCreateChatCompletion = jest.fn();
|
|
4
|
-
const mockArkApiClient = {
|
|
5
|
-
createChatCompletion: mockCreateChatCompletion,
|
|
6
|
-
createChatCompletionStream: jest.fn(),
|
|
7
|
-
getQueryTargets: jest.fn(),
|
|
8
|
-
};
|
|
9
|
-
const { ChatClient } = await import('./chatClient.js');
|
|
10
|
-
describe('ChatClient', () => {
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
jest.clearAllMocks();
|
|
13
|
-
});
|
|
14
|
-
describe('sendMessage', () => {
|
|
15
|
-
it('should include sessionId directly in metadata when provided', async () => {
|
|
16
|
-
const client = new ChatClient(mockArkApiClient);
|
|
17
|
-
mockCreateChatCompletion.mockResolvedValue({
|
|
18
|
-
id: 'test-id',
|
|
19
|
-
object: 'chat.completion',
|
|
20
|
-
created: 1234567890,
|
|
21
|
-
model: 'test-model',
|
|
22
|
-
choices: [
|
|
23
|
-
{
|
|
24
|
-
index: 0,
|
|
25
|
-
message: { role: 'assistant', content: 'Hello' },
|
|
26
|
-
finish_reason: 'stop',
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
usage: {
|
|
30
|
-
prompt_tokens: 10,
|
|
31
|
-
completion_tokens: 5,
|
|
32
|
-
total_tokens: 15,
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
await client.sendMessage('agent/test-agent', [{ role: 'user', content: 'Hello' }], { streamingEnabled: false, sessionId: 'test-session-123' });
|
|
36
|
-
expect(mockCreateChatCompletion).toHaveBeenCalledWith(expect.objectContaining({
|
|
37
|
-
model: 'agent/test-agent',
|
|
38
|
-
messages: [{ role: 'user', content: 'Hello' }],
|
|
39
|
-
metadata: {
|
|
40
|
-
sessionId: 'test-session-123',
|
|
41
|
-
},
|
|
42
|
-
}));
|
|
43
|
-
});
|
|
44
|
-
it('should include both sessionId in metadata and a2aContextId in queryAnnotations when both provided', async () => {
|
|
45
|
-
const client = new ChatClient(mockArkApiClient);
|
|
46
|
-
mockCreateChatCompletion.mockResolvedValue({
|
|
47
|
-
id: 'test-id',
|
|
48
|
-
object: 'chat.completion',
|
|
49
|
-
created: 1234567890,
|
|
50
|
-
model: 'test-model',
|
|
51
|
-
choices: [
|
|
52
|
-
{
|
|
53
|
-
index: 0,
|
|
54
|
-
message: { role: 'assistant', content: 'Hello' },
|
|
55
|
-
finish_reason: 'stop',
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
usage: {
|
|
59
|
-
prompt_tokens: 10,
|
|
60
|
-
completion_tokens: 5,
|
|
61
|
-
total_tokens: 15,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
await client.sendMessage('agent/test-agent', [{ role: 'user', content: 'Hello' }], {
|
|
65
|
-
streamingEnabled: false,
|
|
66
|
-
sessionId: 'test-session-123',
|
|
67
|
-
a2aContextId: 'a2a-context-456',
|
|
68
|
-
});
|
|
69
|
-
expect(mockCreateChatCompletion).toHaveBeenCalled();
|
|
70
|
-
const callArgs = mockCreateChatCompletion.mock.calls[0][0];
|
|
71
|
-
expect(callArgs.model).toBe('agent/test-agent');
|
|
72
|
-
expect(callArgs.messages).toEqual([{ role: 'user', content: 'Hello' }]);
|
|
73
|
-
expect(callArgs.metadata).toBeDefined();
|
|
74
|
-
expect(callArgs.metadata.sessionId).toBe('test-session-123');
|
|
75
|
-
expect(callArgs.metadata.queryAnnotations).toBeDefined();
|
|
76
|
-
const queryAnnotations = JSON.parse(callArgs.metadata.queryAnnotations);
|
|
77
|
-
expect(queryAnnotations[QUERY_ANNOTATIONS.A2A_CONTEXT_ID]).toBe('a2a-context-456');
|
|
78
|
-
});
|
|
79
|
-
it('should not include metadata when neither sessionId nor a2aContextId is provided', async () => {
|
|
80
|
-
const client = new ChatClient(mockArkApiClient);
|
|
81
|
-
mockCreateChatCompletion.mockResolvedValue({
|
|
82
|
-
id: 'test-id',
|
|
83
|
-
object: 'chat.completion',
|
|
84
|
-
created: 1234567890,
|
|
85
|
-
model: 'test-model',
|
|
86
|
-
choices: [
|
|
87
|
-
{
|
|
88
|
-
index: 0,
|
|
89
|
-
message: { role: 'assistant', content: 'Hello' },
|
|
90
|
-
finish_reason: 'stop',
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
usage: {
|
|
94
|
-
prompt_tokens: 10,
|
|
95
|
-
completion_tokens: 5,
|
|
96
|
-
total_tokens: 15,
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
await client.sendMessage('agent/test-agent', [{ role: 'user', content: 'Hello' }], { streamingEnabled: false });
|
|
100
|
-
expect(mockCreateChatCompletion).toHaveBeenCalledWith(expect.objectContaining({
|
|
101
|
-
model: 'agent/test-agent',
|
|
102
|
-
messages: [{ role: 'user', content: 'Hello' }],
|
|
103
|
-
}));
|
|
104
|
-
const callArgs = mockCreateChatCompletion.mock.calls[0];
|
|
105
|
-
expect(callArgs[0].metadata).toBeUndefined();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/lib/cluster.spec.js
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
const mockExeca = jest.fn();
|
|
3
|
-
jest.unstable_mockModule('execa', () => ({
|
|
4
|
-
execa: mockExeca,
|
|
5
|
-
}));
|
|
6
|
-
const { getClusterInfo, detectClusterType } = await import('./cluster.js');
|
|
7
|
-
describe('cluster', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
jest.clearAllMocks();
|
|
10
|
-
});
|
|
11
|
-
describe('detectClusterType', () => {
|
|
12
|
-
it('detects minikube cluster', async () => {
|
|
13
|
-
mockExeca.mockResolvedValue({ stdout: 'minikube' });
|
|
14
|
-
const result = await detectClusterType();
|
|
15
|
-
expect(result).toEqual({ type: 'minikube', context: 'minikube' });
|
|
16
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
17
|
-
'config',
|
|
18
|
-
'current-context',
|
|
19
|
-
]);
|
|
20
|
-
});
|
|
21
|
-
it('detects kind cluster', async () => {
|
|
22
|
-
mockExeca.mockResolvedValue({ stdout: 'kind-kind' });
|
|
23
|
-
const result = await detectClusterType();
|
|
24
|
-
expect(result).toEqual({ type: 'kind', context: 'kind-kind' });
|
|
25
|
-
});
|
|
26
|
-
it('detects k3s cluster', async () => {
|
|
27
|
-
mockExeca.mockResolvedValue({ stdout: 'k3s-default' });
|
|
28
|
-
const result = await detectClusterType();
|
|
29
|
-
expect(result).toEqual({ type: 'k3s', context: 'k3s-default' });
|
|
30
|
-
});
|
|
31
|
-
it('detects docker-desktop cluster', async () => {
|
|
32
|
-
mockExeca.mockResolvedValue({ stdout: 'docker-desktop' });
|
|
33
|
-
const result = await detectClusterType();
|
|
34
|
-
expect(result).toEqual({
|
|
35
|
-
type: 'docker-desktop',
|
|
36
|
-
context: 'docker-desktop',
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
it('detects gke cloud cluster', async () => {
|
|
40
|
-
mockExeca.mockResolvedValue({ stdout: 'gke_project_zone_cluster' });
|
|
41
|
-
const result = await detectClusterType();
|
|
42
|
-
expect(result).toEqual({
|
|
43
|
-
type: 'cloud',
|
|
44
|
-
context: 'gke_project_zone_cluster',
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
it('detects eks cloud cluster', async () => {
|
|
48
|
-
mockExeca.mockResolvedValue({
|
|
49
|
-
stdout: 'arn:aws:eks:region:account:cluster/name',
|
|
50
|
-
});
|
|
51
|
-
const result = await detectClusterType();
|
|
52
|
-
expect(result).toEqual({
|
|
53
|
-
type: 'cloud',
|
|
54
|
-
context: 'arn:aws:eks:region:account:cluster/name',
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
it('detects aks cloud cluster', async () => {
|
|
58
|
-
mockExeca.mockResolvedValue({ stdout: 'aks-cluster-name' });
|
|
59
|
-
const result = await detectClusterType();
|
|
60
|
-
expect(result).toEqual({ type: 'cloud', context: 'aks-cluster-name' });
|
|
61
|
-
});
|
|
62
|
-
it('returns unknown for unrecognized cluster', async () => {
|
|
63
|
-
mockExeca.mockResolvedValue({ stdout: 'some-other-cluster' });
|
|
64
|
-
const result = await detectClusterType();
|
|
65
|
-
expect(result).toEqual({ type: 'unknown', context: 'some-other-cluster' });
|
|
66
|
-
});
|
|
67
|
-
it('handles kubectl error', async () => {
|
|
68
|
-
mockExeca.mockRejectedValue(new Error('kubectl not found'));
|
|
69
|
-
const result = await detectClusterType();
|
|
70
|
-
expect(result).toEqual({ type: 'unknown', error: 'kubectl not found' });
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
describe('getClusterInfo', () => {
|
|
74
|
-
const mockConfig = {
|
|
75
|
-
'current-context': 'minikube',
|
|
76
|
-
contexts: [
|
|
77
|
-
{
|
|
78
|
-
name: 'minikube',
|
|
79
|
-
context: {
|
|
80
|
-
namespace: 'default',
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
};
|
|
85
|
-
it('gets minikube cluster info with IP', async () => {
|
|
86
|
-
mockExeca
|
|
87
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(mockConfig) })
|
|
88
|
-
.mockResolvedValueOnce({ stdout: 'minikube' })
|
|
89
|
-
.mockResolvedValueOnce({ stdout: '192.168.49.2' });
|
|
90
|
-
const result = await getClusterInfo();
|
|
91
|
-
expect(result).toEqual({
|
|
92
|
-
type: 'minikube',
|
|
93
|
-
context: 'minikube',
|
|
94
|
-
namespace: 'default',
|
|
95
|
-
ip: '192.168.49.2',
|
|
96
|
-
});
|
|
97
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
98
|
-
'config',
|
|
99
|
-
'view',
|
|
100
|
-
'--minify',
|
|
101
|
-
'-o',
|
|
102
|
-
'json',
|
|
103
|
-
]);
|
|
104
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
105
|
-
'config',
|
|
106
|
-
'current-context',
|
|
107
|
-
]);
|
|
108
|
-
expect(mockExeca).toHaveBeenCalledWith('minikube', ['ip']);
|
|
109
|
-
});
|
|
110
|
-
it('falls back to kubectl for minikube IP if minikube command fails', async () => {
|
|
111
|
-
mockExeca
|
|
112
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(mockConfig) })
|
|
113
|
-
.mockResolvedValueOnce({ stdout: 'minikube' })
|
|
114
|
-
.mockRejectedValueOnce(new Error('minikube not found'))
|
|
115
|
-
.mockResolvedValueOnce({ stdout: '192.168.49.2' });
|
|
116
|
-
const result = await getClusterInfo();
|
|
117
|
-
expect(result.ip).toBe('192.168.49.2');
|
|
118
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
119
|
-
'get',
|
|
120
|
-
'nodes',
|
|
121
|
-
'-o',
|
|
122
|
-
'jsonpath={.items[0].status.addresses[?(@.type=="InternalIP")].address}',
|
|
123
|
-
]);
|
|
124
|
-
});
|
|
125
|
-
it('gets kind cluster info with IP', async () => {
|
|
126
|
-
const kindConfig = {
|
|
127
|
-
'current-context': 'kind-kind',
|
|
128
|
-
contexts: [
|
|
129
|
-
{
|
|
130
|
-
name: 'kind-kind',
|
|
131
|
-
context: {
|
|
132
|
-
namespace: 'kube-system',
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
};
|
|
137
|
-
mockExeca
|
|
138
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(kindConfig) })
|
|
139
|
-
.mockResolvedValueOnce({ stdout: 'kind-kind' })
|
|
140
|
-
.mockResolvedValueOnce({ stdout: '172.18.0.2' });
|
|
141
|
-
const result = await getClusterInfo();
|
|
142
|
-
expect(result).toEqual({
|
|
143
|
-
type: 'kind',
|
|
144
|
-
context: 'kind-kind',
|
|
145
|
-
namespace: 'kube-system',
|
|
146
|
-
ip: '172.18.0.2',
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
it('gets docker-desktop cluster info', async () => {
|
|
150
|
-
const dockerConfig = {
|
|
151
|
-
'current-context': 'docker-desktop',
|
|
152
|
-
contexts: [
|
|
153
|
-
{
|
|
154
|
-
name: 'docker-desktop',
|
|
155
|
-
context: {},
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
};
|
|
159
|
-
mockExeca
|
|
160
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(dockerConfig) })
|
|
161
|
-
.mockResolvedValueOnce({ stdout: 'docker-desktop' });
|
|
162
|
-
const result = await getClusterInfo();
|
|
163
|
-
expect(result).toEqual({
|
|
164
|
-
type: 'docker-desktop',
|
|
165
|
-
context: 'docker-desktop',
|
|
166
|
-
namespace: 'default',
|
|
167
|
-
ip: 'localhost',
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
it('gets cloud cluster info with load balancer IP', async () => {
|
|
171
|
-
const cloudConfig = {
|
|
172
|
-
'current-context': 'gke_project_zone_cluster',
|
|
173
|
-
contexts: [
|
|
174
|
-
{
|
|
175
|
-
name: 'gke_project_zone_cluster',
|
|
176
|
-
context: {
|
|
177
|
-
namespace: 'production',
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
};
|
|
182
|
-
mockExeca
|
|
183
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(cloudConfig) })
|
|
184
|
-
.mockResolvedValueOnce({ stdout: 'gke_project_zone_cluster' })
|
|
185
|
-
.mockResolvedValueOnce({ stdout: '35.201.125.17' });
|
|
186
|
-
const result = await getClusterInfo();
|
|
187
|
-
expect(result).toEqual({
|
|
188
|
-
type: 'cloud',
|
|
189
|
-
context: 'gke_project_zone_cluster',
|
|
190
|
-
namespace: 'production',
|
|
191
|
-
ip: '35.201.125.17',
|
|
192
|
-
});
|
|
193
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
194
|
-
'get',
|
|
195
|
-
'svc',
|
|
196
|
-
'-n',
|
|
197
|
-
'istio-system',
|
|
198
|
-
'istio-ingressgateway',
|
|
199
|
-
'-o',
|
|
200
|
-
'jsonpath={.status.loadBalancer.ingress[0].ip}',
|
|
201
|
-
]);
|
|
202
|
-
});
|
|
203
|
-
it('falls back to hostname for cloud cluster if no IP', async () => {
|
|
204
|
-
const cloudConfig = {
|
|
205
|
-
'current-context': 'eks-cluster',
|
|
206
|
-
contexts: [
|
|
207
|
-
{
|
|
208
|
-
name: 'eks-cluster',
|
|
209
|
-
context: {},
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
};
|
|
213
|
-
mockExeca
|
|
214
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(cloudConfig) })
|
|
215
|
-
.mockResolvedValueOnce({ stdout: 'eks-cluster' })
|
|
216
|
-
.mockResolvedValueOnce({ stdout: '' })
|
|
217
|
-
.mockResolvedValueOnce({ stdout: 'a1234.elb.amazonaws.com' });
|
|
218
|
-
const result = await getClusterInfo();
|
|
219
|
-
expect(result.ip).toBe('a1234.elb.amazonaws.com');
|
|
220
|
-
});
|
|
221
|
-
it('falls back to external node IP for cloud cluster', async () => {
|
|
222
|
-
const cloudConfig = {
|
|
223
|
-
'current-context': 'gke-cluster',
|
|
224
|
-
contexts: [
|
|
225
|
-
{
|
|
226
|
-
name: 'gke-cluster',
|
|
227
|
-
context: {},
|
|
228
|
-
},
|
|
229
|
-
],
|
|
230
|
-
};
|
|
231
|
-
mockExeca
|
|
232
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(cloudConfig) })
|
|
233
|
-
.mockResolvedValueOnce({ stdout: 'gke-cluster' })
|
|
234
|
-
.mockRejectedValueOnce(new Error('service not found'))
|
|
235
|
-
.mockResolvedValueOnce({ stdout: '35.201.125.18' });
|
|
236
|
-
const result = await getClusterInfo();
|
|
237
|
-
expect(result.ip).toBe('35.201.125.18');
|
|
238
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
239
|
-
'get',
|
|
240
|
-
'nodes',
|
|
241
|
-
'-o',
|
|
242
|
-
'jsonpath={.items[0].status.addresses[?(@.type=="ExternalIP")].address}',
|
|
243
|
-
]);
|
|
244
|
-
});
|
|
245
|
-
it('gets k3s cluster info', async () => {
|
|
246
|
-
const k3sConfig = {
|
|
247
|
-
'current-context': 'k3s-default',
|
|
248
|
-
contexts: [
|
|
249
|
-
{
|
|
250
|
-
name: 'k3s-default',
|
|
251
|
-
context: {},
|
|
252
|
-
},
|
|
253
|
-
],
|
|
254
|
-
};
|
|
255
|
-
mockExeca
|
|
256
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(k3sConfig) })
|
|
257
|
-
.mockResolvedValueOnce({ stdout: 'k3s-default' })
|
|
258
|
-
.mockResolvedValueOnce({ stdout: '10.0.0.5' });
|
|
259
|
-
const result = await getClusterInfo();
|
|
260
|
-
expect(result).toEqual({
|
|
261
|
-
type: 'k3s',
|
|
262
|
-
context: 'k3s-default',
|
|
263
|
-
namespace: 'default',
|
|
264
|
-
ip: '10.0.0.5',
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
it('uses provided context parameter', async () => {
|
|
268
|
-
const multiConfig = {
|
|
269
|
-
'current-context': 'kind-staging',
|
|
270
|
-
contexts: [
|
|
271
|
-
{
|
|
272
|
-
name: 'kind-staging',
|
|
273
|
-
context: {
|
|
274
|
-
namespace: 'staging-ns',
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
],
|
|
278
|
-
};
|
|
279
|
-
mockExeca
|
|
280
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(multiConfig) })
|
|
281
|
-
.mockResolvedValueOnce({ stdout: 'kind-staging' })
|
|
282
|
-
.mockResolvedValueOnce({ stdout: '172.18.0.3' });
|
|
283
|
-
const result = await getClusterInfo('kind-staging');
|
|
284
|
-
expect(result.context).toBe('kind-staging');
|
|
285
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
286
|
-
'config',
|
|
287
|
-
'view',
|
|
288
|
-
'--minify',
|
|
289
|
-
'-o',
|
|
290
|
-
'json',
|
|
291
|
-
'--context',
|
|
292
|
-
'kind-staging',
|
|
293
|
-
]);
|
|
294
|
-
});
|
|
295
|
-
it('handles unknown cluster type', async () => {
|
|
296
|
-
const unknownConfig = {
|
|
297
|
-
'current-context': 'custom-cluster',
|
|
298
|
-
contexts: [
|
|
299
|
-
{
|
|
300
|
-
name: 'custom-cluster',
|
|
301
|
-
context: {},
|
|
302
|
-
},
|
|
303
|
-
],
|
|
304
|
-
};
|
|
305
|
-
mockExeca
|
|
306
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(unknownConfig) })
|
|
307
|
-
.mockResolvedValueOnce({ stdout: 'custom-cluster' })
|
|
308
|
-
.mockResolvedValueOnce({ stdout: '10.0.0.1' });
|
|
309
|
-
const result = await getClusterInfo();
|
|
310
|
-
expect(result).toEqual({
|
|
311
|
-
type: 'unknown',
|
|
312
|
-
context: 'custom-cluster',
|
|
313
|
-
namespace: 'default',
|
|
314
|
-
ip: '10.0.0.1',
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
it('handles kubectl config error', async () => {
|
|
318
|
-
mockExeca.mockRejectedValue(new Error('kubectl not configured'));
|
|
319
|
-
const result = await getClusterInfo();
|
|
320
|
-
expect(result).toEqual({
|
|
321
|
-
type: 'unknown',
|
|
322
|
-
error: 'kubectl not configured',
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
it('handles missing context in config', async () => {
|
|
326
|
-
const emptyConfig = {
|
|
327
|
-
contexts: [],
|
|
328
|
-
};
|
|
329
|
-
mockExeca
|
|
330
|
-
.mockResolvedValueOnce({ stdout: JSON.stringify(emptyConfig) })
|
|
331
|
-
.mockResolvedValueOnce({ stdout: '' })
|
|
332
|
-
.mockResolvedValueOnce({ stdout: '10.0.0.1' });
|
|
333
|
-
const result = await getClusterInfo();
|
|
334
|
-
expect(result.context).toBe('');
|
|
335
|
-
expect(result.namespace).toBe('default');
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
|
|
2
|
-
// Mock chalk to avoid ANSI codes in tests
|
|
3
|
-
jest.unstable_mockModule('chalk', () => ({
|
|
4
|
-
default: {
|
|
5
|
-
gray: (str) => str,
|
|
6
|
-
},
|
|
7
|
-
}));
|
|
8
|
-
// Mock execa using unstable_mockModule
|
|
9
|
-
jest.unstable_mockModule('execa', () => ({
|
|
10
|
-
execa: jest.fn(),
|
|
11
|
-
}));
|
|
12
|
-
// Dynamic imports after mock
|
|
13
|
-
const { execa } = await import('execa');
|
|
14
|
-
const { checkCommandExists, execute } = await import('./commands.js');
|
|
15
|
-
// Type the mock properly
|
|
16
|
-
const mockExeca = execa;
|
|
17
|
-
describe('commands', () => {
|
|
18
|
-
describe('checkCommandExists', () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
jest.clearAllMocks();
|
|
21
|
-
});
|
|
22
|
-
it('returns true when command executes successfully', async () => {
|
|
23
|
-
mockExeca.mockResolvedValue({
|
|
24
|
-
stdout: 'v1.0.0',
|
|
25
|
-
stderr: '',
|
|
26
|
-
exitCode: 0,
|
|
27
|
-
});
|
|
28
|
-
const result = await checkCommandExists('helm', ['version']);
|
|
29
|
-
expect(result).toBe(true);
|
|
30
|
-
expect(mockExeca).toHaveBeenCalledWith('helm', ['version']);
|
|
31
|
-
});
|
|
32
|
-
it('returns false when command fails', async () => {
|
|
33
|
-
mockExeca.mockRejectedValue(new Error('Command not found'));
|
|
34
|
-
const result = await checkCommandExists('nonexistent', ['--version']);
|
|
35
|
-
expect(result).toBe(false);
|
|
36
|
-
expect(mockExeca).toHaveBeenCalledWith('nonexistent', ['--version']);
|
|
37
|
-
});
|
|
38
|
-
it('uses default --version arg when no args provided', async () => {
|
|
39
|
-
mockExeca.mockResolvedValue({
|
|
40
|
-
stdout: '1.0.0',
|
|
41
|
-
stderr: '',
|
|
42
|
-
exitCode: 0,
|
|
43
|
-
});
|
|
44
|
-
const result = await checkCommandExists('node');
|
|
45
|
-
expect(result).toBe(true);
|
|
46
|
-
expect(mockExeca).toHaveBeenCalledWith('node', ['--version']);
|
|
47
|
-
});
|
|
48
|
-
it('uses custom args when provided', async () => {
|
|
49
|
-
mockExeca.mockResolvedValue({
|
|
50
|
-
stdout: 'Client Version: v1.28.0',
|
|
51
|
-
stderr: '',
|
|
52
|
-
exitCode: 0,
|
|
53
|
-
});
|
|
54
|
-
const result = await checkCommandExists('kubectl', [
|
|
55
|
-
'version',
|
|
56
|
-
'--client',
|
|
57
|
-
]);
|
|
58
|
-
expect(result).toBe(true);
|
|
59
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
60
|
-
'version',
|
|
61
|
-
'--client',
|
|
62
|
-
]);
|
|
63
|
-
});
|
|
64
|
-
it('handles empty args array', async () => {
|
|
65
|
-
mockExeca.mockResolvedValue({
|
|
66
|
-
stdout: '',
|
|
67
|
-
stderr: '',
|
|
68
|
-
exitCode: 0,
|
|
69
|
-
});
|
|
70
|
-
const result = await checkCommandExists('echo', []);
|
|
71
|
-
expect(result).toBe(true);
|
|
72
|
-
expect(mockExeca).toHaveBeenCalledWith('echo', []);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
describe('execute', () => {
|
|
76
|
-
let mockConsoleLog;
|
|
77
|
-
beforeEach(() => {
|
|
78
|
-
jest.clearAllMocks();
|
|
79
|
-
mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
80
|
-
});
|
|
81
|
-
afterEach(() => {
|
|
82
|
-
mockConsoleLog.mockRestore();
|
|
83
|
-
});
|
|
84
|
-
it('executes command without verbose output by default', async () => {
|
|
85
|
-
mockExeca.mockResolvedValue({
|
|
86
|
-
stdout: 'success',
|
|
87
|
-
stderr: '',
|
|
88
|
-
exitCode: 0,
|
|
89
|
-
});
|
|
90
|
-
await execute('helm', ['install', 'test'], { stdio: 'inherit' });
|
|
91
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
92
|
-
expect(mockExeca).toHaveBeenCalledWith('helm', ['install', 'test'], {
|
|
93
|
-
stdio: 'inherit',
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
it('prints command when verbose is true', async () => {
|
|
97
|
-
mockExeca.mockResolvedValue({
|
|
98
|
-
stdout: 'success',
|
|
99
|
-
stderr: '',
|
|
100
|
-
exitCode: 0,
|
|
101
|
-
});
|
|
102
|
-
await execute('helm', ['install', 'test'], { stdio: 'inherit' }, { verbose: true });
|
|
103
|
-
expect(mockConsoleLog).toHaveBeenCalledWith('$ helm install test');
|
|
104
|
-
expect(mockExeca).toHaveBeenCalledWith('helm', ['install', 'test'], {
|
|
105
|
-
stdio: 'inherit',
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
it('works with empty args array', async () => {
|
|
109
|
-
mockExeca.mockResolvedValue({
|
|
110
|
-
stdout: '',
|
|
111
|
-
stderr: '',
|
|
112
|
-
exitCode: 0,
|
|
113
|
-
});
|
|
114
|
-
await execute('ls', [], {}, { verbose: true });
|
|
115
|
-
expect(mockConsoleLog).toHaveBeenCalledWith('$ ls ');
|
|
116
|
-
expect(mockExeca).toHaveBeenCalledWith('ls', [], {});
|
|
117
|
-
});
|
|
118
|
-
it('passes through execa options correctly', async () => {
|
|
119
|
-
mockExeca.mockResolvedValue({
|
|
120
|
-
stdout: '',
|
|
121
|
-
stderr: '',
|
|
122
|
-
exitCode: 0,
|
|
123
|
-
});
|
|
124
|
-
const execaOpts = { stdio: 'pipe', timeout: 5000, cwd: '/tmp' };
|
|
125
|
-
await execute('kubectl', ['get', 'pods'], execaOpts);
|
|
126
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
127
|
-
expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'pods'], execaOpts);
|
|
128
|
-
});
|
|
129
|
-
it('handles command failure', async () => {
|
|
130
|
-
const error = new Error('Command failed');
|
|
131
|
-
mockExeca.mockRejectedValue(error);
|
|
132
|
-
await expect(execute('fail', ['now'])).rejects.toThrow('Command failed');
|
|
133
|
-
expect(mockExeca).toHaveBeenCalledWith('fail', ['now'], {});
|
|
134
|
-
});
|
|
135
|
-
it('defaults to no verbose when additionalOptions not provided', async () => {
|
|
136
|
-
mockExeca.mockResolvedValue({
|
|
137
|
-
stdout: 'ok',
|
|
138
|
-
stderr: '',
|
|
139
|
-
exitCode: 0,
|
|
140
|
-
});
|
|
141
|
-
await execute('echo', ['test']);
|
|
142
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
143
|
-
expect(mockExeca).toHaveBeenCalledWith('echo', ['test'], {});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|