@zuplo/cli 6.52.5 → 6.52.7

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.
Files changed (51) hide show
  1. package/dist/__tests__/integration/delete.integration.test.d.ts +2 -0
  2. package/dist/__tests__/integration/delete.integration.test.d.ts.map +1 -0
  3. package/dist/__tests__/integration/delete.integration.test.js +162 -0
  4. package/dist/__tests__/integration/delete.integration.test.js.map +1 -0
  5. package/dist/__tests__/integration/deploy.integration.test.d.ts +2 -0
  6. package/dist/__tests__/integration/deploy.integration.test.d.ts.map +1 -0
  7. package/dist/__tests__/integration/deploy.integration.test.js +249 -0
  8. package/dist/__tests__/integration/deploy.integration.test.js.map +1 -0
  9. package/dist/__tests__/integration/jest-mocks-setup.d.ts +2 -0
  10. package/dist/__tests__/integration/jest-mocks-setup.d.ts.map +1 -0
  11. package/dist/__tests__/integration/jest-mocks-setup.js +59 -0
  12. package/dist/__tests__/integration/jest-mocks-setup.js.map +1 -0
  13. package/dist/__tests__/integration/jest-setup.d.ts +2 -0
  14. package/dist/__tests__/integration/jest-setup.d.ts.map +1 -0
  15. package/dist/__tests__/integration/jest-setup.js +12 -0
  16. package/dist/__tests__/integration/jest-setup.js.map +1 -0
  17. package/dist/__tests__/integration/link.integration.test.d.ts +2 -0
  18. package/dist/__tests__/integration/link.integration.test.d.ts.map +1 -0
  19. package/dist/__tests__/integration/link.integration.test.js +340 -0
  20. package/dist/__tests__/integration/link.integration.test.js.map +1 -0
  21. package/dist/__tests__/integration/list.integration.test.d.ts +2 -0
  22. package/dist/__tests__/integration/list.integration.test.d.ts.map +1 -0
  23. package/dist/__tests__/integration/list.integration.test.js +156 -0
  24. package/dist/__tests__/integration/list.integration.test.js.map +1 -0
  25. package/dist/__tests__/integration/test-utils.d.ts +30 -0
  26. package/dist/__tests__/integration/test-utils.d.ts.map +1 -0
  27. package/dist/__tests__/integration/test-utils.js +82 -0
  28. package/dist/__tests__/integration/test-utils.js.map +1 -0
  29. package/dist/__tests__/integration/tunnel.integration.test.d.ts +2 -0
  30. package/dist/__tests__/integration/tunnel.integration.test.d.ts.map +1 -0
  31. package/dist/__tests__/integration/tunnel.integration.test.js +477 -0
  32. package/dist/__tests__/integration/tunnel.integration.test.js.map +1 -0
  33. package/dist/__tests__/integration/variable.integration.test.d.ts +2 -0
  34. package/dist/__tests__/integration/variable.integration.test.d.ts.map +1 -0
  35. package/dist/__tests__/integration/variable.integration.test.js +258 -0
  36. package/dist/__tests__/integration/variable.integration.test.js.map +1 -0
  37. package/dist/build/handler.d.ts.map +1 -1
  38. package/dist/build/handler.js +8 -1
  39. package/dist/build/handler.js.map +1 -1
  40. package/dist/editor/assets/{index-416489b7.css → index-300931c5.css} +1 -1
  41. package/dist/editor/assets/index-7e947de6.d.ts +2 -0
  42. package/dist/editor/assets/index-7e947de6.d.ts.map +1 -0
  43. package/dist/editor/assets/index-7e947de6.js +10915 -0
  44. package/dist/editor/assets/index-7e947de6.js.map +1 -0
  45. package/dist/editor/index.html +2 -2
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +4 -4
  48. package/dist/editor/assets/index-03352ce7.d.ts +0 -2
  49. package/dist/editor/assets/index-03352ce7.d.ts.map +0 -1
  50. package/dist/editor/assets/index-03352ce7.js +0 -9809
  51. package/dist/editor/assets/index-03352ce7.js.map +0 -1
@@ -0,0 +1,340 @@
1
+ import nock from "nock";
2
+ import yargs from "yargs/yargs";
3
+ import linkCommand from "../../cmds/link.js";
4
+ import { setupTestEnvironment, cleanupTest, setupAuthenticatedNock, RequestCapture, TEST_API_BASE, TEST_ACCOUNT_NAME, TEST_PROJECT_NAME, TEST_AUTH_TOKEN, } from "./test-utils.js";
5
+ jest.doMock("../../common/output.js", () => {
6
+ const originalModule = jest.requireActual("../../common/output.js");
7
+ return {
8
+ ...originalModule,
9
+ __esModule: true,
10
+ printCriticalFailureToConsoleAndExit: jest.fn().mockImplementation(() => {
11
+ throw new Error("Process would exit");
12
+ }),
13
+ };
14
+ });
15
+ jest.mock("../../common/validators/file-system-validator.js", () => ({
16
+ ZuploProjectValidator: jest.fn().mockImplementation(() => ({
17
+ validate: jest.fn().mockResolvedValue(true),
18
+ })),
19
+ }));
20
+ jest.mock("@inquirer/prompts", () => ({
21
+ select: jest.fn().mockResolvedValue("test-environment"),
22
+ }));
23
+ jest.mock("../../link/populate.js", () => ({
24
+ pullSystemConfig: jest.fn().mockResolvedValue(undefined),
25
+ safeMergeConfig: jest.fn().mockResolvedValue(undefined),
26
+ }));
27
+ async function executeLinkCommand(args) {
28
+ const yargsInstance = yargs([])
29
+ .command(linkCommand)
30
+ .help(false)
31
+ .version(false)
32
+ .exitProcess(false);
33
+ const commandArgs = ["link"];
34
+ if (args.account) {
35
+ commandArgs.push("--account", args.account);
36
+ }
37
+ if (args.project) {
38
+ commandArgs.push("--project", args.project);
39
+ }
40
+ if (args["api-key"]) {
41
+ commandArgs.push("--api-key", args["api-key"]);
42
+ }
43
+ if (args.environment) {
44
+ commandArgs.push("--environment", args.environment);
45
+ }
46
+ if (args.dir) {
47
+ commandArgs.push("--dir", args.dir);
48
+ }
49
+ return await yargsInstance.parse(commandArgs);
50
+ }
51
+ const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
52
+ const mockPrintCriticalFailure = jest.mocked(require("../../common/output.js").printCriticalFailureToConsoleAndExit);
53
+ describe("Link Command Integration Tests", () => {
54
+ let requestCapture;
55
+ beforeEach(() => {
56
+ setupTestEnvironment();
57
+ requestCapture = new RequestCapture();
58
+ nock.disableNetConnect();
59
+ jest.clearAllMocks();
60
+ });
61
+ afterEach(() => {
62
+ cleanupTest();
63
+ requestCapture.clear();
64
+ });
65
+ describe("Link with project and environment specified", () => {
66
+ it("should skip API calls when project and environment are provided", async () => {
67
+ await executeLinkCommand({
68
+ account: TEST_ACCOUNT_NAME,
69
+ project: TEST_PROJECT_NAME,
70
+ "api-key": TEST_AUTH_TOKEN,
71
+ environment: "development",
72
+ dir: ".",
73
+ });
74
+ expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining(`Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`));
75
+ const capturedRequests = requestCapture.getRequests();
76
+ expect(capturedRequests).toEqual([]);
77
+ });
78
+ });
79
+ describe("Link with automatic project selection", () => {
80
+ it("should intercept GET request to list projects", async () => {
81
+ const mockProjectsResponse = {
82
+ data: [
83
+ {
84
+ name: TEST_PROJECT_NAME,
85
+ accountName: TEST_ACCOUNT_NAME,
86
+ description: "Test project",
87
+ createdAt: "2024-01-01T00:00:00Z",
88
+ },
89
+ ],
90
+ };
91
+ const mockEnvironmentsResponse = {
92
+ data: [
93
+ {
94
+ name: "development",
95
+ environmentType: "development",
96
+ branchName: "main",
97
+ accountName: TEST_ACCOUNT_NAME,
98
+ projectName: TEST_PROJECT_NAME,
99
+ },
100
+ ],
101
+ };
102
+ const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
103
+ .get("/v1/projects")
104
+ .reply(200, mockProjectsResponse);
105
+ const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
106
+ .get("/v1/environments")
107
+ .query({
108
+ accountName: TEST_ACCOUNT_NAME,
109
+ projectName: TEST_PROJECT_NAME,
110
+ })
111
+ .reply(200, mockEnvironmentsResponse);
112
+ [projectsScope, environmentsScope].forEach((scope) => {
113
+ scope.on("request", (req, interceptor, body) => {
114
+ requestCapture.capture(req, interceptor, body);
115
+ });
116
+ });
117
+ await executeLinkCommand({
118
+ account: TEST_ACCOUNT_NAME,
119
+ "api-key": TEST_AUTH_TOKEN,
120
+ dir: ".",
121
+ });
122
+ expect(projectsScope.isDone()).toBe(true);
123
+ expect(environmentsScope.isDone()).toBe(true);
124
+ expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining(`Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`));
125
+ const capturedRequests = requestCapture.getRequests();
126
+ expect(capturedRequests).toMatchSnapshot("link-automatic-selection-requests");
127
+ });
128
+ it("should handle multiple projects and prompt selection", async () => {
129
+ const mockProjectsResponse = {
130
+ data: [
131
+ { name: "project-1", accountName: TEST_ACCOUNT_NAME },
132
+ { name: "project-2", accountName: TEST_ACCOUNT_NAME },
133
+ ],
134
+ };
135
+ const mockEnvironmentsResponse = {
136
+ data: [
137
+ {
138
+ name: "development",
139
+ environmentType: "development",
140
+ branchName: "main",
141
+ accountName: TEST_ACCOUNT_NAME,
142
+ projectName: "test-environment",
143
+ },
144
+ ],
145
+ };
146
+ const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
147
+ .get("/v1/projects")
148
+ .reply(200, mockProjectsResponse);
149
+ const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
150
+ .get("/v1/environments")
151
+ .query({
152
+ accountName: TEST_ACCOUNT_NAME,
153
+ projectName: "test-environment",
154
+ })
155
+ .reply(200, mockEnvironmentsResponse);
156
+ [projectsScope, environmentsScope].forEach((scope) => {
157
+ scope.on("request", (req, interceptor, body) => {
158
+ requestCapture.capture(req, interceptor, body);
159
+ });
160
+ });
161
+ await executeLinkCommand({
162
+ account: TEST_ACCOUNT_NAME,
163
+ "api-key": TEST_AUTH_TOKEN,
164
+ dir: ".",
165
+ });
166
+ expect(projectsScope.isDone()).toBe(true);
167
+ expect(environmentsScope.isDone()).toBe(true);
168
+ const capturedRequests = requestCapture.getRequests();
169
+ expect(capturedRequests).toMatchSnapshot("link-multiple-projects-requests");
170
+ });
171
+ });
172
+ describe("Link with environment creation", () => {
173
+ it("should create development environment when none exists", async () => {
174
+ const mockProjectsResponse = {
175
+ data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],
176
+ };
177
+ const mockEmptyEnvironmentsResponse = { data: [] };
178
+ const mockCreatedEnvironment = {
179
+ name: "development",
180
+ environmentType: "development",
181
+ accountName: TEST_ACCOUNT_NAME,
182
+ projectName: TEST_PROJECT_NAME,
183
+ };
184
+ const mockEnvironmentsAfterCreate = {
185
+ data: [mockCreatedEnvironment],
186
+ };
187
+ const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
188
+ .get("/v1/projects")
189
+ .reply(200, mockProjectsResponse);
190
+ const environmentsScope1 = setupAuthenticatedNock(nock(TEST_API_BASE))
191
+ .get("/v1/environments")
192
+ .query({
193
+ accountName: TEST_ACCOUNT_NAME,
194
+ projectName: TEST_PROJECT_NAME,
195
+ })
196
+ .reply(200, mockEmptyEnvironmentsResponse);
197
+ const createEnvironmentScope = setupAuthenticatedNock(nock(TEST_API_BASE))
198
+ .post("/v1/environments")
199
+ .reply(201, mockCreatedEnvironment);
200
+ const environmentsScope2 = setupAuthenticatedNock(nock(TEST_API_BASE))
201
+ .get("/v1/environments")
202
+ .query({
203
+ accountName: TEST_ACCOUNT_NAME,
204
+ projectName: TEST_PROJECT_NAME,
205
+ })
206
+ .reply(200, mockEnvironmentsAfterCreate);
207
+ [
208
+ projectsScope,
209
+ environmentsScope1,
210
+ createEnvironmentScope,
211
+ environmentsScope2,
212
+ ].forEach((scope) => {
213
+ scope.on("request", (req, interceptor, body) => {
214
+ requestCapture.capture(req, interceptor, body);
215
+ });
216
+ });
217
+ await executeLinkCommand({
218
+ account: TEST_ACCOUNT_NAME,
219
+ "api-key": TEST_AUTH_TOKEN,
220
+ dir: ".",
221
+ });
222
+ expect(projectsScope.isDone()).toBe(true);
223
+ expect(environmentsScope1.isDone()).toBe(true);
224
+ expect(createEnvironmentScope.isDone()).toBe(true);
225
+ expect(environmentsScope2.isDone()).toBe(true);
226
+ const capturedRequests = requestCapture.getRequests();
227
+ expect(capturedRequests).toMatchSnapshot("link-create-environment-requests");
228
+ const createRequest = capturedRequests.find((req) => req.method === "POST" && req.path === "/v1/environments");
229
+ expect(createRequest?.body).toEqual({
230
+ accountName: TEST_ACCOUNT_NAME,
231
+ projectName: TEST_PROJECT_NAME,
232
+ environmentType: "development",
233
+ });
234
+ });
235
+ });
236
+ describe("Error handling", () => {
237
+ it("should handle projects list failure", async () => {
238
+ const errorResponse = {
239
+ error: "Unauthorized",
240
+ message: "Invalid API key",
241
+ statusCode: 401,
242
+ };
243
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
244
+ .get("/v1/projects")
245
+ .reply(401, errorResponse);
246
+ scope.on("request", (req, interceptor, body) => {
247
+ requestCapture.capture(req, interceptor, body);
248
+ });
249
+ await expect(executeLinkCommand({
250
+ account: TEST_ACCOUNT_NAME,
251
+ "api-key": TEST_AUTH_TOKEN,
252
+ dir: ".",
253
+ })).rejects.toThrow("Process would exit");
254
+ expect(scope.isDone()).toBe(true);
255
+ expect(mockPrintCriticalFailure).toHaveBeenCalled();
256
+ const capturedRequests = requestCapture.getRequests();
257
+ expect(capturedRequests).toMatchSnapshot("link-projects-error-requests");
258
+ });
259
+ it("should handle environments list failure", async () => {
260
+ const mockProjectsResponse = {
261
+ data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],
262
+ };
263
+ const errorResponse = {
264
+ error: "Forbidden",
265
+ message: "Access denied to project",
266
+ statusCode: 403,
267
+ };
268
+ const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
269
+ .get("/v1/projects")
270
+ .reply(200, mockProjectsResponse);
271
+ const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))
272
+ .get("/v1/environments")
273
+ .query({
274
+ accountName: TEST_ACCOUNT_NAME,
275
+ projectName: TEST_PROJECT_NAME,
276
+ })
277
+ .reply(403, errorResponse);
278
+ [projectsScope, environmentsScope].forEach((scope) => {
279
+ scope.on("request", (req, interceptor, body) => {
280
+ requestCapture.capture(req, interceptor, body);
281
+ });
282
+ });
283
+ await expect(executeLinkCommand({
284
+ account: TEST_ACCOUNT_NAME,
285
+ "api-key": TEST_AUTH_TOKEN,
286
+ dir: ".",
287
+ })).rejects.toThrow("Process would exit");
288
+ expect(projectsScope.isDone()).toBe(true);
289
+ expect(environmentsScope.isDone()).toBe(true);
290
+ expect(mockPrintCriticalFailure).toHaveBeenCalled();
291
+ const capturedRequests = requestCapture.getRequests();
292
+ expect(capturedRequests).toMatchSnapshot("link-environments-error-requests");
293
+ });
294
+ });
295
+ describe("Request validation", () => {
296
+ it("should include correct headers for API requests", async () => {
297
+ const mockProjectsResponse = {
298
+ data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],
299
+ };
300
+ const mockEnvironmentsResponse = {
301
+ data: [
302
+ {
303
+ name: "development",
304
+ environmentType: "development",
305
+ branchName: "main",
306
+ accountName: TEST_ACCOUNT_NAME,
307
+ projectName: TEST_PROJECT_NAME,
308
+ },
309
+ ],
310
+ };
311
+ const projectsScope = nock(TEST_API_BASE)
312
+ .get("/v1/projects")
313
+ .matchHeader("authorization", `Bearer ${TEST_AUTH_TOKEN}`)
314
+ .reply(200, mockProjectsResponse);
315
+ const environmentsScope = nock(TEST_API_BASE)
316
+ .get("/v1/environments")
317
+ .query({
318
+ accountName: TEST_ACCOUNT_NAME,
319
+ projectName: TEST_PROJECT_NAME,
320
+ })
321
+ .matchHeader("authorization", `Bearer ${TEST_AUTH_TOKEN}`)
322
+ .reply(200, mockEnvironmentsResponse);
323
+ [projectsScope, environmentsScope].forEach((scope) => {
324
+ scope.on("request", (req, interceptor, body) => {
325
+ requestCapture.capture(req, interceptor, body);
326
+ });
327
+ });
328
+ await executeLinkCommand({
329
+ account: TEST_ACCOUNT_NAME,
330
+ "api-key": TEST_AUTH_TOKEN,
331
+ dir: ".",
332
+ });
333
+ expect(projectsScope.isDone()).toBe(true);
334
+ expect(environmentsScope.isDone()).toBe(true);
335
+ const capturedRequests = requestCapture.getRequests();
336
+ expect(capturedRequests).toMatchSnapshot("link-headers-validation");
337
+ });
338
+ });
339
+ });
340
+ //# sourceMappingURL=link.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/link.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the link command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport linkCommand from \"../../cmds/link.js\";\nimport {\n setupTestEnvironment,\n cleanupTest,\n setupAuthenticatedNock,\n RequestCapture,\n TEST_API_BASE,\n TEST_ACCOUNT_NAME,\n TEST_PROJECT_NAME,\n TEST_AUTH_TOKEN,\n} from \"./test-utils.js\";\n\n// Override only the critical failure mock for link tests to simulate process exit\njest.doMock(\"../../common/output.js\", () => {\n const originalModule = jest.requireActual(\"../../common/output.js\");\n return {\n ...originalModule,\n __esModule: true,\n printCriticalFailureToConsoleAndExit: jest.fn().mockImplementation(() => {\n throw new Error(\"Process would exit\"); // Simulate process exit\n }),\n };\n});\n\n// Mock file system validator\njest.mock(\"../../common/validators/file-system-validator.js\", () => ({\n ZuploProjectValidator: jest.fn().mockImplementation(() => ({\n validate: jest.fn().mockResolvedValue(true),\n })),\n}));\n\n// Mock inquirer prompts to avoid interactive prompts\njest.mock(\"@inquirer/prompts\", () => ({\n select: jest.fn().mockResolvedValue(\"test-environment\"),\n}));\n\n// Mock link population functions to prevent file system operations\njest.mock(\"../../link/populate.js\", () => ({\n pullSystemConfig: jest.fn().mockResolvedValue(undefined),\n safeMergeConfig: jest.fn().mockResolvedValue(undefined),\n}));\n\n/**\n * Execute link command with given arguments using actual yargs command\n */\nasync function executeLinkCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n environment?: string;\n dir?: string;\n}) {\n const yargsInstance = yargs([])\n .command(linkCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"link\"];\n\n if (args.account) {\n commandArgs.push(\"--account\", args.account);\n }\n\n if (args.project) {\n commandArgs.push(\"--project\", args.project);\n }\n\n if (args[\"api-key\"]) {\n commandArgs.push(\"--api-key\", args[\"api-key\"]);\n }\n\n if (args.environment) {\n commandArgs.push(\"--environment\", args.environment);\n }\n\n if (args.dir) {\n commandArgs.push(\"--dir\", args.dir);\n }\n\n // Parse and execute the command\n return await yargsInstance.parse(commandArgs);\n}\n\n// Get the mocked functions\nconst mockPrintResult = jest.mocked(\n require(\"../../common/output.js\").printResultToConsoleAndExitGracefully\n);\nconst mockPrintCriticalFailure = jest.mocked(\n require(\"../../common/output.js\").printCriticalFailureToConsoleAndExit\n);\n\ndescribe(\"Link Command Integration Tests\", () => {\n let requestCapture: RequestCapture;\n\n beforeEach(() => {\n setupTestEnvironment();\n requestCapture = new RequestCapture();\n\n // Disable real HTTP requests\n nock.disableNetConnect();\n\n // Clear mock calls from previous tests\n jest.clearAllMocks();\n });\n\n afterEach(() => {\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"Link with project and environment specified\", () => {\n it(\"should skip API calls when project and environment are provided\", async () => {\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n environment: \"development\",\n dir: \".\",\n });\n\n // Verify successful linking message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n `Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`\n )\n );\n\n // No HTTP requests should be made when all params are provided\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toEqual([]);\n });\n });\n\n describe(\"Link with automatic project selection\", () => {\n it(\"should intercept GET request to list projects\", async () => {\n const mockProjectsResponse = {\n data: [\n {\n name: TEST_PROJECT_NAME,\n accountName: TEST_ACCOUNT_NAME,\n description: \"Test project\",\n createdAt: \"2024-01-01T00:00:00Z\",\n },\n ],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n };\n\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .reply(200, mockProjectsResponse);\n\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEnvironmentsResponse);\n\n [projectsScope, environmentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n `Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`\n )\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-automatic-selection-requests\"\n );\n });\n\n it(\"should handle multiple projects and prompt selection\", async () => {\n const mockProjectsResponse = {\n data: [\n { name: \"project-1\", accountName: TEST_ACCOUNT_NAME },\n { name: \"project-2\", accountName: TEST_ACCOUNT_NAME },\n ],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: \"test-environment\",\n },\n ],\n };\n\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .reply(200, mockProjectsResponse);\n\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: \"test-environment\",\n })\n .reply(200, mockEnvironmentsResponse);\n\n [projectsScope, environmentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-multiple-projects-requests\"\n );\n });\n });\n\n describe(\"Link with environment creation\", () => {\n it(\"should create development environment when none exists\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const mockEmptyEnvironmentsResponse = { data: [] };\n const mockCreatedEnvironment = {\n name: \"development\",\n environmentType: \"development\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n };\n const mockEnvironmentsAfterCreate = {\n data: [mockCreatedEnvironment],\n };\n\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .reply(200, mockProjectsResponse);\n\n const environmentsScope1 = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEmptyEnvironmentsResponse);\n\n const createEnvironmentScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/environments\")\n .reply(201, mockCreatedEnvironment);\n\n const environmentsScope2 = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEnvironmentsAfterCreate);\n\n [\n projectsScope,\n environmentsScope1,\n createEnvironmentScope,\n environmentsScope2,\n ].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope1.isDone()).toBe(true);\n expect(createEnvironmentScope.isDone()).toBe(true);\n expect(environmentsScope2.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-create-environment-requests\"\n );\n\n // Verify environment creation request body\n const createRequest = capturedRequests.find(\n (req) => req.method === \"POST\" && req.path === \"/v1/environments\"\n );\n expect(createRequest?.body).toEqual({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n environmentType: \"development\",\n });\n });\n });\n\n describe(\"Error handling\", () => {\n it(\"should handle projects list failure\", async () => {\n const errorResponse = {\n error: \"Unauthorized\",\n message: \"Invalid API key\",\n statusCode: 401,\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .reply(401, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n // Expect the function to throw (simulating process exit)\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintCriticalFailure).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"link-projects-error-requests\");\n });\n\n it(\"should handle environments list failure\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const errorResponse = {\n error: \"Forbidden\",\n message: \"Access denied to project\",\n statusCode: 403,\n };\n\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .reply(200, mockProjectsResponse);\n\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(403, errorResponse);\n\n [projectsScope, environmentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Expect the function to throw (simulating process exit)\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(mockPrintCriticalFailure).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-environments-error-requests\"\n );\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers for API requests\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n };\n\n const projectsScope = nock(TEST_API_BASE)\n .get(\"/v1/projects\")\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockProjectsResponse);\n\n const environmentsScope = nock(TEST_API_BASE)\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockEnvironmentsResponse);\n\n [projectsScope, environmentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"link-headers-validation\");\n });\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=list.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/list.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ import nock from "nock";
2
+ import yargs from "yargs/yargs";
3
+ import listCommand from "../../cmds/list.js";
4
+ import { setupTestEnvironment, cleanupTest, setupAuthenticatedNock, RequestCapture, TEST_API_BASE, TEST_ACCOUNT_NAME, TEST_PROJECT_NAME, TEST_AUTH_TOKEN, } from "./test-utils.js";
5
+ async function executeListCommand(args) {
6
+ const yargsInstance = yargs([])
7
+ .command(listCommand)
8
+ .help(false)
9
+ .version(false)
10
+ .exitProcess(false);
11
+ const commandArgs = ["list"];
12
+ if (args.account) {
13
+ commandArgs.push("--account", args.account);
14
+ }
15
+ if (args.project) {
16
+ commandArgs.push("--project", args.project);
17
+ }
18
+ if (args["api-key"]) {
19
+ commandArgs.push("--api-key", args["api-key"]);
20
+ }
21
+ if (args["self-hosted-endpoint"]) {
22
+ commandArgs.push("--self-hosted-endpoint", args["self-hosted-endpoint"]);
23
+ }
24
+ return await yargsInstance.parse(commandArgs);
25
+ }
26
+ const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
27
+ const mockPrintDiagnostics = jest.mocked(require("../../common/output.js").printDiagnosticsToConsole);
28
+ describe("List Command Integration Tests", () => {
29
+ let requestCapture;
30
+ beforeEach(() => {
31
+ setupTestEnvironment();
32
+ requestCapture = new RequestCapture();
33
+ nock.disableNetConnect();
34
+ jest.clearAllMocks();
35
+ });
36
+ afterEach(() => {
37
+ cleanupTest();
38
+ requestCapture.clear();
39
+ });
40
+ describe("SaaS version", () => {
41
+ it("should intercept GET request to list deployments with account and project", async () => {
42
+ const mockResponse = {
43
+ data: [
44
+ { url: "https://test-project-production.zuplo.app" },
45
+ { url: "https://test-project-staging.zuplo.app" },
46
+ ],
47
+ };
48
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
49
+ .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`)
50
+ .reply(200, mockResponse);
51
+ scope.on("request", (req, interceptor, body) => {
52
+ requestCapture.capture(req, interceptor, body);
53
+ });
54
+ await executeListCommand({
55
+ account: TEST_ACCOUNT_NAME,
56
+ project: TEST_PROJECT_NAME,
57
+ "api-key": TEST_AUTH_TOKEN,
58
+ });
59
+ expect(scope.isDone()).toBe(true);
60
+ const expectedOutput = "https://test-project-production.zuplo.app\nhttps://test-project-staging.zuplo.app";
61
+ expect(mockPrintResult).toHaveBeenCalledWith(expectedOutput);
62
+ const capturedRequests = requestCapture.getRequests();
63
+ expect(capturedRequests).toMatchSnapshot("list-deployments-saas-requests");
64
+ });
65
+ it("should handle empty deployments list", async () => {
66
+ const mockResponse = { data: [] };
67
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
68
+ .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`)
69
+ .reply(200, mockResponse);
70
+ scope.on("request", (req, interceptor, body) => {
71
+ requestCapture.capture(req, interceptor, body);
72
+ });
73
+ await executeListCommand({
74
+ account: TEST_ACCOUNT_NAME,
75
+ project: TEST_PROJECT_NAME,
76
+ "api-key": TEST_AUTH_TOKEN,
77
+ });
78
+ expect(scope.isDone()).toBe(true);
79
+ expect(mockPrintResult).toHaveBeenCalledWith("");
80
+ const capturedRequests = requestCapture.getRequests();
81
+ expect(capturedRequests).toMatchSnapshot("list-deployments-empty-requests");
82
+ });
83
+ it("should handle API error responses", async () => {
84
+ const errorResponse = {
85
+ error: "Not Found",
86
+ message: "Project not found",
87
+ statusCode: 404,
88
+ };
89
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
90
+ .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`)
91
+ .reply(404, errorResponse);
92
+ scope.on("request", (req, interceptor, body) => {
93
+ requestCapture.capture(req, interceptor, body);
94
+ });
95
+ await executeListCommand({
96
+ account: TEST_ACCOUNT_NAME,
97
+ project: TEST_PROJECT_NAME,
98
+ "api-key": TEST_AUTH_TOKEN,
99
+ });
100
+ expect(scope.isDone()).toBe(true);
101
+ expect(mockPrintDiagnostics).toHaveBeenCalled();
102
+ const capturedRequests = requestCapture.getRequests();
103
+ expect(capturedRequests).toMatchSnapshot("list-deployments-error-requests");
104
+ });
105
+ });
106
+ describe("Self-hosted version", () => {
107
+ it("should intercept GET request to self-hosted endpoint", async () => {
108
+ const selfHostedEndpoint = "https://my-zuplo.company.com";
109
+ const mockResponse = {
110
+ data: [
111
+ { deploymentUrl: "https://my-zuplo.company.com/project1" },
112
+ { deploymentUrl: "https://my-zuplo.company.com/project2" },
113
+ ],
114
+ };
115
+ const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))
116
+ .get("/v1/deployments")
117
+ .reply(200, mockResponse);
118
+ scope.on("request", (req, interceptor, body) => {
119
+ requestCapture.capture(req, interceptor, body);
120
+ });
121
+ await executeListCommand({
122
+ account: TEST_ACCOUNT_NAME,
123
+ project: TEST_PROJECT_NAME,
124
+ "api-key": TEST_AUTH_TOKEN,
125
+ "self-hosted-endpoint": selfHostedEndpoint,
126
+ });
127
+ expect(scope.isDone()).toBe(true);
128
+ const expectedOutput = "https://my-zuplo.company.com/project1\nhttps://my-zuplo.company.com/project2";
129
+ expect(mockPrintResult).toHaveBeenCalledWith(expectedOutput);
130
+ const capturedRequests = requestCapture.getRequests();
131
+ expect(capturedRequests).toMatchSnapshot("list-deployments-self-hosted-requests");
132
+ });
133
+ });
134
+ describe("Request headers validation", () => {
135
+ it("should include correct authorization headers", async () => {
136
+ const mockResponse = { data: [{ url: "https://test.zuplo.app" }] };
137
+ const scope = nock(TEST_API_BASE)
138
+ .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`)
139
+ .matchHeader("authorization", `Bearer ${TEST_AUTH_TOKEN}`)
140
+ .reply(200, mockResponse);
141
+ scope.on("request", (req, interceptor, body) => {
142
+ requestCapture.capture(req, interceptor, body);
143
+ });
144
+ await executeListCommand({
145
+ account: TEST_ACCOUNT_NAME,
146
+ project: TEST_PROJECT_NAME,
147
+ "api-key": TEST_AUTH_TOKEN,
148
+ });
149
+ expect(scope.isDone()).toBe(true);
150
+ expect(mockPrintResult).toHaveBeenCalledWith("https://test.zuplo.app");
151
+ const capturedRequests = requestCapture.getRequests();
152
+ expect(capturedRequests).toMatchSnapshot("list-deployments-headers-validation");
153
+ });
154
+ });
155
+ });
156
+ //# sourceMappingURL=list.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/list.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the list command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport listCommand from \"../../cmds/list.js\";\nimport {\n setupTestEnvironment,\n cleanupTest,\n setupAuthenticatedNock,\n RequestCapture,\n TEST_API_BASE,\n TEST_ACCOUNT_NAME,\n TEST_PROJECT_NAME,\n TEST_AUTH_TOKEN,\n} from \"./test-utils.js\";\n\n/**\n * Execute list command with given arguments using actual yargs command\n */\nasync function executeListCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n \"self-hosted-endpoint\"?: string;\n}) {\n const yargsInstance = yargs([])\n .command(listCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"list\"];\n\n if (args.account) {\n commandArgs.push(\"--account\", args.account);\n }\n\n if (args.project) {\n commandArgs.push(\"--project\", args.project);\n }\n\n if (args[\"api-key\"]) {\n commandArgs.push(\"--api-key\", args[\"api-key\"]);\n }\n\n if (args[\"self-hosted-endpoint\"]) {\n commandArgs.push(\"--self-hosted-endpoint\", args[\"self-hosted-endpoint\"]);\n }\n\n // Parse and execute the command\n return await yargsInstance.parse(commandArgs);\n}\n\n// Get the mocked functions\nconst mockPrintResult = jest.mocked(\n require(\"../../common/output.js\").printResultToConsoleAndExitGracefully\n);\nconst mockPrintDiagnostics = jest.mocked(\n require(\"../../common/output.js\").printDiagnosticsToConsole\n);\n\ndescribe(\"List Command Integration Tests\", () => {\n let requestCapture: RequestCapture;\n\n beforeEach(() => {\n setupTestEnvironment();\n requestCapture = new RequestCapture();\n\n // Disable real HTTP requests\n nock.disableNetConnect();\n\n // Clear mock calls from previous tests\n jest.clearAllMocks();\n });\n\n afterEach(() => {\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"SaaS version\", () => {\n it(\"should intercept GET request to list deployments with account and project\", async () => {\n const mockResponse = {\n data: [\n { url: \"https://test-project-production.zuplo.app\" },\n { url: \"https://test-project-staging.zuplo.app\" },\n ],\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, mockResponse);\n\n // Capture requests for snapshot testing\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n // Execute the list command through yargs\n await executeListCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n });\n\n // Verify the request was made\n expect(scope.isDone()).toBe(true);\n\n // Verify the output function was called with the URLs\n const expectedOutput =\n \"https://test-project-production.zuplo.app\\nhttps://test-project-staging.zuplo.app\";\n expect(mockPrintResult).toHaveBeenCalledWith(expectedOutput);\n\n // Snapshot test for captured requests\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"list-deployments-saas-requests\"\n );\n });\n\n it(\"should handle empty deployments list\", async () => {\n const mockResponse = { data: [] };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, mockResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeListCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n });\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintResult).toHaveBeenCalledWith(\"\");\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"list-deployments-empty-requests\"\n );\n });\n\n it(\"should handle API error responses\", async () => {\n const errorResponse = {\n error: \"Not Found\",\n message: \"Project not found\",\n statusCode: 404,\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(404, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeListCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n });\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintDiagnostics).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"list-deployments-error-requests\"\n );\n });\n });\n\n describe(\"Self-hosted version\", () => {\n it(\"should intercept GET request to self-hosted endpoint\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const mockResponse = {\n data: [\n { deploymentUrl: \"https://my-zuplo.company.com/project1\" },\n { deploymentUrl: \"https://my-zuplo.company.com/project2\" },\n ],\n };\n\n const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .get(\"/v1/deployments\")\n .reply(200, mockResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeListCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n });\n\n expect(scope.isDone()).toBe(true);\n const expectedOutput =\n \"https://my-zuplo.company.com/project1\\nhttps://my-zuplo.company.com/project2\";\n expect(mockPrintResult).toHaveBeenCalledWith(expectedOutput);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"list-deployments-self-hosted-requests\"\n );\n });\n });\n\n describe(\"Request headers validation\", () => {\n it(\"should include correct authorization headers\", async () => {\n const mockResponse = { data: [{ url: \"https://test.zuplo.app\" }] };\n\n const scope = nock(TEST_API_BASE)\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeListCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n });\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintResult).toHaveBeenCalledWith(\"https://test.zuplo.app\");\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"list-deployments-headers-validation\"\n );\n });\n });\n});\n"]}
@@ -0,0 +1,30 @@
1
+ import nock from "nock";
2
+ export declare const TEST_AUTH_TOKEN = "test-auth-token-123";
3
+ export declare const TEST_ACCOUNT_NAME = "test-account";
4
+ export declare const TEST_PROJECT_NAME = "test-project";
5
+ export declare const TEST_BRANCH_NAME = "main";
6
+ export declare const TEST_DEPLOYMENT_NAME = "test-deployment";
7
+ export declare const TEST_API_BASE: string;
8
+ export declare function setupAuthenticatedNock(scope: nock.Scope): nock.Scope;
9
+ export declare function setupTestEnvironment(): void;
10
+ export declare function cleanupTest(): void;
11
+ export interface CapturedRequest {
12
+ method: string;
13
+ path: string;
14
+ headers: Record<string, string>;
15
+ body?: unknown;
16
+ }
17
+ interface NockRequest {
18
+ method?: string;
19
+ path?: string;
20
+ headers?: Record<string, unknown>;
21
+ }
22
+ export declare class RequestCapture {
23
+ private requests;
24
+ capture(req: NockRequest, _interceptor?: unknown, body?: unknown): void;
25
+ getRequests(): CapturedRequest[];
26
+ clear(): void;
27
+ private normalizeHeaders;
28
+ }
29
+ export {};
30
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/test-utils.ts"],"names":[],"mappings":"AAGA,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,eAAO,MAAM,eAAe,wBAAwB,CAAC;AACrD,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,gBAAgB,SAAS,CAAC;AACvC,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAGtD,eAAO,MAAM,aAAa,QAAwC,CAAC;AAKnE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAEpE;AAKD,wBAAgB,oBAAoB,SAYnC;AAKD,wBAAgB,WAAW,SAM1B;AAKD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAyB;IAEzC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAuBvE,WAAW,IAAI,eAAe,EAAE;IAIhC,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,gBAAgB;CA6BzB"}