@vibescope/mcp-server 0.4.4 → 0.4.6
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/CHANGELOG.md +84 -84
- package/README.md +194 -194
- package/dist/api-client/bodies-of-work.d.ts +125 -0
- package/dist/api-client/bodies-of-work.js +78 -0
- package/dist/api-client/chat.d.ts +26 -0
- package/dist/api-client/chat.js +20 -0
- package/dist/api-client/connectors.d.ts +104 -0
- package/dist/api-client/connectors.js +46 -0
- package/dist/api-client/deployment.d.ts +190 -0
- package/dist/api-client/deployment.js +113 -0
- package/dist/api-client/file-checkouts.d.ts +71 -0
- package/dist/api-client/file-checkouts.js +43 -0
- package/dist/api-client/git-issues.d.ts +55 -0
- package/dist/api-client/git-issues.js +34 -0
- package/dist/api-client/index.d.ts +619 -1
- package/dist/api-client/index.js +148 -0
- package/dist/api-client/organizations.d.ts +101 -0
- package/dist/api-client/organizations.js +86 -0
- package/dist/api-client/progress.d.ts +61 -0
- package/dist/api-client/progress.js +34 -0
- package/dist/api-client/project.d.ts +1 -0
- package/dist/api-client/requests.d.ts +28 -0
- package/dist/api-client/requests.js +28 -0
- package/dist/api-client/sprints.d.ts +153 -0
- package/dist/api-client/sprints.js +82 -0
- package/dist/api-client/subtasks.d.ts +37 -0
- package/dist/api-client/subtasks.js +23 -0
- package/dist/api-client.d.ts +23 -0
- package/dist/api-client.js +15 -0
- package/dist/cli-init.js +21 -21
- package/dist/cli.js +26 -26
- package/dist/handlers/blockers.js +4 -0
- package/dist/handlers/chat.d.ts +23 -0
- package/dist/handlers/chat.js +84 -0
- package/dist/handlers/deployment.d.ts +3 -0
- package/dist/handlers/deployment.js +23 -0
- package/dist/handlers/discovery.js +13 -0
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/project.js +4 -2
- package/dist/handlers/session.js +7 -0
- package/dist/handlers/tasks.js +7 -0
- package/dist/handlers/tool-docs.js +1204 -1131
- package/dist/index.js +73 -73
- package/dist/templates/agent-guidelines.d.ts +1 -1
- package/dist/templates/agent-guidelines.js +205 -187
- package/dist/templates/help-content.js +1621 -1621
- package/dist/tools/bodies-of-work.js +6 -6
- package/dist/tools/chat.d.ts +1 -0
- package/dist/tools/chat.js +24 -0
- package/dist/tools/cloud-agents.js +22 -22
- package/dist/tools/deployment.js +13 -0
- package/dist/tools/features.d.ts +13 -0
- package/dist/tools/features.js +151 -0
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/milestones.js +2 -2
- package/dist/tools/project.js +4 -0
- package/dist/tools/requests.js +1 -1
- package/dist/tools/session.js +11 -11
- package/dist/tools/sprints.js +9 -9
- package/dist/tools/tasks.js +35 -35
- package/dist/tools/worktrees.js +14 -14
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +3602 -0
- package/dist/utils.js +11 -11
- package/docs/TOOLS.md +2663 -2545
- package/package.json +53 -53
- package/scripts/generate-docs.ts +212 -212
- package/scripts/version-bump.ts +203 -203
- package/src/api-client/blockers.ts +86 -86
- package/src/api-client/bodies-of-work.ts +194 -0
- package/src/api-client/chat.ts +50 -0
- package/src/api-client/connectors.ts +152 -0
- package/src/api-client/cost.ts +185 -185
- package/src/api-client/decisions.ts +87 -87
- package/src/api-client/deployment.ts +313 -0
- package/src/api-client/discovery.ts +81 -81
- package/src/api-client/fallback.ts +52 -52
- package/src/api-client/file-checkouts.ts +115 -0
- package/src/api-client/findings.ts +100 -100
- package/src/api-client/git-issues.ts +88 -0
- package/src/api-client/ideas.ts +112 -112
- package/src/api-client/index.ts +592 -426
- package/src/api-client/milestones.ts +83 -83
- package/src/api-client/organizations.ts +185 -0
- package/src/api-client/progress.ts +94 -0
- package/src/api-client/project.ts +180 -179
- package/src/api-client/requests.ts +54 -0
- package/src/api-client/session.ts +220 -220
- package/src/api-client/sprints.ts +227 -0
- package/src/api-client/subtasks.ts +57 -0
- package/src/api-client/tasks.ts +450 -450
- package/src/api-client/types.ts +32 -32
- package/src/api-client/validation.ts +60 -60
- package/src/api-client/worktrees.ts +53 -53
- package/src/api-client.test.ts +847 -850
- package/src/api-client.ts +2707 -2672
- package/src/cli-init.ts +557 -557
- package/src/cli.test.ts +284 -284
- package/src/cli.ts +204 -204
- package/src/handlers/__test-setup__.ts +240 -236
- package/src/handlers/__test-utils__.ts +89 -89
- package/src/handlers/blockers.test.ts +468 -468
- package/src/handlers/blockers.ts +172 -163
- package/src/handlers/bodies-of-work.test.ts +704 -704
- package/src/handlers/bodies-of-work.ts +526 -526
- package/src/handlers/chat.test.ts +185 -0
- package/src/handlers/chat.ts +101 -0
- package/src/handlers/cloud-agents.test.ts +438 -438
- package/src/handlers/cloud-agents.ts +156 -156
- package/src/handlers/connectors.test.ts +834 -834
- package/src/handlers/connectors.ts +229 -229
- package/src/handlers/cost.test.ts +462 -462
- package/src/handlers/cost.ts +285 -285
- package/src/handlers/decisions.test.ts +382 -382
- package/src/handlers/decisions.ts +153 -153
- package/src/handlers/deployment.test.ts +551 -551
- package/src/handlers/deployment.ts +570 -541
- package/src/handlers/discovery.test.ts +206 -206
- package/src/handlers/discovery.ts +427 -414
- package/src/handlers/fallback.test.ts +537 -537
- package/src/handlers/fallback.ts +194 -194
- package/src/handlers/file-checkouts.test.ts +750 -750
- package/src/handlers/file-checkouts.ts +185 -185
- package/src/handlers/findings.test.ts +633 -633
- package/src/handlers/findings.ts +239 -239
- package/src/handlers/git-issues.test.ts +631 -631
- package/src/handlers/git-issues.ts +136 -136
- package/src/handlers/ideas.test.ts +644 -644
- package/src/handlers/ideas.ts +207 -207
- package/src/handlers/index.ts +93 -90
- package/src/handlers/milestones.test.ts +475 -475
- package/src/handlers/milestones.ts +180 -180
- package/src/handlers/organizations.test.ts +826 -826
- package/src/handlers/organizations.ts +315 -315
- package/src/handlers/progress.test.ts +269 -269
- package/src/handlers/progress.ts +77 -77
- package/src/handlers/project.test.ts +546 -546
- package/src/handlers/project.ts +242 -239
- package/src/handlers/requests.test.ts +303 -303
- package/src/handlers/requests.ts +99 -99
- package/src/handlers/roles.test.ts +305 -305
- package/src/handlers/roles.ts +219 -219
- package/src/handlers/session.test.ts +998 -998
- package/src/handlers/session.ts +1105 -1093
- package/src/handlers/sprints.test.ts +732 -732
- package/src/handlers/sprints.ts +537 -537
- package/src/handlers/tasks.test.ts +931 -931
- package/src/handlers/tasks.ts +1133 -1121
- package/src/handlers/tool-categories.test.ts +66 -66
- package/src/handlers/tool-docs.test.ts +511 -511
- package/src/handlers/tool-docs.ts +1571 -1491
- package/src/handlers/types.test.ts +259 -259
- package/src/handlers/types.ts +176 -176
- package/src/handlers/validation.test.ts +582 -582
- package/src/handlers/validation.ts +164 -164
- package/src/handlers/version.ts +63 -63
- package/src/index.test.ts +674 -674
- package/src/index.ts +807 -807
- package/src/setup.test.ts +233 -233
- package/src/setup.ts +404 -404
- package/src/templates/agent-guidelines.ts +233 -215
- package/src/templates/help-content.ts +1751 -1751
- package/src/token-tracking.test.ts +463 -463
- package/src/token-tracking.ts +167 -167
- package/src/tools/blockers.ts +122 -122
- package/src/tools/bodies-of-work.ts +283 -283
- package/src/tools/chat.ts +72 -46
- package/src/tools/cloud-agents.ts +101 -101
- package/src/tools/connectors.ts +191 -191
- package/src/tools/cost.ts +111 -111
- package/src/tools/decisions.ts +111 -111
- package/src/tools/deployment.ts +455 -442
- package/src/tools/discovery.ts +76 -76
- package/src/tools/fallback.ts +111 -111
- package/src/tools/features.ts +154 -0
- package/src/tools/file-checkouts.ts +145 -145
- package/src/tools/findings.ts +101 -101
- package/src/tools/git-issues.ts +130 -130
- package/src/tools/ideas.ts +162 -162
- package/src/tools/index.ts +141 -137
- package/src/tools/milestones.ts +118 -118
- package/src/tools/organizations.ts +224 -224
- package/src/tools/progress.ts +73 -73
- package/src/tools/project.ts +206 -202
- package/src/tools/requests.ts +68 -68
- package/src/tools/roles.ts +112 -112
- package/src/tools/session.ts +181 -181
- package/src/tools/sprints.ts +298 -298
- package/src/tools/tasks.ts +550 -550
- package/src/tools/tools.test.ts +222 -222
- package/src/tools/types.ts +9 -9
- package/src/tools/validation.ts +75 -75
- package/src/tools/version.ts +34 -34
- package/src/tools/worktrees.ts +66 -66
- package/src/tools.test.ts +416 -416
- package/src/utils.test.ts +1014 -1014
- package/src/utils.ts +586 -586
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +249 -249
- package/src/version.ts +109 -109
- package/tsconfig.json +16 -16
- package/vitest.config.ts +14 -14
|
@@ -1,438 +1,438 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
cleanupStaleCloudAgents,
|
|
4
|
-
listCloudAgents,
|
|
5
|
-
} from './cloud-agents.js';
|
|
6
|
-
import { ValidationError } from '../validators.js';
|
|
7
|
-
import { createMockContext, testUUID } from './__test-utils__.js';
|
|
8
|
-
import { mockApiClient } from './__test-setup__.js';
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// cleanupStaleCloudAgents Tests
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
|
-
describe('cleanupStaleCloudAgents', () => {
|
|
15
|
-
beforeEach(() => vi.clearAllMocks());
|
|
16
|
-
|
|
17
|
-
it('should throw error for missing project_id', async () => {
|
|
18
|
-
const ctx = createMockContext();
|
|
19
|
-
|
|
20
|
-
await expect(
|
|
21
|
-
cleanupStaleCloudAgents({}, ctx)
|
|
22
|
-
).rejects.toThrow(ValidationError);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should throw error for invalid project_id UUID', async () => {
|
|
26
|
-
const ctx = createMockContext();
|
|
27
|
-
|
|
28
|
-
await expect(
|
|
29
|
-
cleanupStaleCloudAgents({ project_id: 'invalid' }, ctx)
|
|
30
|
-
).rejects.toThrow(ValidationError);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should cleanup stale agents successfully', async () => {
|
|
34
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
35
|
-
ok: true,
|
|
36
|
-
data: {
|
|
37
|
-
cleaned: 2,
|
|
38
|
-
failed: 0,
|
|
39
|
-
agents: [
|
|
40
|
-
{ id: 'agent-1', name: 'Agent 1', success: true },
|
|
41
|
-
{ id: 'agent-2', name: 'Agent 2', success: true },
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
const ctx = createMockContext();
|
|
46
|
-
|
|
47
|
-
const result = await cleanupStaleCloudAgents(
|
|
48
|
-
{ project_id: testUUID() },
|
|
49
|
-
ctx
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
expect(result.result).toMatchObject({
|
|
53
|
-
cleaned: 2,
|
|
54
|
-
failed: 0,
|
|
55
|
-
message: 'Cleaned up 2 stale agents',
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should call API with correct parameters', async () => {
|
|
60
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
61
|
-
ok: true,
|
|
62
|
-
data: { cleaned: 0, agents: [] },
|
|
63
|
-
});
|
|
64
|
-
const ctx = createMockContext();
|
|
65
|
-
|
|
66
|
-
await cleanupStaleCloudAgents(
|
|
67
|
-
{
|
|
68
|
-
project_id: testUUID(),
|
|
69
|
-
stale_minutes: 10,
|
|
70
|
-
include_running: true,
|
|
71
|
-
dry_run: false,
|
|
72
|
-
},
|
|
73
|
-
ctx
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
77
|
-
'cleanup_stale_cloud_agents',
|
|
78
|
-
{
|
|
79
|
-
project_id: testUUID(),
|
|
80
|
-
staleMinutes: 10,
|
|
81
|
-
includeRunning: true,
|
|
82
|
-
dryRun: false,
|
|
83
|
-
}
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should use default values for optional parameters', async () => {
|
|
88
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
89
|
-
ok: true,
|
|
90
|
-
data: { cleaned: 0, agents: [] },
|
|
91
|
-
});
|
|
92
|
-
const ctx = createMockContext();
|
|
93
|
-
|
|
94
|
-
await cleanupStaleCloudAgents(
|
|
95
|
-
{ project_id: testUUID() },
|
|
96
|
-
ctx
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
100
|
-
'cleanup_stale_cloud_agents',
|
|
101
|
-
{
|
|
102
|
-
project_id: testUUID(),
|
|
103
|
-
staleMinutes: 5, // default
|
|
104
|
-
includeRunning: false, // default
|
|
105
|
-
dryRun: false, // default
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should return dry run preview when dry_run is true', async () => {
|
|
111
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
112
|
-
ok: true,
|
|
113
|
-
data: {
|
|
114
|
-
wouldClean: 3,
|
|
115
|
-
dryRun: true,
|
|
116
|
-
agents: [
|
|
117
|
-
{ id: 'agent-1', status: 'failed', createdAt: '2025-01-14T10:00:00Z' },
|
|
118
|
-
{ id: 'agent-2', status: 'starting', createdAt: '2025-01-14T10:05:00Z' },
|
|
119
|
-
{ id: 'agent-3', status: 'stopped', createdAt: '2025-01-14T10:10:00Z' },
|
|
120
|
-
],
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
const ctx = createMockContext();
|
|
124
|
-
|
|
125
|
-
const result = await cleanupStaleCloudAgents(
|
|
126
|
-
{ project_id: testUUID(), dry_run: true },
|
|
127
|
-
ctx
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
expect(result.result).toMatchObject({
|
|
131
|
-
dryRun: true,
|
|
132
|
-
wouldClean: 3,
|
|
133
|
-
message: 'Found 3 stale agents that would be cleaned up',
|
|
134
|
-
});
|
|
135
|
-
expect((result.result as { agents: unknown[] }).agents).toHaveLength(3);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should include failed count in response', async () => {
|
|
139
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
140
|
-
ok: true,
|
|
141
|
-
data: {
|
|
142
|
-
cleaned: 2,
|
|
143
|
-
failed: 1,
|
|
144
|
-
agents: [
|
|
145
|
-
{ id: 'agent-1', success: true },
|
|
146
|
-
{ id: 'agent-2', success: true },
|
|
147
|
-
{ id: 'agent-3', success: false, error: 'Agent still running' },
|
|
148
|
-
],
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
const ctx = createMockContext();
|
|
152
|
-
|
|
153
|
-
const result = await cleanupStaleCloudAgents(
|
|
154
|
-
{ project_id: testUUID() },
|
|
155
|
-
ctx
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
expect(result.result).toMatchObject({
|
|
159
|
-
cleaned: 2,
|
|
160
|
-
failed: 1,
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should return error when API call fails', async () => {
|
|
165
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
166
|
-
ok: false,
|
|
167
|
-
error: 'Failed to cleanup agents',
|
|
168
|
-
});
|
|
169
|
-
const ctx = createMockContext();
|
|
170
|
-
|
|
171
|
-
const result = await cleanupStaleCloudAgents(
|
|
172
|
-
{ project_id: testUUID() },
|
|
173
|
-
ctx
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
expect(result.isError).toBe(true);
|
|
177
|
-
expect(result.result).toMatchObject({
|
|
178
|
-
error: 'Failed to cleanup agents',
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should return error when API returns undefined data', async () => {
|
|
183
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
184
|
-
ok: true,
|
|
185
|
-
data: undefined,
|
|
186
|
-
});
|
|
187
|
-
const ctx = createMockContext();
|
|
188
|
-
|
|
189
|
-
const result = await cleanupStaleCloudAgents(
|
|
190
|
-
{ project_id: testUUID() },
|
|
191
|
-
ctx
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
expect(result.isError).toBe(true);
|
|
195
|
-
expect(result.result).toMatchObject({
|
|
196
|
-
error: 'Failed to cleanup stale agents',
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// ============================================================================
|
|
202
|
-
// listCloudAgents Tests
|
|
203
|
-
// ============================================================================
|
|
204
|
-
|
|
205
|
-
describe('listCloudAgents', () => {
|
|
206
|
-
beforeEach(() => vi.clearAllMocks());
|
|
207
|
-
|
|
208
|
-
it('should throw error for missing project_id', async () => {
|
|
209
|
-
const ctx = createMockContext();
|
|
210
|
-
|
|
211
|
-
await expect(
|
|
212
|
-
listCloudAgents({}, ctx)
|
|
213
|
-
).rejects.toThrow(ValidationError);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should throw error for invalid project_id UUID', async () => {
|
|
217
|
-
const ctx = createMockContext();
|
|
218
|
-
|
|
219
|
-
await expect(
|
|
220
|
-
listCloudAgents({ project_id: 'invalid' }, ctx)
|
|
221
|
-
).rejects.toThrow(ValidationError);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('should throw error for invalid status value', async () => {
|
|
225
|
-
const ctx = createMockContext();
|
|
226
|
-
|
|
227
|
-
await expect(
|
|
228
|
-
listCloudAgents({ project_id: testUUID(), status: 'invalid_status' }, ctx)
|
|
229
|
-
).rejects.toThrow(ValidationError);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('should return empty list when no agents', async () => {
|
|
233
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
234
|
-
ok: true,
|
|
235
|
-
data: { agents: [] },
|
|
236
|
-
});
|
|
237
|
-
const ctx = createMockContext();
|
|
238
|
-
|
|
239
|
-
const result = await listCloudAgents(
|
|
240
|
-
{ project_id: testUUID() },
|
|
241
|
-
ctx
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
expect(result.result).toMatchObject({
|
|
245
|
-
agents: [],
|
|
246
|
-
count: 0,
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('should return agents list with count', async () => {
|
|
251
|
-
const mockAgents = [
|
|
252
|
-
{
|
|
253
|
-
id: 'agent-1',
|
|
254
|
-
name: 'Test Agent 1',
|
|
255
|
-
status: 'running',
|
|
256
|
-
created_at: '2025-01-14T10:00:00Z',
|
|
257
|
-
last_heartbeat: '2025-01-14T12:00:00Z',
|
|
258
|
-
public_ip: '10.0.0.1',
|
|
259
|
-
ecs_task_id: 'task-abc123',
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
id: 'agent-2',
|
|
263
|
-
name: 'Test Agent 2',
|
|
264
|
-
status: 'stopped',
|
|
265
|
-
created_at: '2025-01-14T09:00:00Z',
|
|
266
|
-
},
|
|
267
|
-
];
|
|
268
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
269
|
-
ok: true,
|
|
270
|
-
data: { agents: mockAgents },
|
|
271
|
-
});
|
|
272
|
-
const ctx = createMockContext();
|
|
273
|
-
|
|
274
|
-
const result = await listCloudAgents(
|
|
275
|
-
{ project_id: testUUID() },
|
|
276
|
-
ctx
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
expect(result.result).toMatchObject({
|
|
280
|
-
agents: mockAgents,
|
|
281
|
-
count: 2,
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('should call API with status filter', async () => {
|
|
286
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
287
|
-
ok: true,
|
|
288
|
-
data: { agents: [] },
|
|
289
|
-
});
|
|
290
|
-
const ctx = createMockContext();
|
|
291
|
-
|
|
292
|
-
await listCloudAgents(
|
|
293
|
-
{ project_id: testUUID(), status: 'running' },
|
|
294
|
-
ctx
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
298
|
-
'list_cloud_agents',
|
|
299
|
-
{
|
|
300
|
-
project_id: testUUID(),
|
|
301
|
-
status: 'running',
|
|
302
|
-
}
|
|
303
|
-
);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it('should not pass status when "all" is specified', async () => {
|
|
307
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
308
|
-
ok: true,
|
|
309
|
-
data: { agents: [] },
|
|
310
|
-
});
|
|
311
|
-
const ctx = createMockContext();
|
|
312
|
-
|
|
313
|
-
await listCloudAgents(
|
|
314
|
-
{ project_id: testUUID(), status: 'all' },
|
|
315
|
-
ctx
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
319
|
-
'list_cloud_agents',
|
|
320
|
-
{
|
|
321
|
-
project_id: testUUID(),
|
|
322
|
-
status: undefined,
|
|
323
|
-
}
|
|
324
|
-
);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it('should use default status of "all"', async () => {
|
|
328
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
329
|
-
ok: true,
|
|
330
|
-
data: { agents: [] },
|
|
331
|
-
});
|
|
332
|
-
const ctx = createMockContext();
|
|
333
|
-
|
|
334
|
-
await listCloudAgents(
|
|
335
|
-
{ project_id: testUUID() },
|
|
336
|
-
ctx
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
340
|
-
'list_cloud_agents',
|
|
341
|
-
{
|
|
342
|
-
project_id: testUUID(),
|
|
343
|
-
status: undefined, // 'all' becomes undefined
|
|
344
|
-
}
|
|
345
|
-
);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it('should filter by starting status', async () => {
|
|
349
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
350
|
-
ok: true,
|
|
351
|
-
data: { agents: [{ id: 'a1', status: 'starting', created_at: '2025-01-14T10:00:00Z' }] },
|
|
352
|
-
});
|
|
353
|
-
const ctx = createMockContext();
|
|
354
|
-
|
|
355
|
-
const result = await listCloudAgents(
|
|
356
|
-
{ project_id: testUUID(), status: 'starting' },
|
|
357
|
-
ctx
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
361
|
-
'list_cloud_agents',
|
|
362
|
-
{ project_id: testUUID(), status: 'starting' }
|
|
363
|
-
);
|
|
364
|
-
expect((result.result as { count: number }).count).toBe(1);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it('should filter by failed status', async () => {
|
|
368
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
369
|
-
ok: true,
|
|
370
|
-
data: { agents: [] },
|
|
371
|
-
});
|
|
372
|
-
const ctx = createMockContext();
|
|
373
|
-
|
|
374
|
-
await listCloudAgents(
|
|
375
|
-
{ project_id: testUUID(), status: 'failed' },
|
|
376
|
-
ctx
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
380
|
-
'list_cloud_agents',
|
|
381
|
-
{ project_id: testUUID(), status: 'failed' }
|
|
382
|
-
);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('should filter by stopped status', async () => {
|
|
386
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
387
|
-
ok: true,
|
|
388
|
-
data: { agents: [] },
|
|
389
|
-
});
|
|
390
|
-
const ctx = createMockContext();
|
|
391
|
-
|
|
392
|
-
await listCloudAgents(
|
|
393
|
-
{ project_id: testUUID(), status: 'stopped' },
|
|
394
|
-
ctx
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
398
|
-
'list_cloud_agents',
|
|
399
|
-
{ project_id: testUUID(), status: 'stopped' }
|
|
400
|
-
);
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it('should return error when API call fails', async () => {
|
|
404
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
405
|
-
ok: false,
|
|
406
|
-
error: 'Database error',
|
|
407
|
-
});
|
|
408
|
-
const ctx = createMockContext();
|
|
409
|
-
|
|
410
|
-
const result = await listCloudAgents(
|
|
411
|
-
{ project_id: testUUID() },
|
|
412
|
-
ctx
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
expect(result.isError).toBe(true);
|
|
416
|
-
expect(result.result).toMatchObject({
|
|
417
|
-
error: 'Database error',
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
it('should return error when API returns undefined data', async () => {
|
|
422
|
-
mockApiClient.proxy.mockResolvedValue({
|
|
423
|
-
ok: true,
|
|
424
|
-
data: undefined,
|
|
425
|
-
});
|
|
426
|
-
const ctx = createMockContext();
|
|
427
|
-
|
|
428
|
-
const result = await listCloudAgents(
|
|
429
|
-
{ project_id: testUUID() },
|
|
430
|
-
ctx
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
expect(result.isError).toBe(true);
|
|
434
|
-
expect(result.result).toMatchObject({
|
|
435
|
-
error: 'Failed to list cloud agents',
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
cleanupStaleCloudAgents,
|
|
4
|
+
listCloudAgents,
|
|
5
|
+
} from './cloud-agents.js';
|
|
6
|
+
import { ValidationError } from '../validators.js';
|
|
7
|
+
import { createMockContext, testUUID } from './__test-utils__.js';
|
|
8
|
+
import { mockApiClient } from './__test-setup__.js';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// cleanupStaleCloudAgents Tests
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
describe('cleanupStaleCloudAgents', () => {
|
|
15
|
+
beforeEach(() => vi.clearAllMocks());
|
|
16
|
+
|
|
17
|
+
it('should throw error for missing project_id', async () => {
|
|
18
|
+
const ctx = createMockContext();
|
|
19
|
+
|
|
20
|
+
await expect(
|
|
21
|
+
cleanupStaleCloudAgents({}, ctx)
|
|
22
|
+
).rejects.toThrow(ValidationError);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should throw error for invalid project_id UUID', async () => {
|
|
26
|
+
const ctx = createMockContext();
|
|
27
|
+
|
|
28
|
+
await expect(
|
|
29
|
+
cleanupStaleCloudAgents({ project_id: 'invalid' }, ctx)
|
|
30
|
+
).rejects.toThrow(ValidationError);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should cleanup stale agents successfully', async () => {
|
|
34
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
35
|
+
ok: true,
|
|
36
|
+
data: {
|
|
37
|
+
cleaned: 2,
|
|
38
|
+
failed: 0,
|
|
39
|
+
agents: [
|
|
40
|
+
{ id: 'agent-1', name: 'Agent 1', success: true },
|
|
41
|
+
{ id: 'agent-2', name: 'Agent 2', success: true },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const ctx = createMockContext();
|
|
46
|
+
|
|
47
|
+
const result = await cleanupStaleCloudAgents(
|
|
48
|
+
{ project_id: testUUID() },
|
|
49
|
+
ctx
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(result.result).toMatchObject({
|
|
53
|
+
cleaned: 2,
|
|
54
|
+
failed: 0,
|
|
55
|
+
message: 'Cleaned up 2 stale agents',
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should call API with correct parameters', async () => {
|
|
60
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
61
|
+
ok: true,
|
|
62
|
+
data: { cleaned: 0, agents: [] },
|
|
63
|
+
});
|
|
64
|
+
const ctx = createMockContext();
|
|
65
|
+
|
|
66
|
+
await cleanupStaleCloudAgents(
|
|
67
|
+
{
|
|
68
|
+
project_id: testUUID(),
|
|
69
|
+
stale_minutes: 10,
|
|
70
|
+
include_running: true,
|
|
71
|
+
dry_run: false,
|
|
72
|
+
},
|
|
73
|
+
ctx
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
77
|
+
'cleanup_stale_cloud_agents',
|
|
78
|
+
{
|
|
79
|
+
project_id: testUUID(),
|
|
80
|
+
staleMinutes: 10,
|
|
81
|
+
includeRunning: true,
|
|
82
|
+
dryRun: false,
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should use default values for optional parameters', async () => {
|
|
88
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
89
|
+
ok: true,
|
|
90
|
+
data: { cleaned: 0, agents: [] },
|
|
91
|
+
});
|
|
92
|
+
const ctx = createMockContext();
|
|
93
|
+
|
|
94
|
+
await cleanupStaleCloudAgents(
|
|
95
|
+
{ project_id: testUUID() },
|
|
96
|
+
ctx
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
100
|
+
'cleanup_stale_cloud_agents',
|
|
101
|
+
{
|
|
102
|
+
project_id: testUUID(),
|
|
103
|
+
staleMinutes: 5, // default
|
|
104
|
+
includeRunning: false, // default
|
|
105
|
+
dryRun: false, // default
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should return dry run preview when dry_run is true', async () => {
|
|
111
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
112
|
+
ok: true,
|
|
113
|
+
data: {
|
|
114
|
+
wouldClean: 3,
|
|
115
|
+
dryRun: true,
|
|
116
|
+
agents: [
|
|
117
|
+
{ id: 'agent-1', status: 'failed', createdAt: '2025-01-14T10:00:00Z' },
|
|
118
|
+
{ id: 'agent-2', status: 'starting', createdAt: '2025-01-14T10:05:00Z' },
|
|
119
|
+
{ id: 'agent-3', status: 'stopped', createdAt: '2025-01-14T10:10:00Z' },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const ctx = createMockContext();
|
|
124
|
+
|
|
125
|
+
const result = await cleanupStaleCloudAgents(
|
|
126
|
+
{ project_id: testUUID(), dry_run: true },
|
|
127
|
+
ctx
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(result.result).toMatchObject({
|
|
131
|
+
dryRun: true,
|
|
132
|
+
wouldClean: 3,
|
|
133
|
+
message: 'Found 3 stale agents that would be cleaned up',
|
|
134
|
+
});
|
|
135
|
+
expect((result.result as { agents: unknown[] }).agents).toHaveLength(3);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should include failed count in response', async () => {
|
|
139
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
140
|
+
ok: true,
|
|
141
|
+
data: {
|
|
142
|
+
cleaned: 2,
|
|
143
|
+
failed: 1,
|
|
144
|
+
agents: [
|
|
145
|
+
{ id: 'agent-1', success: true },
|
|
146
|
+
{ id: 'agent-2', success: true },
|
|
147
|
+
{ id: 'agent-3', success: false, error: 'Agent still running' },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
const ctx = createMockContext();
|
|
152
|
+
|
|
153
|
+
const result = await cleanupStaleCloudAgents(
|
|
154
|
+
{ project_id: testUUID() },
|
|
155
|
+
ctx
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(result.result).toMatchObject({
|
|
159
|
+
cleaned: 2,
|
|
160
|
+
failed: 1,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should return error when API call fails', async () => {
|
|
165
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
166
|
+
ok: false,
|
|
167
|
+
error: 'Failed to cleanup agents',
|
|
168
|
+
});
|
|
169
|
+
const ctx = createMockContext();
|
|
170
|
+
|
|
171
|
+
const result = await cleanupStaleCloudAgents(
|
|
172
|
+
{ project_id: testUUID() },
|
|
173
|
+
ctx
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(result.isError).toBe(true);
|
|
177
|
+
expect(result.result).toMatchObject({
|
|
178
|
+
error: 'Failed to cleanup agents',
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should return error when API returns undefined data', async () => {
|
|
183
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
184
|
+
ok: true,
|
|
185
|
+
data: undefined,
|
|
186
|
+
});
|
|
187
|
+
const ctx = createMockContext();
|
|
188
|
+
|
|
189
|
+
const result = await cleanupStaleCloudAgents(
|
|
190
|
+
{ project_id: testUUID() },
|
|
191
|
+
ctx
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(result.isError).toBe(true);
|
|
195
|
+
expect(result.result).toMatchObject({
|
|
196
|
+
error: 'Failed to cleanup stale agents',
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// listCloudAgents Tests
|
|
203
|
+
// ============================================================================
|
|
204
|
+
|
|
205
|
+
describe('listCloudAgents', () => {
|
|
206
|
+
beforeEach(() => vi.clearAllMocks());
|
|
207
|
+
|
|
208
|
+
it('should throw error for missing project_id', async () => {
|
|
209
|
+
const ctx = createMockContext();
|
|
210
|
+
|
|
211
|
+
await expect(
|
|
212
|
+
listCloudAgents({}, ctx)
|
|
213
|
+
).rejects.toThrow(ValidationError);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should throw error for invalid project_id UUID', async () => {
|
|
217
|
+
const ctx = createMockContext();
|
|
218
|
+
|
|
219
|
+
await expect(
|
|
220
|
+
listCloudAgents({ project_id: 'invalid' }, ctx)
|
|
221
|
+
).rejects.toThrow(ValidationError);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should throw error for invalid status value', async () => {
|
|
225
|
+
const ctx = createMockContext();
|
|
226
|
+
|
|
227
|
+
await expect(
|
|
228
|
+
listCloudAgents({ project_id: testUUID(), status: 'invalid_status' }, ctx)
|
|
229
|
+
).rejects.toThrow(ValidationError);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should return empty list when no agents', async () => {
|
|
233
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
234
|
+
ok: true,
|
|
235
|
+
data: { agents: [] },
|
|
236
|
+
});
|
|
237
|
+
const ctx = createMockContext();
|
|
238
|
+
|
|
239
|
+
const result = await listCloudAgents(
|
|
240
|
+
{ project_id: testUUID() },
|
|
241
|
+
ctx
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
expect(result.result).toMatchObject({
|
|
245
|
+
agents: [],
|
|
246
|
+
count: 0,
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should return agents list with count', async () => {
|
|
251
|
+
const mockAgents = [
|
|
252
|
+
{
|
|
253
|
+
id: 'agent-1',
|
|
254
|
+
name: 'Test Agent 1',
|
|
255
|
+
status: 'running',
|
|
256
|
+
created_at: '2025-01-14T10:00:00Z',
|
|
257
|
+
last_heartbeat: '2025-01-14T12:00:00Z',
|
|
258
|
+
public_ip: '10.0.0.1',
|
|
259
|
+
ecs_task_id: 'task-abc123',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'agent-2',
|
|
263
|
+
name: 'Test Agent 2',
|
|
264
|
+
status: 'stopped',
|
|
265
|
+
created_at: '2025-01-14T09:00:00Z',
|
|
266
|
+
},
|
|
267
|
+
];
|
|
268
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
269
|
+
ok: true,
|
|
270
|
+
data: { agents: mockAgents },
|
|
271
|
+
});
|
|
272
|
+
const ctx = createMockContext();
|
|
273
|
+
|
|
274
|
+
const result = await listCloudAgents(
|
|
275
|
+
{ project_id: testUUID() },
|
|
276
|
+
ctx
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(result.result).toMatchObject({
|
|
280
|
+
agents: mockAgents,
|
|
281
|
+
count: 2,
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should call API with status filter', async () => {
|
|
286
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
287
|
+
ok: true,
|
|
288
|
+
data: { agents: [] },
|
|
289
|
+
});
|
|
290
|
+
const ctx = createMockContext();
|
|
291
|
+
|
|
292
|
+
await listCloudAgents(
|
|
293
|
+
{ project_id: testUUID(), status: 'running' },
|
|
294
|
+
ctx
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
298
|
+
'list_cloud_agents',
|
|
299
|
+
{
|
|
300
|
+
project_id: testUUID(),
|
|
301
|
+
status: 'running',
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should not pass status when "all" is specified', async () => {
|
|
307
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
308
|
+
ok: true,
|
|
309
|
+
data: { agents: [] },
|
|
310
|
+
});
|
|
311
|
+
const ctx = createMockContext();
|
|
312
|
+
|
|
313
|
+
await listCloudAgents(
|
|
314
|
+
{ project_id: testUUID(), status: 'all' },
|
|
315
|
+
ctx
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
319
|
+
'list_cloud_agents',
|
|
320
|
+
{
|
|
321
|
+
project_id: testUUID(),
|
|
322
|
+
status: undefined,
|
|
323
|
+
}
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should use default status of "all"', async () => {
|
|
328
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
329
|
+
ok: true,
|
|
330
|
+
data: { agents: [] },
|
|
331
|
+
});
|
|
332
|
+
const ctx = createMockContext();
|
|
333
|
+
|
|
334
|
+
await listCloudAgents(
|
|
335
|
+
{ project_id: testUUID() },
|
|
336
|
+
ctx
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
340
|
+
'list_cloud_agents',
|
|
341
|
+
{
|
|
342
|
+
project_id: testUUID(),
|
|
343
|
+
status: undefined, // 'all' becomes undefined
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should filter by starting status', async () => {
|
|
349
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
350
|
+
ok: true,
|
|
351
|
+
data: { agents: [{ id: 'a1', status: 'starting', created_at: '2025-01-14T10:00:00Z' }] },
|
|
352
|
+
});
|
|
353
|
+
const ctx = createMockContext();
|
|
354
|
+
|
|
355
|
+
const result = await listCloudAgents(
|
|
356
|
+
{ project_id: testUUID(), status: 'starting' },
|
|
357
|
+
ctx
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
361
|
+
'list_cloud_agents',
|
|
362
|
+
{ project_id: testUUID(), status: 'starting' }
|
|
363
|
+
);
|
|
364
|
+
expect((result.result as { count: number }).count).toBe(1);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should filter by failed status', async () => {
|
|
368
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
369
|
+
ok: true,
|
|
370
|
+
data: { agents: [] },
|
|
371
|
+
});
|
|
372
|
+
const ctx = createMockContext();
|
|
373
|
+
|
|
374
|
+
await listCloudAgents(
|
|
375
|
+
{ project_id: testUUID(), status: 'failed' },
|
|
376
|
+
ctx
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
380
|
+
'list_cloud_agents',
|
|
381
|
+
{ project_id: testUUID(), status: 'failed' }
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should filter by stopped status', async () => {
|
|
386
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
387
|
+
ok: true,
|
|
388
|
+
data: { agents: [] },
|
|
389
|
+
});
|
|
390
|
+
const ctx = createMockContext();
|
|
391
|
+
|
|
392
|
+
await listCloudAgents(
|
|
393
|
+
{ project_id: testUUID(), status: 'stopped' },
|
|
394
|
+
ctx
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
398
|
+
'list_cloud_agents',
|
|
399
|
+
{ project_id: testUUID(), status: 'stopped' }
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should return error when API call fails', async () => {
|
|
404
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
405
|
+
ok: false,
|
|
406
|
+
error: 'Database error',
|
|
407
|
+
});
|
|
408
|
+
const ctx = createMockContext();
|
|
409
|
+
|
|
410
|
+
const result = await listCloudAgents(
|
|
411
|
+
{ project_id: testUUID() },
|
|
412
|
+
ctx
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
expect(result.isError).toBe(true);
|
|
416
|
+
expect(result.result).toMatchObject({
|
|
417
|
+
error: 'Database error',
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should return error when API returns undefined data', async () => {
|
|
422
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
423
|
+
ok: true,
|
|
424
|
+
data: undefined,
|
|
425
|
+
});
|
|
426
|
+
const ctx = createMockContext();
|
|
427
|
+
|
|
428
|
+
const result = await listCloudAgents(
|
|
429
|
+
{ project_id: testUUID() },
|
|
430
|
+
ctx
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(result.isError).toBe(true);
|
|
434
|
+
expect(result.result).toMatchObject({
|
|
435
|
+
error: 'Failed to list cloud agents',
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
});
|