xuanwu-cli 2.2.0 → 2.3.3
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/.env.test.example +14 -0
- package/__tests__/E2E_TEST_REPORT.md +206 -0
- package/__tests__/README.md +322 -0
- package/__tests__/TEST_SUMMARY.md +215 -0
- package/__tests__/global-setup.ts +13 -0
- package/__tests__/global-teardown.ts +3 -0
- package/__tests__/helpers/test-utils.ts +70 -0
- package/__tests__/integration/app.integration.test.ts +363 -0
- package/__tests__/integration/auth.integration.test.ts +243 -0
- package/__tests__/integration/build.integration.test.ts +215 -0
- package/__tests__/integration/e2e.test.ts +267 -0
- package/__tests__/integration/service.integration.test.ts +267 -0
- package/__tests__/integration/webhook.integration.test.ts +246 -0
- package/__tests__/run-e2e.js +360 -0
- package/__tests__/setup.ts +9 -0
- package/bin/xuanwu +0 -0
- package/dist/api/client.d.ts +29 -4
- package/dist/api/client.js +113 -29
- package/dist/commands/app.js +44 -0
- package/dist/commands/auth/login.js +5 -4
- package/dist/commands/deploy.js +77 -49
- package/dist/commands/env.js +31 -48
- package/dist/commands/project.d.ts +5 -0
- package/dist/commands/project.js +134 -0
- package/dist/commands/svc.js +36 -0
- package/dist/config/types.d.ts +1 -0
- package/dist/index.js +2 -0
- package/jest.config.js +18 -0
- package/package.json +10 -2
- package/src/api/client.ts +142 -33
- package/src/commands/app.ts +53 -0
- package/src/commands/auth/login.ts +6 -4
- package/src/commands/deploy.ts +93 -48
- package/src/commands/env.ts +35 -52
- package/src/commands/project.ts +153 -0
- package/src/commands/svc.ts +40 -0
- package/src/config/types.ts +1 -0
- package/src/index.ts +2 -0
- package/test/cli-integration.sh +245 -0
- package/test/integration.js +3 -3
- package/test/integration.sh +252 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 端到端测试
|
|
3
|
+
* 测试完整的 CLI 命令流程
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CLIExecutor, generateRandomCode, sleep, parseTableOutput, extractValue } from '../helpers/test-utils'
|
|
7
|
+
|
|
8
|
+
const API_URL = process.env.TEST_API_URL || 'https://i.xuanwu.dev.aimstek.cn'
|
|
9
|
+
const TEST_EMAIL = process.env.TEST_USER_EMAIL || ''
|
|
10
|
+
const TEST_PASSWORD = process.env.TEST_USER_PASSWORD || ''
|
|
11
|
+
|
|
12
|
+
describe('End-to-End CLI Tests', () => {
|
|
13
|
+
let cli: CLIExecutor
|
|
14
|
+
let testAppCode: string
|
|
15
|
+
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
cli = new CLIExecutor()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('Login Command', () => {
|
|
21
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should login with email and password', async () => {
|
|
22
|
+
const result = await cli.execute([
|
|
23
|
+
'login',
|
|
24
|
+
'--email', TEST_EMAIL,
|
|
25
|
+
'--password', TEST_PASSWORD,
|
|
26
|
+
'--api-url', API_URL,
|
|
27
|
+
'--expires-in', '30d'
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
expect(result.exitCode).toBe(0)
|
|
31
|
+
expect(result.stdout).toContain('登录成功')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('should show error for invalid credentials', async () => {
|
|
35
|
+
const result = await cli.execute([
|
|
36
|
+
'login',
|
|
37
|
+
'--email', 'invalid@example.com',
|
|
38
|
+
'--password', 'wrongpassword',
|
|
39
|
+
'--api-url', API_URL
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
expect(result.exitCode).not.toBe(0)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('Application Commands', () => {
|
|
47
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should create and list application', async () => {
|
|
48
|
+
testAppCode = generateRandomCode('e2e-app')
|
|
49
|
+
|
|
50
|
+
const createResult = await cli.execute([
|
|
51
|
+
'app', 'create',
|
|
52
|
+
'--name', 'E2E测试应用',
|
|
53
|
+
'--code', testAppCode,
|
|
54
|
+
'--git', 'https://github.com/example/test.git',
|
|
55
|
+
'--api-url', API_URL
|
|
56
|
+
], {
|
|
57
|
+
env: {
|
|
58
|
+
TEST_EMAIL,
|
|
59
|
+
TEST_PASSWORD
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(createResult.exitCode).toBe(0)
|
|
64
|
+
expect(createResult.stdout).toContain('created')
|
|
65
|
+
|
|
66
|
+
await sleep(1000)
|
|
67
|
+
|
|
68
|
+
const listResult = await cli.execute(['app', 'ls'], {
|
|
69
|
+
env: {
|
|
70
|
+
TEST_EMAIL,
|
|
71
|
+
TEST_PASSWORD
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
expect(listResult.exitCode).toBe(0)
|
|
76
|
+
expect(listResult.stdout).toContain(testAppCode)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should get application details', async () => {
|
|
80
|
+
if (!testAppCode) {
|
|
81
|
+
console.warn('No test app created, skipping')
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const result = await cli.execute(['app', 'get', testAppCode], {
|
|
86
|
+
env: {
|
|
87
|
+
TEST_EMAIL,
|
|
88
|
+
TEST_PASSWORD
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
expect(result.exitCode).toBe(0)
|
|
93
|
+
expect(result.stdout).toContain(testAppCode)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should update application', async () => {
|
|
97
|
+
if (!testAppCode) {
|
|
98
|
+
console.warn('No test app created, skipping')
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const result = await cli.execute([
|
|
103
|
+
'app', 'update', testAppCode,
|
|
104
|
+
'--name', '更新后的应用名'
|
|
105
|
+
], {
|
|
106
|
+
env: {
|
|
107
|
+
TEST_EMAIL,
|
|
108
|
+
TEST_PASSWORD
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
expect(result.exitCode).toBe(0)
|
|
113
|
+
expect(result.stdout).toContain('updated')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should trigger build', async () => {
|
|
117
|
+
if (!testAppCode) {
|
|
118
|
+
console.warn('No test app created, skipping')
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const result = await cli.execute([
|
|
123
|
+
'app', 'build', testAppCode
|
|
124
|
+
], {
|
|
125
|
+
env: {
|
|
126
|
+
TEST_EMAIL,
|
|
127
|
+
TEST_PASSWORD
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
expect(result.exitCode).toBe(0)
|
|
132
|
+
expect(result.stdout).toContain('started')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list builds for application', async () => {
|
|
136
|
+
if (!testAppCode) {
|
|
137
|
+
console.warn('No test app created, skipping')
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await sleep(2000)
|
|
142
|
+
|
|
143
|
+
const result = await cli.execute([
|
|
144
|
+
'app', 'builds', testAppCode
|
|
145
|
+
], {
|
|
146
|
+
env: {
|
|
147
|
+
TEST_EMAIL,
|
|
148
|
+
TEST_PASSWORD
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
expect(result.exitCode).toBe(0)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should delete application', async () => {
|
|
156
|
+
if (!testAppCode) {
|
|
157
|
+
console.warn('No test app created, skipping')
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result = await cli.execute([
|
|
162
|
+
'app', 'delete', testAppCode
|
|
163
|
+
], {
|
|
164
|
+
env: {
|
|
165
|
+
TEST_EMAIL,
|
|
166
|
+
TEST_PASSWORD
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
expect(result.exitCode).toBe(0)
|
|
171
|
+
expect(result.stdout).toContain('deleted')
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe('Service Commands', () => {
|
|
176
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list services', async () => {
|
|
177
|
+
const result = await cli.execute(['svc', 'ls'], {
|
|
178
|
+
env: {
|
|
179
|
+
TEST_EMAIL,
|
|
180
|
+
TEST_PASSWORD
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
expect(result.exitCode).toBe(0)
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
describe('Build Commands', () => {
|
|
189
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list all builds', async () => {
|
|
190
|
+
const result = await cli.execute(['build', 'ls'], {
|
|
191
|
+
env: {
|
|
192
|
+
TEST_EMAIL,
|
|
193
|
+
TEST_PASSWORD
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
expect(result.exitCode).toBe(0)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
describe('Whoami Command', () => {
|
|
202
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should show current user info', async () => {
|
|
203
|
+
const result = await cli.execute(['whoami'], {
|
|
204
|
+
env: {
|
|
205
|
+
TEST_EMAIL,
|
|
206
|
+
TEST_PASSWORD
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
expect(result.exitCode).toBe(0)
|
|
211
|
+
expect(result.stdout).toContain(TEST_EMAIL)
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
describe('Tokens Command', () => {
|
|
216
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list tokens', async () => {
|
|
217
|
+
const result = await cli.execute(['tokens'], {
|
|
218
|
+
env: {
|
|
219
|
+
TEST_EMAIL,
|
|
220
|
+
TEST_PASSWORD
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
expect(result.exitCode).toBe(0)
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('Logout Command', () => {
|
|
229
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should logout successfully', async () => {
|
|
230
|
+
const result = await cli.execute(['logout'], {
|
|
231
|
+
env: {
|
|
232
|
+
TEST_EMAIL,
|
|
233
|
+
TEST_PASSWORD
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
expect(result.exitCode).toBe(0)
|
|
238
|
+
expect(result.stdout).toContain('Logged out')
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('Error Handling', () => {
|
|
243
|
+
test('should show error for non-existent application', async () => {
|
|
244
|
+
const result = await cli.execute(['app', 'get', 'non-existent-app'], {
|
|
245
|
+
env: {
|
|
246
|
+
TEST_EMAIL,
|
|
247
|
+
TEST_PASSWORD
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
expect(result.exitCode).not.toBe(0)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
test('should show error for invalid command', async () => {
|
|
255
|
+
const result = await cli.execute(['invalid-command'])
|
|
256
|
+
|
|
257
|
+
expect(result.exitCode).not.toBe(0)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
test('should show help message', async () => {
|
|
261
|
+
const result = await cli.execute(['--help'])
|
|
262
|
+
|
|
263
|
+
expect(result.exitCode).toBe(0)
|
|
264
|
+
expect(result.stdout).toContain('玄武工厂')
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
})
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service 命令集成测试
|
|
3
|
+
* 测试 Service (K8s) 相关的 CLI API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { APIClient } from '../../src/api/client'
|
|
7
|
+
import { Connection } from '../../src/config/types'
|
|
8
|
+
|
|
9
|
+
const API_URL = process.env.TEST_API_URL || 'https://i.xuanwu.dev.aimstek.cn'
|
|
10
|
+
const TEST_EMAIL = process.env.TEST_USER_EMAIL || ''
|
|
11
|
+
const TEST_PASSWORD = process.env.TEST_USER_PASSWORD || ''
|
|
12
|
+
const TEST_NAMESPACE = process.env.TEST_NAMESPACE || 'default'
|
|
13
|
+
|
|
14
|
+
describe('Service Integration Tests', () => {
|
|
15
|
+
let apiClient: APIClient
|
|
16
|
+
let testToken: string
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
if (!TEST_EMAIL || !TEST_PASSWORD) {
|
|
20
|
+
console.warn('Skipping tests: TEST_USER_EMAIL and TEST_USER_PASSWORD not set')
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const response = await fetch(`${API_URL}/api/cli/auth/login`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
email: TEST_EMAIL,
|
|
29
|
+
password: TEST_PASSWORD,
|
|
30
|
+
deviceName: 'Test CLI',
|
|
31
|
+
expiresIn: '30d'
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error('Failed to login')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const data = await response.json() as { token: string }
|
|
40
|
+
testToken = data.token
|
|
41
|
+
|
|
42
|
+
const conn: Connection = {
|
|
43
|
+
name: 'test',
|
|
44
|
+
endpoint: API_URL,
|
|
45
|
+
token: testToken,
|
|
46
|
+
isDefault: true
|
|
47
|
+
}
|
|
48
|
+
apiClient = new APIClient(conn)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('List Services', () => {
|
|
52
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list services successfully', async () => {
|
|
53
|
+
const result = await apiClient.listK8sServices()
|
|
54
|
+
|
|
55
|
+
expect(result.success).toBeTruthy()
|
|
56
|
+
expect(result.data).toBeDefined()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list services in specific namespace', async () => {
|
|
60
|
+
const result = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
61
|
+
|
|
62
|
+
expect(result.success).toBeTruthy()
|
|
63
|
+
expect(result.data).toBeDefined()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should return services with correct fields', async () => {
|
|
67
|
+
const result = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
68
|
+
|
|
69
|
+
if (result.data && Array.isArray(result.data) && result.data.length > 0) {
|
|
70
|
+
const service = result.data[0]
|
|
71
|
+
expect(service).toHaveProperty('name')
|
|
72
|
+
expect(service).toHaveProperty('namespace')
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('Get Service', () => {
|
|
78
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should get service by namespace and name', async () => {
|
|
79
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
80
|
+
|
|
81
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
82
|
+
console.warn('No services found in namespace, skipping test')
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
87
|
+
const serviceName = services[0]?.name
|
|
88
|
+
|
|
89
|
+
if (!serviceName) {
|
|
90
|
+
console.warn('No service name found, skipping test')
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const result = await apiClient.getK8sService(TEST_NAMESPACE, serviceName)
|
|
95
|
+
|
|
96
|
+
expect(result.success).toBeTruthy()
|
|
97
|
+
expect(result.data).toBeDefined()
|
|
98
|
+
expect(result.data?.name).toBe(serviceName)
|
|
99
|
+
expect(result.data?.namespace).toBe(TEST_NAMESPACE)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should fail to get non-existent service', async () => {
|
|
103
|
+
const result = await apiClient.getK8sService(TEST_NAMESPACE, 'non-existent-service')
|
|
104
|
+
|
|
105
|
+
expect(result.success).toBeFalsy()
|
|
106
|
+
expect(result.error).toBeDefined()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('Get Service Status', () => {
|
|
111
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should get service status', async () => {
|
|
112
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
113
|
+
|
|
114
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
115
|
+
console.warn('No services found, skipping test')
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
120
|
+
const serviceName = services[0]?.name
|
|
121
|
+
|
|
122
|
+
if (!serviceName) {
|
|
123
|
+
console.warn('No service name found, skipping test')
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = await apiClient.getK8sServiceStatus(TEST_NAMESPACE, serviceName)
|
|
128
|
+
|
|
129
|
+
expect(result.success).toBeTruthy()
|
|
130
|
+
expect(result.data).toBeDefined()
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
describe('Get Service Logs', () => {
|
|
135
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should get service logs', async () => {
|
|
136
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
137
|
+
|
|
138
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
139
|
+
console.warn('No services found, skipping test')
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
144
|
+
const serviceName = services[0]?.name
|
|
145
|
+
|
|
146
|
+
if (!serviceName) {
|
|
147
|
+
console.warn('No service name found, skipping test')
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const result = await apiClient.getK8sServiceLogs(TEST_NAMESPACE, serviceName, 10)
|
|
152
|
+
|
|
153
|
+
expect(result.success).toBeTruthy()
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('Restart Service', () => {
|
|
158
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should restart service', async () => {
|
|
159
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
160
|
+
|
|
161
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
162
|
+
console.warn('No services found, skipping test')
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
167
|
+
const serviceName = services[0]?.name
|
|
168
|
+
|
|
169
|
+
if (!serviceName) {
|
|
170
|
+
console.warn('No service name found, skipping test')
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const result = await apiClient.restartK8sService(TEST_NAMESPACE, serviceName)
|
|
175
|
+
|
|
176
|
+
expect(result.success).toBeTruthy()
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe('Scale Service', () => {
|
|
181
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should scale service replicas', async () => {
|
|
182
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
183
|
+
|
|
184
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
185
|
+
console.warn('No services found, skipping test')
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
190
|
+
const serviceName = services[0]?.name
|
|
191
|
+
|
|
192
|
+
if (!serviceName) {
|
|
193
|
+
console.warn('No service name found, skipping test')
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const statusResult = await apiClient.getK8sServiceStatus(TEST_NAMESPACE, serviceName)
|
|
198
|
+
const currentReplicas = statusResult.data?.replicas || 1
|
|
199
|
+
|
|
200
|
+
const result = await apiClient.scaleK8sService(TEST_NAMESPACE, serviceName, currentReplicas)
|
|
201
|
+
|
|
202
|
+
expect(result.success).toBeTruthy()
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should fail to scale non-existent service', async () => {
|
|
206
|
+
const result = await apiClient.scaleK8sService(TEST_NAMESPACE, 'non-existent-service', 3)
|
|
207
|
+
|
|
208
|
+
expect(result.success).toBeFalsy()
|
|
209
|
+
expect(result.error).toBeDefined()
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
describe('List Service Pods', () => {
|
|
214
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should list pods for service', async () => {
|
|
215
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
216
|
+
|
|
217
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
218
|
+
console.warn('No services found, skipping test')
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
223
|
+
const serviceName = services[0]?.name
|
|
224
|
+
|
|
225
|
+
if (!serviceName) {
|
|
226
|
+
console.warn('No service name found, skipping test')
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const result = await apiClient.listK8sServicePods(TEST_NAMESPACE, serviceName)
|
|
231
|
+
|
|
232
|
+
expect(result.success).toBeTruthy()
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
describe('Exec in Service', () => {
|
|
237
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should execute command in service pod', async () => {
|
|
238
|
+
const listResult = await apiClient.listK8sServices(TEST_NAMESPACE)
|
|
239
|
+
|
|
240
|
+
if (!listResult.success || !listResult.data || (Array.isArray(listResult.data) && listResult.data.length === 0)) {
|
|
241
|
+
console.warn('No services found, skipping test')
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const services = Array.isArray(listResult.data) ? listResult.data : []
|
|
246
|
+
const serviceName = services[0]?.name
|
|
247
|
+
|
|
248
|
+
if (!serviceName) {
|
|
249
|
+
console.warn('No service name found, skipping test')
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const result = await apiClient.execK8sService(TEST_NAMESPACE, serviceName, 'echo test')
|
|
254
|
+
|
|
255
|
+
expect(result.success).toBeTruthy()
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
describe('Delete Service', () => {
|
|
260
|
+
test.skipIf(!TEST_EMAIL || !TEST_PASSWORD)('should fail to delete non-existent service', async () => {
|
|
261
|
+
const result = await apiClient.deleteK8sService(TEST_NAMESPACE, 'non-existent-service')
|
|
262
|
+
|
|
263
|
+
expect(result.success).toBeFalsy()
|
|
264
|
+
expect(result.error).toBeDefined()
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
})
|