@nexical/cli 0.1.7 → 0.11.0
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/.github/workflows/deploy.yml +3 -3
- package/GEMINI.md +193 -0
- package/README.md +317 -104
- package/dist/chunk-JYASTIIW.js +42 -0
- package/dist/chunk-JYASTIIW.js.map +1 -0
- package/dist/chunk-LZ3YQWAR.js +2204 -0
- package/dist/chunk-LZ3YQWAR.js.map +1 -0
- package/dist/chunk-OKXOCNXP.js +105 -0
- package/dist/chunk-OKXOCNXP.js.map +1 -0
- package/dist/chunk-OYFWMYPG.js +52 -0
- package/dist/chunk-OYFWMYPG.js.map +1 -0
- package/dist/chunk-WKERTCM6.js +74 -0
- package/dist/chunk-WKERTCM6.js.map +1 -0
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/dist/src/commands/init.d.ts +11 -0
- package/dist/src/commands/init.js +89 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/module/add.d.ts +14 -0
- package/dist/src/commands/module/add.js +136 -0
- package/dist/src/commands/module/add.js.map +1 -0
- package/dist/src/commands/module/list.d.ts +10 -0
- package/dist/src/commands/module/list.js +73 -0
- package/dist/src/commands/module/list.js.map +1 -0
- package/dist/src/commands/module/remove.d.ts +12 -0
- package/dist/src/commands/module/remove.js +71 -0
- package/dist/src/commands/module/remove.js.map +1 -0
- package/dist/src/commands/module/update.d.ts +11 -0
- package/dist/src/commands/module/update.js +52 -0
- package/dist/src/commands/module/update.js.map +1 -0
- package/dist/src/commands/run.d.ts +11 -0
- package/dist/src/commands/run.js +93 -0
- package/dist/src/commands/run.js.map +1 -0
- package/dist/src/commands/{login.d.ts → setup.d.ts} +2 -2
- package/dist/src/commands/setup.js +62 -0
- package/dist/src/commands/setup.js.map +1 -0
- package/dist/src/utils/discovery.d.ts +13 -0
- package/dist/src/utils/discovery.js +9 -0
- package/dist/src/utils/git.d.ts +16 -0
- package/dist/src/utils/git.js +29 -0
- package/dist/src/utils/git.js.map +1 -0
- package/dist/src/utils/url-resolver.d.ts +15 -0
- package/dist/src/utils/url-resolver.js +9 -0
- package/dist/src/utils/url-resolver.js.map +1 -0
- package/index.ts +29 -5
- package/package.json +32 -30
- package/src/commands/init.ts +86 -0
- package/src/commands/module/add.ts +169 -0
- package/src/commands/module/list.ts +69 -0
- package/src/commands/module/remove.ts +74 -0
- package/src/commands/module/update.ts +50 -0
- package/src/commands/run.ts +98 -0
- package/src/commands/setup.ts +74 -0
- package/src/utils/discovery.ts +134 -0
- package/src/utils/git.ts +65 -0
- package/src/utils/url-resolver.ts +57 -0
- package/test/e2e/lifecycle.e2e.test.ts +153 -0
- package/test/integration/commands/init.integration.test.ts +85 -0
- package/test/integration/commands/module.integration.test.ts +144 -0
- package/test/integration/commands/run.integration.test.ts +90 -0
- package/test/integration/utils/command-loading.integration.test.ts +80 -0
- package/test/unit/commands/init.test.ts +153 -0
- package/test/unit/commands/module/add.test.ts +262 -0
- package/test/unit/commands/module/list.test.ts +115 -0
- package/test/unit/commands/module/remove.test.ts +89 -0
- package/test/unit/commands/module/update.test.ts +91 -0
- package/test/unit/commands/run.test.ts +252 -0
- package/test/unit/commands/setup.test.ts +169 -0
- package/test/unit/utils/command-discovery.test.ts +176 -0
- package/test/unit/utils/git.test.ts +152 -0
- package/test/unit/utils/integration-helpers.test.ts +72 -0
- package/test/unit/utils/url-resolver.test.ts +39 -0
- package/test/utils/integration-helpers.ts +66 -0
- package/vitest.e2e.config.ts +0 -1
- package/dist/chunk-JDRAVUKK.js +0 -48
- package/dist/chunk-JDRAVUKK.js.map +0 -1
- package/dist/src/commands/admin/create-user.d.ts +0 -15
- package/dist/src/commands/admin/create-user.js +0 -49
- package/dist/src/commands/admin/create-user.js.map +0 -1
- package/dist/src/commands/branch/create.d.ts +0 -19
- package/dist/src/commands/branch/create.js +0 -59
- package/dist/src/commands/branch/create.js.map +0 -1
- package/dist/src/commands/branch/delete.d.ts +0 -15
- package/dist/src/commands/branch/delete.js +0 -50
- package/dist/src/commands/branch/delete.js.map +0 -1
- package/dist/src/commands/branch/get.d.ts +0 -15
- package/dist/src/commands/branch/get.js +0 -53
- package/dist/src/commands/branch/get.js.map +0 -1
- package/dist/src/commands/branch/list.d.ts +0 -15
- package/dist/src/commands/branch/list.js +0 -51
- package/dist/src/commands/branch/list.js.map +0 -1
- package/dist/src/commands/job/get.d.ts +0 -15
- package/dist/src/commands/job/get.js +0 -62
- package/dist/src/commands/job/get.js.map +0 -1
- package/dist/src/commands/job/list.d.ts +0 -15
- package/dist/src/commands/job/list.js +0 -57
- package/dist/src/commands/job/list.js.map +0 -1
- package/dist/src/commands/job/logs.d.ts +0 -15
- package/dist/src/commands/job/logs.js +0 -67
- package/dist/src/commands/job/logs.js.map +0 -1
- package/dist/src/commands/job/trigger.d.ts +0 -19
- package/dist/src/commands/job/trigger.js +0 -74
- package/dist/src/commands/job/trigger.js.map +0 -1
- package/dist/src/commands/login.js +0 -31
- package/dist/src/commands/login.js.map +0 -1
- package/dist/src/commands/project/create.d.ts +0 -24
- package/dist/src/commands/project/create.js +0 -63
- package/dist/src/commands/project/create.js.map +0 -1
- package/dist/src/commands/project/delete.d.ts +0 -20
- package/dist/src/commands/project/delete.js +0 -58
- package/dist/src/commands/project/delete.js.map +0 -1
- package/dist/src/commands/project/get.d.ts +0 -15
- package/dist/src/commands/project/get.js +0 -49
- package/dist/src/commands/project/get.js.map +0 -1
- package/dist/src/commands/project/list.d.ts +0 -15
- package/dist/src/commands/project/list.js +0 -45
- package/dist/src/commands/project/list.js.map +0 -1
- package/dist/src/commands/project/update.d.ts +0 -19
- package/dist/src/commands/project/update.js +0 -66
- package/dist/src/commands/project/update.js.map +0 -1
- package/dist/src/commands/team/create.d.ts +0 -19
- package/dist/src/commands/team/create.js +0 -45
- package/dist/src/commands/team/create.js.map +0 -1
- package/dist/src/commands/team/delete.d.ts +0 -20
- package/dist/src/commands/team/delete.js +0 -52
- package/dist/src/commands/team/delete.js.map +0 -1
- package/dist/src/commands/team/get.d.ts +0 -15
- package/dist/src/commands/team/get.js +0 -42
- package/dist/src/commands/team/get.js.map +0 -1
- package/dist/src/commands/team/list.d.ts +0 -8
- package/dist/src/commands/team/list.js +0 -30
- package/dist/src/commands/team/list.js.map +0 -1
- package/dist/src/commands/team/member/invite.d.ts +0 -20
- package/dist/src/commands/team/member/invite.js +0 -54
- package/dist/src/commands/team/member/invite.js.map +0 -1
- package/dist/src/commands/team/member/remove.d.ts +0 -15
- package/dist/src/commands/team/member/remove.js +0 -43
- package/dist/src/commands/team/member/remove.js.map +0 -1
- package/dist/src/commands/team/update.d.ts +0 -19
- package/dist/src/commands/team/update.js +0 -55
- package/dist/src/commands/team/update.js.map +0 -1
- package/dist/src/commands/token/generate.d.ts +0 -19
- package/dist/src/commands/token/generate.js +0 -48
- package/dist/src/commands/token/generate.js.map +0 -1
- package/dist/src/commands/token/list.d.ts +0 -8
- package/dist/src/commands/token/list.js +0 -31
- package/dist/src/commands/token/list.js.map +0 -1
- package/dist/src/commands/token/revoke.d.ts +0 -15
- package/dist/src/commands/token/revoke.js +0 -38
- package/dist/src/commands/token/revoke.js.map +0 -1
- package/dist/src/commands/whoami.d.ts +0 -8
- package/dist/src/commands/whoami.js +0 -26
- package/dist/src/commands/whoami.js.map +0 -1
- package/dist/src/utils/nexical-client.d.ts +0 -10
- package/dist/src/utils/nexical-client.js +0 -12
- package/src/commands/admin/create-user.ts +0 -46
- package/src/commands/branch/create.ts +0 -57
- package/src/commands/branch/delete.ts +0 -47
- package/src/commands/branch/get.ts +0 -50
- package/src/commands/branch/list.ts +0 -50
- package/src/commands/job/get.ts +0 -59
- package/src/commands/job/list.ts +0 -56
- package/src/commands/job/logs.ts +0 -67
- package/src/commands/job/trigger.ts +0 -73
- package/src/commands/login.ts +0 -31
- package/src/commands/project/create.ts +0 -61
- package/src/commands/project/delete.ts +0 -56
- package/src/commands/project/get.ts +0 -46
- package/src/commands/project/list.ts +0 -44
- package/src/commands/project/update.ts +0 -63
- package/src/commands/team/create.ts +0 -43
- package/src/commands/team/delete.ts +0 -50
- package/src/commands/team/get.ts +0 -39
- package/src/commands/team/list.ts +0 -26
- package/src/commands/team/member/invite.ts +0 -56
- package/src/commands/team/member/remove.ts +0 -40
- package/src/commands/team/update.ts +0 -53
- package/src/commands/token/generate.ts +0 -45
- package/src/commands/token/list.ts +0 -27
- package/src/commands/token/revoke.ts +0 -35
- package/src/commands/whoami.ts +0 -21
- package/src/utils/nexical-client.ts +0 -47
- package/test/e2e/auth.e2e.test.ts +0 -46
- package/test/e2e/job-workflow.e2e.test.ts +0 -33
- package/test/e2e/project-lifecycle.e2e.test.ts +0 -48
- package/test/e2e/setup.ts +0 -237
- package/test/e2e/utils.ts +0 -33
- package/test/integration/commands/admin/create-user.test.ts +0 -51
- package/test/integration/commands/branch/create.test.ts +0 -51
- package/test/integration/commands/branch/delete.test.ts +0 -43
- package/test/integration/commands/branch/get.test.ts +0 -49
- package/test/integration/commands/branch/list.test.ts +0 -47
- package/test/integration/commands/job/get.test.ts +0 -54
- package/test/integration/commands/job/list.test.ts +0 -47
- package/test/integration/commands/job/logs.test.ts +0 -47
- package/test/integration/commands/job/trigger.test.ts +0 -57
- package/test/integration/commands/login.test.ts +0 -62
- package/test/integration/commands/project/create.test.ts +0 -53
- package/test/integration/commands/project/delete.test.ts +0 -43
- package/test/integration/commands/project/get.test.ts +0 -51
- package/test/integration/commands/project/list.test.ts +0 -47
- package/test/integration/commands/project/update.test.ts +0 -53
- package/test/integration/commands/team/create.test.ts +0 -53
- package/test/integration/commands/team/delete.test.ts +0 -43
- package/test/integration/commands/team/get.test.ts +0 -50
- package/test/integration/commands/team/list.test.ts +0 -47
- package/test/integration/commands/team/member/invite.test.ts +0 -46
- package/test/integration/commands/team/member/remove.test.ts +0 -43
- package/test/integration/commands/team/update.test.ts +0 -50
- package/test/integration/commands/token/generate.test.ts +0 -51
- package/test/integration/commands/token/list.test.ts +0 -47
- package/test/integration/commands/token/revoke.test.ts +0 -43
- package/test/integration/commands/whoami.test.ts +0 -49
- package/test/unit/commands/admin/create-user.test.ts +0 -51
- package/test/unit/commands/branch/create.test.ts +0 -57
- package/test/unit/commands/branch/delete.test.ts +0 -49
- package/test/unit/commands/branch/get.test.ts +0 -67
- package/test/unit/commands/branch/list.test.ts +0 -62
- package/test/unit/commands/job/get.test.ts +0 -76
- package/test/unit/commands/job/list.test.ts +0 -62
- package/test/unit/commands/job/logs.test.ts +0 -60
- package/test/unit/commands/job/trigger.test.ts +0 -75
- package/test/unit/commands/login.test.ts +0 -64
- package/test/unit/commands/project/create.test.ts +0 -64
- package/test/unit/commands/project/delete.test.ts +0 -72
- package/test/unit/commands/project/get.test.ts +0 -73
- package/test/unit/commands/project/list.test.ts +0 -62
- package/test/unit/commands/project/update.test.ts +0 -58
- package/test/unit/commands/team/create.test.ts +0 -68
- package/test/unit/commands/team/delete.test.ts +0 -71
- package/test/unit/commands/team/get.test.ts +0 -70
- package/test/unit/commands/team/list.test.ts +0 -56
- package/test/unit/commands/team/member/invite.test.ts +0 -52
- package/test/unit/commands/team/member/remove.test.ts +0 -49
- package/test/unit/commands/team/update.test.ts +0 -63
- package/test/unit/commands/token/generate.test.ts +0 -65
- package/test/unit/commands/token/list.test.ts +0 -58
- package/test/unit/commands/token/revoke.test.ts +0 -49
- package/test/unit/commands/whoami.test.ts +0 -49
- package/test/unit/utils/nexical-client.test.ts +0 -113
- /package/dist/src/utils/{nexical-client.js.map → discovery.js.map} +0 -0
package/test/e2e/setup.ts
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import { beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import http from 'http';
|
|
3
|
-
|
|
4
|
-
const MOCK_PORT = 3333;
|
|
5
|
-
export const TEST_API_URL = `http://localhost:${MOCK_PORT}`;
|
|
6
|
-
|
|
7
|
-
// Simple in-memory mock store
|
|
8
|
-
export const mockStore = {
|
|
9
|
-
users: [] as any[],
|
|
10
|
-
tokens: [] as any[],
|
|
11
|
-
teams: [] as any[],
|
|
12
|
-
projects: [] as any[],
|
|
13
|
-
jobs: [] as any[],
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const resetMockStore = () => {
|
|
17
|
-
mockStore.users = [];
|
|
18
|
-
mockStore.tokens = [];
|
|
19
|
-
mockStore.teams = [
|
|
20
|
-
{ id: 1, name: 'My Team', slug: 'my-team' }
|
|
21
|
-
];
|
|
22
|
-
mockStore.projects = [];
|
|
23
|
-
mockStore.jobs = [];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const server = http.createServer((req, res) => {
|
|
27
|
-
const { method, url } = req;
|
|
28
|
-
let body = '';
|
|
29
|
-
|
|
30
|
-
req.on('data', chunk => {
|
|
31
|
-
body += chunk.toString();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
req.on('end', () => {
|
|
35
|
-
let parsedBody: any = {};
|
|
36
|
-
try {
|
|
37
|
-
if (body) parsedBody = JSON.parse(body);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
// ignore
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.log(`[MOCK API] ${method} ${url}`, parsedBody);
|
|
43
|
-
|
|
44
|
-
res.setHeader('Content-Type', 'application/json');
|
|
45
|
-
|
|
46
|
-
// --- Mock Routes ---
|
|
47
|
-
|
|
48
|
-
// AUTH
|
|
49
|
-
if (method === 'POST' && url === '/auth/device') {
|
|
50
|
-
res.writeHead(200);
|
|
51
|
-
res.end(JSON.stringify({ deviceCode: '1234', userCode: 'ABCD-1234', verificationUrl: 'http://localhost:3000/verify' }));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (method === 'POST' && url === '/auth/token') {
|
|
56
|
-
res.writeHead(200);
|
|
57
|
-
res.end(JSON.stringify({ accessToken: 'mock-access-token', refreshToken: 'mock-refresh-token' }));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (method === 'POST' && url === '/auth/logout') {
|
|
62
|
-
res.writeHead(200);
|
|
63
|
-
res.end(JSON.stringify({ success: true }));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Auth
|
|
68
|
-
if (method === 'GET' && url === '/users/me') {
|
|
69
|
-
const authHeader = req.headers['authorization'];
|
|
70
|
-
const token = authHeader?.split(' ')[1];
|
|
71
|
-
|
|
72
|
-
if (!token || token === 'expired') {
|
|
73
|
-
res.writeHead(401);
|
|
74
|
-
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const user = mockStore.users[0] || {
|
|
79
|
-
id: '123e4567-e89b-12d3-a456-426614174000', // Valid UUID
|
|
80
|
-
email: 'test@example.com',
|
|
81
|
-
fullName: 'Test User',
|
|
82
|
-
avatarUrl: null,
|
|
83
|
-
role: 'user',
|
|
84
|
-
createdAt: new Date().toISOString(),
|
|
85
|
-
updatedAt: new Date().toISOString()
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// Wrap in { user: ... }
|
|
89
|
-
res.writeHead(200);
|
|
90
|
-
res.end(JSON.stringify({ user }));
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// PROJECTS
|
|
95
|
-
if (method === 'POST' && url === '/teams/1/projects') {
|
|
96
|
-
const newProject = {
|
|
97
|
-
id: 101, // SDK expects number
|
|
98
|
-
teamId: 1,
|
|
99
|
-
name: parsedBody.name,
|
|
100
|
-
repoUrl: parsedBody.repoUrl || null,
|
|
101
|
-
productionUrl: null,
|
|
102
|
-
contextHash: null,
|
|
103
|
-
mode: 'managed',
|
|
104
|
-
createdAt: new Date().toISOString(),
|
|
105
|
-
updatedAt: new Date().toISOString(),
|
|
106
|
-
};
|
|
107
|
-
mockStore.projects.push(newProject);
|
|
108
|
-
|
|
109
|
-
// Wrap in { project: ... }
|
|
110
|
-
res.writeHead(201);
|
|
111
|
-
res.end(JSON.stringify({ project: newProject }));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (method === 'GET' && url === '/teams/1/projects') {
|
|
116
|
-
// Wrap in { projects: ... }
|
|
117
|
-
res.writeHead(200);
|
|
118
|
-
res.end(JSON.stringify({ projects: mockStore.projects }));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (method === 'PUT' && url?.match(/\/teams\/1\/projects\/\d+$/)) {
|
|
123
|
-
const id = parseInt(url.split('/').pop() || '0');
|
|
124
|
-
const project = mockStore.projects.find((p: any) => p.id === id);
|
|
125
|
-
if (project) {
|
|
126
|
-
// Update logic mimicking body parse (simplification)
|
|
127
|
-
// content-length usually small in tests
|
|
128
|
-
// For now, return existing or updated stub
|
|
129
|
-
project.name = 'Updated Project'; // Hardcode for test expectation
|
|
130
|
-
res.writeHead(200);
|
|
131
|
-
res.end(JSON.stringify({ project }));
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
res.writeHead(404);
|
|
135
|
-
res.end('Not Found');
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (method === 'DELETE' && url?.match(/\/teams\/1\/projects\/\d+$/)) {
|
|
140
|
-
const id = parseInt(url.split('/').pop() || '0');
|
|
141
|
-
const index = mockStore.projects.findIndex((p: any) => p.id === id);
|
|
142
|
-
if (index !== -1) {
|
|
143
|
-
mockStore.projects.splice(index, 1);
|
|
144
|
-
res.writeHead(200);
|
|
145
|
-
res.end(JSON.stringify({ message: 'Project deleted' }));
|
|
146
|
-
} else {
|
|
147
|
-
res.writeHead(404);
|
|
148
|
-
res.end(JSON.stringify({ message: "Not Found" }));
|
|
149
|
-
}
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// TEAMS
|
|
154
|
-
if (method === 'GET' && url === '/teams/1') {
|
|
155
|
-
// Return team 1 mock
|
|
156
|
-
res.writeHead(200);
|
|
157
|
-
res.end(JSON.stringify({
|
|
158
|
-
id: 1,
|
|
159
|
-
name: 'My Team',
|
|
160
|
-
slug: 'my-team',
|
|
161
|
-
billingPlan: 'free',
|
|
162
|
-
creditsBalance: 100,
|
|
163
|
-
createdAt: new Date().toISOString(),
|
|
164
|
-
updatedAt: new Date().toISOString(),
|
|
165
|
-
role: 'owner'
|
|
166
|
-
}));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Jobs
|
|
171
|
-
if (method === 'POST' && url?.includes('/jobs') && !url.includes('/logs')) {
|
|
172
|
-
const newJob = {
|
|
173
|
-
id: 123,
|
|
174
|
-
branchId: 3,
|
|
175
|
-
type: 'deploy',
|
|
176
|
-
status: 'pending',
|
|
177
|
-
queue: 'public',
|
|
178
|
-
inputs: null,
|
|
179
|
-
outputs: null,
|
|
180
|
-
assignedFactoryId: null,
|
|
181
|
-
startedAt: null,
|
|
182
|
-
completedAt: null,
|
|
183
|
-
createdAt: new Date().toISOString(),
|
|
184
|
-
};
|
|
185
|
-
mockStore.jobs.push(newJob);
|
|
186
|
-
// Wrap in { job: ... }
|
|
187
|
-
res.writeHead(201);
|
|
188
|
-
res.end(JSON.stringify({ job: newJob }));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Logs
|
|
193
|
-
if (method === 'GET' && url?.includes('/logs')) {
|
|
194
|
-
const stubLogs = [
|
|
195
|
-
{
|
|
196
|
-
id: 1,
|
|
197
|
-
jobId: 123,
|
|
198
|
-
level: 'info',
|
|
199
|
-
message: 'Build initialized',
|
|
200
|
-
metadata: null,
|
|
201
|
-
timestamp: new Date().toISOString()
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
id: 2,
|
|
205
|
-
jobId: 123,
|
|
206
|
-
level: 'info',
|
|
207
|
-
message: 'Build successful',
|
|
208
|
-
metadata: null,
|
|
209
|
-
timestamp: new Date().toISOString()
|
|
210
|
-
}
|
|
211
|
-
];
|
|
212
|
-
// Wrap in { logs: ... }
|
|
213
|
-
res.writeHead(200);
|
|
214
|
-
res.end(JSON.stringify({ logs: stubLogs }));
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Default 404
|
|
219
|
-
res.writeHead(404);
|
|
220
|
-
res.end(JSON.stringify({ message: 'Not Found' }));
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
beforeAll(async () => {
|
|
225
|
-
return new Promise<void>((resolve) => {
|
|
226
|
-
server.listen(MOCK_PORT, () => {
|
|
227
|
-
console.log(`Mock API running on port ${MOCK_PORT}`);
|
|
228
|
-
resolve();
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
afterAll(async () => {
|
|
234
|
-
return new Promise<void>((resolve) => {
|
|
235
|
-
server.close(() => resolve());
|
|
236
|
-
});
|
|
237
|
-
});
|
package/test/e2e/utils.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { execa } from "execa";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import fs from 'fs-extra';
|
|
6
|
-
|
|
7
|
-
// Constants
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const CLI_BIN = path.resolve(__dirname, '../../dist/index.js');
|
|
10
|
-
const TEST_API_URL = 'http://localhost:3333';
|
|
11
|
-
export const TEST_HOME = path.resolve(__dirname, '../../test-home');
|
|
12
|
-
|
|
13
|
-
// Ensure test home exists
|
|
14
|
-
if (!fs.existsSync(TEST_HOME)) {
|
|
15
|
-
fs.mkdirpSync(TEST_HOME);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Runs the CLI command against the compiled binary (E2E style)
|
|
20
|
-
*/
|
|
21
|
-
export async function runCLI(args: string[], options: any = {}) {
|
|
22
|
-
return execa("node", [CLI_BIN, ...args], {
|
|
23
|
-
cwd: options.cwd || process.cwd(),
|
|
24
|
-
...options,
|
|
25
|
-
env: {
|
|
26
|
-
...process.env,
|
|
27
|
-
NEXICAL_API_URL: TEST_API_URL, // Point to Mock API
|
|
28
|
-
HOME: TEST_HOME, // Isolate config
|
|
29
|
-
...options.env,
|
|
30
|
-
},
|
|
31
|
-
reject: false, // Allow checking exit code in tests
|
|
32
|
-
});
|
|
33
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import AdminUsersCreateSystemCommand from '../../../../src/commands/admin/create-user.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('AdminUsersCreateSystemCommand Integration', () => {
|
|
9
|
-
let command: AdminUsersCreateSystemCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
auth: {
|
|
17
|
-
createSystemUser: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new AdminUsersCreateSystemCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'success').mockImplementation(() => { });
|
|
25
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should create system user successfully', async () => {
|
|
29
|
-
mockClient.auth.createSystemUser.mockResolvedValue({
|
|
30
|
-
user: { fullName: 'Integration User', id: 'sys-int' }
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
await command.run({ name: 'Integration User', email: 'sys-int@example.com', password: 'password123' });
|
|
34
|
-
|
|
35
|
-
expect(mockClient.auth.createSystemUser).toHaveBeenCalledWith({
|
|
36
|
-
fullName: 'Integration User',
|
|
37
|
-
email: 'sys-int@example.com',
|
|
38
|
-
password: 'password123'
|
|
39
|
-
});
|
|
40
|
-
expect(command.success).toHaveBeenCalledWith('System user "Integration User" created!');
|
|
41
|
-
expect(command.info).toHaveBeenCalledWith('ID: sys-int');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should handle creation failure', async () => {
|
|
45
|
-
mockClient.auth.createSystemUser.mockRejectedValue(new Error('Email already exists'));
|
|
46
|
-
|
|
47
|
-
await command.run({ name: 'Integration User', email: 'sys-int@example.com', password: 'password123' });
|
|
48
|
-
|
|
49
|
-
expect(command.error).toHaveBeenCalledWith('Failed to create system user: Email already exists');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import BranchesCreateCommand from '../../../../src/commands/branch/create.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('BranchesCreateCommand Integration', () => {
|
|
9
|
-
let command: BranchesCreateCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
branches: {
|
|
17
|
-
create: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new BranchesCreateCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'success').mockImplementation(() => { });
|
|
25
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should create branch successfully', async () => {
|
|
29
|
-
mockClient.branches.create.mockResolvedValue({
|
|
30
|
-
name: 'integration-branch',
|
|
31
|
-
id: 'br-int'
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
await command.run({ teamId: '1', projectId: '2', name: 'integration-branch', preview: 'http://int.com' });
|
|
35
|
-
|
|
36
|
-
expect(mockClient.branches.create).toHaveBeenCalledWith(1, 2, {
|
|
37
|
-
name: 'integration-branch',
|
|
38
|
-
previewUrl: 'http://int.com',
|
|
39
|
-
});
|
|
40
|
-
expect(command.success).toHaveBeenCalledWith('Branch "integration-branch" created!');
|
|
41
|
-
expect(command.info).toHaveBeenCalledWith('ID: br-int');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should handle creation failure', async () => {
|
|
45
|
-
mockClient.branches.create.mockRejectedValue(new Error('Branch exists'));
|
|
46
|
-
|
|
47
|
-
await command.run({ teamId: '1', projectId: '2', name: 'main' });
|
|
48
|
-
|
|
49
|
-
expect(command.error).toHaveBeenCalledWith('Failed to create branch: Branch exists');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import BranchesDeleteCommand from '../../../../src/commands/branch/delete.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('BranchesDeleteCommand Integration', () => {
|
|
9
|
-
let command: BranchesDeleteCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
branches: {
|
|
17
|
-
delete: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new BranchesDeleteCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'success').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should delete branch successfully', async () => {
|
|
28
|
-
mockClient.branches.delete.mockResolvedValue({});
|
|
29
|
-
|
|
30
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3' });
|
|
31
|
-
|
|
32
|
-
expect(mockClient.branches.delete).toHaveBeenCalledWith(1, 2, 3);
|
|
33
|
-
expect(command.success).toHaveBeenCalledWith('Branch 3 deleted.');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should handle deletion failure', async () => {
|
|
37
|
-
mockClient.branches.delete.mockRejectedValue(new Error('Not found'));
|
|
38
|
-
|
|
39
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3' });
|
|
40
|
-
|
|
41
|
-
expect(command.error).toHaveBeenCalledWith('Failed to delete branch: Not found');
|
|
42
|
-
});
|
|
43
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import BranchesGetCommand from '../../../../src/commands/branch/get.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('BranchesGetCommand Integration', () => {
|
|
9
|
-
let command: BranchesGetCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
branches: {
|
|
17
|
-
get: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new BranchesGetCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should get branch details successfully', async () => {
|
|
28
|
-
mockClient.branches.get.mockResolvedValue({
|
|
29
|
-
id: '100',
|
|
30
|
-
name: 'integration-main',
|
|
31
|
-
previewUrl: 'http://int.com'
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '100' });
|
|
35
|
-
|
|
36
|
-
expect(mockClient.branches.get).toHaveBeenCalledWith(1, 2, 100);
|
|
37
|
-
expect(command.info).toHaveBeenCalledWith('Branch Details:');
|
|
38
|
-
expect(command.info).toHaveBeenCalledWith(' ID: 100');
|
|
39
|
-
expect(command.info).toHaveBeenCalledWith(' Name: integration-main');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should handle get failure', async () => {
|
|
43
|
-
mockClient.branches.get.mockRejectedValue(new Error('Not found'));
|
|
44
|
-
|
|
45
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '100' });
|
|
46
|
-
|
|
47
|
-
expect(command.error).toHaveBeenCalledWith('Failed to get branch: Not found');
|
|
48
|
-
});
|
|
49
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import BranchesListCommand from '../../../../src/commands/branch/list.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('BranchesListCommand Integration', () => {
|
|
9
|
-
let command: BranchesListCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
branches: {
|
|
17
|
-
list: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new BranchesListCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should list branches successfully', async () => {
|
|
28
|
-
mockClient.branches.list.mockResolvedValue([
|
|
29
|
-
{ id: '100', name: 'integration-main' },
|
|
30
|
-
{ id: '101', name: 'integration-dev' }
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
await command.run({ teamId: '1', projectId: '2' });
|
|
34
|
-
|
|
35
|
-
expect(mockClient.branches.list).toHaveBeenCalledWith(1, 2);
|
|
36
|
-
expect(command.info).toHaveBeenCalledWith('Branches for Project 2:');
|
|
37
|
-
expect(command.info).toHaveBeenCalledWith('- integration-main (ID: 100)');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should handle list failure', async () => {
|
|
41
|
-
mockClient.branches.list.mockRejectedValue(new Error('Network error'));
|
|
42
|
-
|
|
43
|
-
await command.run({ teamId: '1', projectId: '2' });
|
|
44
|
-
|
|
45
|
-
expect(command.error).toHaveBeenCalledWith('Failed to list branches: Network error');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import JobsGetCommand from '../../../../src/commands/job/get.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('JobsGetCommand Integration', () => {
|
|
9
|
-
let command: JobsGetCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
jobs: {
|
|
17
|
-
get: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new JobsGetCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should get job details successfully', async () => {
|
|
28
|
-
mockClient.jobs.get.mockResolvedValue({
|
|
29
|
-
id: '123',
|
|
30
|
-
type: 'build',
|
|
31
|
-
status: 'success',
|
|
32
|
-
createdAt: '2023-01-01T00:00:00Z',
|
|
33
|
-
finishedAt: '2023-01-01T00:05:00Z',
|
|
34
|
-
startedAt: '2023-01-01T00:01:00Z',
|
|
35
|
-
completedAt: '2023-01-01T00:05:00Z',
|
|
36
|
-
queue: 'default'
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3', jobId: '123' });
|
|
40
|
-
|
|
41
|
-
expect(mockClient.jobs.get).toHaveBeenCalledWith(1, 2, 3, 123);
|
|
42
|
-
expect(command.info).toHaveBeenCalledWith('Job Details:');
|
|
43
|
-
expect(command.info).toHaveBeenCalledWith(' ID: 123');
|
|
44
|
-
expect(command.info).toHaveBeenCalledWith(' Status: success');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should handle get failure', async () => {
|
|
48
|
-
mockClient.jobs.get.mockRejectedValue(new Error('Job not found'));
|
|
49
|
-
|
|
50
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3', jobId: '123' });
|
|
51
|
-
|
|
52
|
-
expect(command.error).toHaveBeenCalledWith('Failed to get job: Job not found');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import JobsListCommand from '../../../../src/commands/job/list.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('JobsListCommand Integration', () => {
|
|
9
|
-
let command: JobsListCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
jobs: {
|
|
17
|
-
list: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new JobsListCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should list jobs successfully', async () => {
|
|
28
|
-
mockClient.jobs.list.mockResolvedValue([
|
|
29
|
-
{ id: '101', status: 'running', type: 'build' },
|
|
30
|
-
{ id: '102', status: 'pending', type: 'deploy' }
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3' });
|
|
34
|
-
|
|
35
|
-
expect(mockClient.jobs.list).toHaveBeenCalledWith(1, 2, 3);
|
|
36
|
-
expect(command.info).toHaveBeenCalledWith('Jobs for Branch 3:');
|
|
37
|
-
expect(command.info).toHaveBeenCalledWith('101 - build [running] (Started: Waiting)');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should handle list failure', async () => {
|
|
41
|
-
mockClient.jobs.list.mockRejectedValue(new Error('Network error'));
|
|
42
|
-
|
|
43
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3' });
|
|
44
|
-
|
|
45
|
-
expect(command.error).toHaveBeenCalledWith('Failed to list jobs: Network error');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import JobsLogsCommand from '../../../../src/commands/job/logs.js';
|
|
4
|
-
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
-
|
|
6
|
-
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
-
|
|
8
|
-
describe('JobsLogsCommand Integration', () => {
|
|
9
|
-
let command: JobsLogsCommand;
|
|
10
|
-
let mockClient: any;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.resetAllMocks();
|
|
14
|
-
|
|
15
|
-
mockClient = {
|
|
16
|
-
jobs: {
|
|
17
|
-
getLogs: vi.fn(),
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
-
|
|
22
|
-
command = new JobsLogsCommand([], {} as any);
|
|
23
|
-
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
-
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should get job logs successfully', async () => {
|
|
28
|
-
mockClient.jobs.getLogs.mockResolvedValue([
|
|
29
|
-
{ timestamp: '2023T12:00:00', level: 'info', message: 'Build started' },
|
|
30
|
-
{ timestamp: '2023T12:01:00', level: 'error', message: 'Build failed' }
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3', jobId: '123' });
|
|
34
|
-
|
|
35
|
-
expect(mockClient.jobs.getLogs).toHaveBeenCalledWith(1, 2, 3, 123);
|
|
36
|
-
expect(command.info).toHaveBeenCalledWith(expect.stringContaining('Build started'));
|
|
37
|
-
expect(command.error).toHaveBeenCalledWith(expect.stringContaining('Build failed'));
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should handle log failure', async () => {
|
|
41
|
-
mockClient.jobs.getLogs.mockRejectedValue(new Error('Logs unavailable'));
|
|
42
|
-
|
|
43
|
-
await command.run({ teamId: '1', projectId: '2', branchId: '3', jobId: '123' });
|
|
44
|
-
|
|
45
|
-
expect(command.error).toHaveBeenCalledWith('Failed to get logs: Logs unavailable');
|
|
46
|
-
});
|
|
47
|
-
});
|