@zuplo/cli 7.8.19 → 7.8.21

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 (54) hide show
  1. package/dist/__tests__/integration/delete.integration.test.js +101 -1
  2. package/dist/__tests__/integration/delete.integration.test.js.map +1 -1
  3. package/dist/__tests__/integration/deploy.integration.test.js +117 -1
  4. package/dist/__tests__/integration/deploy.integration.test.js.map +1 -1
  5. package/dist/cmds/delete.d.ts.map +1 -1
  6. package/dist/cmds/delete.js +10 -5
  7. package/dist/cmds/delete.js.map +1 -1
  8. package/dist/cmds/deploy.d.ts.map +1 -1
  9. package/dist/cmds/deploy.js +11 -6
  10. package/dist/cmds/deploy.js.map +1 -1
  11. package/dist/common/middleware/get-environment-param.d.ts +7 -0
  12. package/dist/common/middleware/get-environment-param.d.ts.map +1 -1
  13. package/dist/common/middleware/get-environment-param.js +16 -2
  14. package/dist/common/middleware/get-environment-param.js.map +1 -1
  15. package/dist/common/middleware/get-environment-param.test.js +25 -1
  16. package/dist/common/middleware/get-environment-param.test.js.map +1 -1
  17. package/node_modules/@hono/node-server/README.md +21 -10
  18. package/node_modules/@hono/node-server/dist/early-hints.cjs +1 -1
  19. package/node_modules/@hono/node-server/dist/early-hints.mjs +1 -1
  20. package/node_modules/@hono/node-server/dist/index.cjs +41 -19
  21. package/node_modules/@hono/node-server/dist/index.mjs +42 -20
  22. package/node_modules/@hono/node-server/dist/serve-static.cjs +1 -0
  23. package/node_modules/@hono/node-server/dist/serve-static.d.cts +2 -1
  24. package/node_modules/@hono/node-server/dist/serve-static.d.mts +2 -1
  25. package/node_modules/@hono/node-server/dist/serve-static.mjs +1 -0
  26. package/node_modules/@hono/node-server/package.json +3 -2
  27. package/node_modules/@posthog/types/dist/posthog.d.ts +14 -0
  28. package/node_modules/@posthog/types/dist/posthog.d.ts.map +1 -1
  29. package/node_modules/@posthog/types/package.json +1 -1
  30. package/node_modules/@posthog/types/src/posthog.ts +14 -0
  31. package/node_modules/@zuplo/core/customer.cli.minified.js +1 -1
  32. package/node_modules/@zuplo/core/index.minified.js +1 -1
  33. package/node_modules/@zuplo/core/package.json +1 -1
  34. package/node_modules/@zuplo/graphql/package.json +1 -1
  35. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  36. package/node_modules/@zuplo/otel/package.json +1 -1
  37. package/node_modules/@zuplo/runtime/out/esm/{chunk-I5ENAL3B.js → chunk-OABXMALF.js} +97 -97
  38. package/node_modules/@zuplo/runtime/out/esm/chunk-OABXMALF.js.map +1 -0
  39. package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
  40. package/node_modules/@zuplo/runtime/out/esm/index.js.map +1 -1
  41. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +1 -1
  42. package/node_modules/@zuplo/runtime/out/esm/mocks/index.js +1 -1
  43. package/node_modules/@zuplo/runtime/out/types/index.d.ts +105 -0
  44. package/node_modules/@zuplo/runtime/package.json +1 -1
  45. package/node_modules/hono/dist/cjs/adapter/bun/websocket.js +1 -1
  46. package/node_modules/hono/dist/cjs/middleware/language/language.js +1 -1
  47. package/node_modules/hono/dist/cjs/middleware/serve-static/index.js +1 -0
  48. package/node_modules/hono/dist/middleware/language/language.js +1 -1
  49. package/node_modules/hono/dist/middleware/serve-static/index.js +1 -0
  50. package/node_modules/hono/dist/types/middleware/serve-static/index.d.ts +2 -0
  51. package/node_modules/hono/package.json +1 -1
  52. package/package.json +11 -11
  53. package/node_modules/@zuplo/runtime/out/esm/chunk-I5ENAL3B.js.map +0 -1
  54. /package/node_modules/@zuplo/runtime/out/esm/{chunk-I5ENAL3B.js.LEGAL.txt → chunk-OABXMALF.js.LEGAL.txt} +0 -0
@@ -18,19 +18,26 @@ async function executeDeleteCommand(args) {
18
18
  if (args["api-key"]) {
19
19
  commandArgs.push("--api-key", args["api-key"]);
20
20
  }
21
- commandArgs.push("--url", args.url);
21
+ if (args.url) {
22
+ commandArgs.push("--url", args.url);
23
+ }
24
+ if (args.branch) {
25
+ commandArgs.push("--branch", args.branch);
26
+ }
22
27
  if (args.wait) {
23
28
  commandArgs.push("--wait");
24
29
  }
25
30
  if (args["self-hosted-endpoint"]) {
26
31
  commandArgs.push("--self-hosted-endpoint", args["self-hosted-endpoint"]);
27
32
  }
33
+ process.argv = ["node", "zuplo", ...commandArgs];
28
34
  return await yargsInstance.parse(commandArgs);
29
35
  }
30
36
  const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
31
37
  const mockPrintCriticalFailure = jest.mocked(require("../../common/output.js").printCriticalFailureToConsoleAndExit);
32
38
  describe("Delete Command Integration Tests", () => {
33
39
  let requestCapture;
40
+ const originalArgv = process.argv;
34
41
  beforeEach(() => {
35
42
  setupTestEnvironment();
36
43
  requestCapture = new RequestCapture();
@@ -38,6 +45,8 @@ describe("Delete Command Integration Tests", () => {
38
45
  jest.clearAllMocks();
39
46
  });
40
47
  afterEach(() => {
48
+ process.argv = originalArgv;
49
+ delete process.env.ZUPLO_BRANCH;
41
50
  cleanupTest();
42
51
  requestCapture.clear();
43
52
  });
@@ -110,6 +119,97 @@ describe("Delete Command Integration Tests", () => {
110
119
  expect(capturedRequests).toMatchSnapshot("delete-deployment-wait-requests");
111
120
  });
112
121
  });
122
+ describe("By branch name", () => {
123
+ const deployments = {
124
+ data: [
125
+ {
126
+ name: "my-api-main-abc123",
127
+ branchName: "main",
128
+ environmentType: "PRODUCTION",
129
+ },
130
+ {
131
+ name: "my-api-my-feature-def456",
132
+ branchName: "my-feature",
133
+ environmentType: "PREVIEW",
134
+ },
135
+ ],
136
+ };
137
+ it("should delete the deployment matching --branch", async () => {
138
+ const listScope = setupAuthenticatedNock(nock(TEST_API_BASE))
139
+ .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`)
140
+ .reply(200, deployments);
141
+ const deleteScope = setupAuthenticatedNock(nock(TEST_API_BASE))
142
+ .delete("/v1/deployments/my-api-my-feature-def456")
143
+ .reply(200, {});
144
+ await executeDeleteCommand({
145
+ account: TEST_ACCOUNT_NAME,
146
+ project: TEST_PROJECT_NAME,
147
+ "api-key": TEST_AUTH_TOKEN,
148
+ branch: "my-feature",
149
+ });
150
+ expect(listScope.isDone()).toBe(true);
151
+ expect(deleteScope.isDone()).toBe(true);
152
+ expect(mockPrintResult).toHaveBeenCalledWith('Successfully deleted deployment my-api-my-feature-def456 for branch "my-feature"');
153
+ });
154
+ it("should not delete anything when no branch matches", async () => {
155
+ const listScope = setupAuthenticatedNock(nock(TEST_API_BASE))
156
+ .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`)
157
+ .reply(200, deployments);
158
+ const exit = jest.spyOn(process, "exit").mockImplementation((() => {
159
+ throw new Error("Process would exit");
160
+ }));
161
+ await expect(executeDeleteCommand({
162
+ account: TEST_ACCOUNT_NAME,
163
+ project: TEST_PROJECT_NAME,
164
+ "api-key": TEST_AUTH_TOKEN,
165
+ branch: "does-not-exist",
166
+ })).rejects.toThrow("Process would exit");
167
+ expect(listScope.isDone()).toBe(true);
168
+ expect(exit).toHaveBeenCalledWith(0);
169
+ });
170
+ it("should reject --url together with --branch", async () => {
171
+ await expect(executeDeleteCommand({
172
+ account: TEST_ACCOUNT_NAME,
173
+ project: TEST_PROJECT_NAME,
174
+ "api-key": TEST_AUTH_TOKEN,
175
+ url: "https://test-deployment.zuplo.app",
176
+ branch: "my-feature",
177
+ })).rejects.toThrow("Cannot specify both --url and --branch");
178
+ });
179
+ it("should keep rejecting --url when ZUPLO_BRANCH is set", async () => {
180
+ process.env.ZUPLO_BRANCH = "my-feature";
181
+ process.argv = [
182
+ "node",
183
+ "zuplo",
184
+ "delete",
185
+ "--url",
186
+ "https://test-deployment.zuplo.app",
187
+ ];
188
+ await expect(async () => yargs([])
189
+ .env("ZUPLO")
190
+ .command(deleteCommand)
191
+ .help(false)
192
+ .version(false)
193
+ .exitProcess(false)
194
+ .parse([
195
+ "delete",
196
+ "--api-key",
197
+ TEST_AUTH_TOKEN,
198
+ "--url",
199
+ "https://test-deployment.zuplo.app",
200
+ ])).rejects.toThrow("Cannot specify both --url and --branch");
201
+ });
202
+ it("should not take the branch to delete from ZUPLO_BRANCH", async () => {
203
+ process.env.ZUPLO_BRANCH = "my-feature";
204
+ await expect(async () => yargs([])
205
+ .env("ZUPLO")
206
+ .command(deleteCommand)
207
+ .help(false)
208
+ .version(false)
209
+ .exitProcess(false)
210
+ .parse(["delete", "--api-key", TEST_AUTH_TOKEN])).rejects.toThrow("--url is required");
211
+ });
212
+ });
113
213
  describe("Self-hosted version", () => {
114
214
  it("should intercept DELETE request to self-hosted endpoint", async () => {
115
215
  const selfHostedEndpoint = "https://my-zuplo.company.com";
@@ -1 +1 @@
1
- {"version":3,"file":"delete.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/delete.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the delete command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport deleteCommand from \"../../cmds/delete.js\";\nimport {\n cleanupTest,\n RequestCapture,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\n/**\n * Execute delete command with given arguments using actual yargs command\n */\nasync function executeDeleteCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n url: string;\n wait?: boolean;\n \"self-hosted-endpoint\"?: string;\n}) {\n const yargsInstance = yargs([])\n .command(deleteCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"delete\"];\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 commandArgs.push(\"--url\", args.url);\n\n if (args.wait) {\n commandArgs.push(\"--wait\");\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 mockPrintCriticalFailure = jest.mocked(\n require(\"../../common/output.js\").printCriticalFailureToConsoleAndExit\n);\n\ndescribe(\"Delete 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 DELETE request to remove deployment\", async () => {\n const testUrl = `https://test-deployment.zuplo.app`;\n const deploymentName = \"test-deployment\"; // First part of hostname before first dot\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .reply(200, { message: \"Deployment deleted successfully\" });\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 delete command through yargs\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n });\n\n // Verify the request was made\n expect(scope.isDone()).toBe(true);\n\n // Verify the output function was called\n expect(mockPrintResult).toHaveBeenCalled();\n\n // Snapshot test for captured requests\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-saas-requests\"\n );\n });\n\n it(\"should handle deployment not found error\", async () => {\n const testUrl = `https://nonexistent-deployment.zuplo.app`;\n const deploymentName = \"nonexistent-deployment\";\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .reply(404, { error: \"Not Found\", message: \"Deployment not found\" });\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await expect(\n executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-not-found-requests\"\n );\n });\n\n it(\"should handle wait option for polling deletion status\", async () => {\n const testUrl = `https://test-deployment.zuplo.app`;\n const deploymentName = \"test-deployment\";\n\n // Mock the initial delete request\n const deleteScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .reply(200, { message: \"Deletion initiated\" });\n\n // Mock the polling request to check deployment status\n // First poll returns 200 (still exists), second poll returns 404 (deleted)\n const pollScope = nock(\"https://test-deployment.zuplo.app\")\n .get(\"/__zuplo/build\")\n .reply(200, { status: \"alive\" })\n .get(\"/__zuplo/build\")\n .reply(404, { error: \"Not Found\" });\n\n [deleteScope, pollScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n wait: true,\n });\n\n expect(deleteScope.isDone()).toBe(true);\n expect(pollScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-wait-requests\"\n );\n });\n });\n\n describe(\"Self-hosted version\", () => {\n it(\"should intercept DELETE request to self-hosted endpoint\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const testUrl = `https://test-deployment.my-zuplo.company.com`;\n const deploymentName = \"test-deployment\"; // First part of hostname before first dot\n\n const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .delete(`/v1/deployments/${deploymentName}`)\n .reply(200, { message: \"Deployment deleted successfully\" });\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n });\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintResult).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-self-hosted-requests\"\n );\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers and method\", async () => {\n const testUrl = `https://test-deployment.zuplo.app`;\n const deploymentName = \"test-deployment\";\n\n const scope = nock(TEST_API_BASE)\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, { message: \"Deployment deleted successfully\" });\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n });\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-headers-validation\"\n );\n\n // Verify it's a DELETE request\n const request = capturedRequests[0];\n expect(request.method).toBe(\"DELETE\");\n });\n });\n});\n"]}
1
+ {"version":3,"file":"delete.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/delete.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the delete command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport deleteCommand from \"../../cmds/delete.js\";\nimport {\n cleanupTest,\n RequestCapture,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\n/**\n * Execute delete command with given arguments using actual yargs command\n */\nasync function executeDeleteCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n url?: string;\n branch?: string;\n wait?: boolean;\n \"self-hosted-endpoint\"?: string;\n}) {\n const yargsInstance = yargs([])\n .command(deleteCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"delete\"];\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.url) {\n commandArgs.push(\"--url\", args.url);\n }\n\n if (args.branch) {\n commandArgs.push(\"--branch\", args.branch);\n }\n\n if (args.wait) {\n commandArgs.push(\"--wait\");\n }\n\n if (args[\"self-hosted-endpoint\"]) {\n commandArgs.push(\"--self-hosted-endpoint\", args[\"self-hosted-endpoint\"]);\n }\n\n // The command inspects process.argv to tell flags apart from env vars\n process.argv = [\"node\", \"zuplo\", ...commandArgs];\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(\"Delete Command Integration Tests\", () => {\n let requestCapture: RequestCapture;\n const originalArgv = process.argv;\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 process.argv = originalArgv;\n delete process.env.ZUPLO_BRANCH;\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"SaaS version\", () => {\n it(\"should intercept DELETE request to remove deployment\", async () => {\n const testUrl = `https://test-deployment.zuplo.app`;\n const deploymentName = \"test-deployment\"; // First part of hostname before first dot\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .reply(200, { message: \"Deployment deleted successfully\" });\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 delete command through yargs\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n });\n\n // Verify the request was made\n expect(scope.isDone()).toBe(true);\n\n // Verify the output function was called\n expect(mockPrintResult).toHaveBeenCalled();\n\n // Snapshot test for captured requests\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-saas-requests\"\n );\n });\n\n it(\"should handle deployment not found error\", async () => {\n const testUrl = `https://nonexistent-deployment.zuplo.app`;\n const deploymentName = \"nonexistent-deployment\";\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .reply(404, { error: \"Not Found\", message: \"Deployment not found\" });\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await expect(\n executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-not-found-requests\"\n );\n });\n\n it(\"should handle wait option for polling deletion status\", async () => {\n const testUrl = `https://test-deployment.zuplo.app`;\n const deploymentName = \"test-deployment\";\n\n // Mock the initial delete request\n const deleteScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .reply(200, { message: \"Deletion initiated\" });\n\n // Mock the polling request to check deployment status\n // First poll returns 200 (still exists), second poll returns 404 (deleted)\n const pollScope = nock(\"https://test-deployment.zuplo.app\")\n .get(\"/__zuplo/build\")\n .reply(200, { status: \"alive\" })\n .get(\"/__zuplo/build\")\n .reply(404, { error: \"Not Found\" });\n\n [deleteScope, pollScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n wait: true,\n });\n\n expect(deleteScope.isDone()).toBe(true);\n expect(pollScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-wait-requests\"\n );\n });\n });\n\n describe(\"By branch name\", () => {\n const deployments = {\n data: [\n {\n name: \"my-api-main-abc123\",\n branchName: \"main\",\n environmentType: \"PRODUCTION\",\n },\n {\n name: \"my-api-my-feature-def456\",\n branchName: \"my-feature\",\n environmentType: \"PREVIEW\",\n },\n ],\n };\n\n it(\"should delete the deployment matching --branch\", async () => {\n const listScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, deployments);\n const deleteScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .delete(\"/v1/deployments/my-api-my-feature-def456\")\n .reply(200, {});\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n branch: \"my-feature\",\n });\n\n expect(listScope.isDone()).toBe(true);\n expect(deleteScope.isDone()).toBe(true);\n expect(mockPrintResult).toHaveBeenCalledWith(\n 'Successfully deleted deployment my-api-my-feature-def456 for branch \"my-feature\"'\n );\n });\n\n it(\"should not delete anything when no branch matches\", async () => {\n const listScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, deployments);\n const exit = jest.spyOn(process, \"exit\").mockImplementation((() => {\n throw new Error(\"Process would exit\");\n }) as never);\n\n await expect(\n executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n branch: \"does-not-exist\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(listScope.isDone()).toBe(true);\n expect(exit).toHaveBeenCalledWith(0);\n });\n\n it(\"should reject --url together with --branch\", async () => {\n await expect(\n executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: \"https://test-deployment.zuplo.app\",\n branch: \"my-feature\",\n })\n ).rejects.toThrow(\"Cannot specify both --url and --branch\");\n });\n\n it(\"should keep rejecting --url when ZUPLO_BRANCH is set\", async () => {\n process.env.ZUPLO_BRANCH = \"my-feature\";\n process.argv = [\n \"node\",\n \"zuplo\",\n \"delete\",\n \"--url\",\n \"https://test-deployment.zuplo.app\",\n ];\n\n await expect(async () =>\n yargs([])\n .env(\"ZUPLO\")\n .command(deleteCommand)\n .help(false)\n .version(false)\n .exitProcess(false)\n .parse([\n \"delete\",\n \"--api-key\",\n TEST_AUTH_TOKEN,\n \"--url\",\n \"https://test-deployment.zuplo.app\",\n ])\n ).rejects.toThrow(\"Cannot specify both --url and --branch\");\n });\n\n it(\"should not take the branch to delete from ZUPLO_BRANCH\", async () => {\n process.env.ZUPLO_BRANCH = \"my-feature\";\n\n await expect(async () =>\n yargs([])\n .env(\"ZUPLO\")\n .command(deleteCommand)\n .help(false)\n .version(false)\n .exitProcess(false)\n .parse([\"delete\", \"--api-key\", TEST_AUTH_TOKEN])\n ).rejects.toThrow(\"--url is required\");\n });\n });\n\n describe(\"Self-hosted version\", () => {\n it(\"should intercept DELETE request to self-hosted endpoint\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const testUrl = `https://test-deployment.my-zuplo.company.com`;\n const deploymentName = \"test-deployment\"; // First part of hostname before first dot\n\n const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .delete(`/v1/deployments/${deploymentName}`)\n .reply(200, { message: \"Deployment deleted successfully\" });\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n });\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintResult).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-self-hosted-requests\"\n );\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers and method\", async () => {\n const testUrl = `https://test-deployment.zuplo.app`;\n const deploymentName = \"test-deployment\";\n\n const scope = nock(TEST_API_BASE)\n .delete(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`\n )\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, { message: \"Deployment deleted successfully\" });\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeleteCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n url: testUrl,\n });\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"delete-deployment-headers-validation\"\n );\n\n // Verify it's a DELETE request\n const request = capturedRequests[0];\n expect(request.method).toBe(\"DELETE\");\n });\n });\n});\n"]}
@@ -22,6 +22,12 @@ jest.mock("../../deploy/archive.js", () => ({
22
22
  repoUrl: "https://github.com/user/repo.git",
23
23
  }),
24
24
  }));
25
+ jest.mock("simple-git", () => ({
26
+ simpleGit: () => ({
27
+ checkIsRepo: jest.fn().mockResolvedValue(true),
28
+ status: jest.fn().mockResolvedValue({ current: "git-branch" }),
29
+ }),
30
+ }));
25
31
  jest.mock("../../deploy/file-upload.js", () => ({
26
32
  upload: jest.fn(async ({ uploadUrl }) => {
27
33
  const response = await fetch(uploadUrl, {
@@ -86,7 +92,7 @@ async function executeDeployCommand(args) {
86
92
  if (args["api-key"]) {
87
93
  commandArgs.push("--api-key", args["api-key"]);
88
94
  }
89
- commandArgs.push("--environment", args.environment ?? "main");
95
+ commandArgs.push("--branch", args.environment ?? "main");
90
96
  if (args["self-hosted-endpoint"]) {
91
97
  commandArgs.push("--self-hosted-endpoint", args["self-hosted-endpoint"]);
92
98
  }
@@ -96,6 +102,7 @@ async function executeDeployCommand(args) {
96
102
  if (args.dir) {
97
103
  commandArgs.push("--dir", args.dir);
98
104
  }
105
+ process.argv = ["node", "zuplo", ...commandArgs];
99
106
  return await yargsInstance.parse(commandArgs);
100
107
  }
101
108
  const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
@@ -104,6 +111,7 @@ const mockReaddirSync = jest.mocked(require("node:fs").readdirSync);
104
111
  const mockExistsSync = jest.mocked(require("node:fs").existsSync);
105
112
  describe("Deploy Command Integration Tests", () => {
106
113
  let requestCapture;
114
+ const originalArgv = process.argv;
107
115
  beforeEach(() => {
108
116
  setupTestEnvironment();
109
117
  requestCapture = new RequestCapture();
@@ -122,6 +130,7 @@ describe("Deploy Command Integration Tests", () => {
122
130
  });
123
131
  });
124
132
  afterEach(() => {
133
+ process.argv = originalArgv;
125
134
  cleanupTest();
126
135
  requestCapture.clear();
127
136
  });
@@ -186,6 +195,113 @@ describe("Deploy Command Integration Tests", () => {
186
195
  expect(capturedRequests).toMatchSnapshot("deploy-saas-error-requests");
187
196
  });
188
197
  });
198
+ describe("Branch flag spellings", () => {
199
+ const mockArchive = jest.mocked(require("../../deploy/archive.js").archive);
200
+ const baseArgs = [
201
+ "deploy",
202
+ "--account",
203
+ TEST_ACCOUNT_NAME,
204
+ "--project",
205
+ TEST_PROJECT_NAME,
206
+ "--api-key",
207
+ TEST_AUTH_TOKEN,
208
+ ];
209
+ afterEach(() => {
210
+ delete process.env.ZUPLO_ENVIRONMENT;
211
+ delete process.env.ZUPLO_BRANCH;
212
+ });
213
+ const cases = [
214
+ {
215
+ name: "--environment X",
216
+ flags: ["--environment", "my-feature"],
217
+ env: {},
218
+ expected: "my-feature",
219
+ },
220
+ {
221
+ name: "--environment=X",
222
+ flags: ["--environment=my-feature"],
223
+ env: {},
224
+ expected: "my-feature",
225
+ },
226
+ {
227
+ name: "ZUPLO_ENVIRONMENT",
228
+ flags: [],
229
+ env: { ZUPLO_ENVIRONMENT: "my-feature" },
230
+ expected: "my-feature",
231
+ },
232
+ {
233
+ name: "--branch X",
234
+ flags: ["--branch", "my-feature"],
235
+ env: {},
236
+ expected: "my-feature",
237
+ },
238
+ {
239
+ name: "--branch=X",
240
+ flags: ["--branch=my-feature"],
241
+ env: {},
242
+ expected: "my-feature",
243
+ },
244
+ {
245
+ name: "ZUPLO_BRANCH alone: ignored, falls back to git as before",
246
+ flags: [],
247
+ env: { ZUPLO_BRANCH: "from-branch-var" },
248
+ expected: "git-branch",
249
+ },
250
+ {
251
+ name: "no flag or env var: git branch",
252
+ flags: [],
253
+ env: {},
254
+ expected: "git-branch",
255
+ },
256
+ {
257
+ name: "--branch overrides --environment",
258
+ flags: ["--branch", "my-feature", "--environment", "other"],
259
+ env: {},
260
+ expected: "my-feature",
261
+ },
262
+ {
263
+ name: "--branch overrides ZUPLO_ENVIRONMENT",
264
+ flags: ["--branch", "my-feature"],
265
+ env: { ZUPLO_ENVIRONMENT: "from-env-var" },
266
+ expected: "my-feature",
267
+ },
268
+ {
269
+ name: "a typed flag beats ZUPLO_ENVIRONMENT",
270
+ flags: ["--environment", "my-feature"],
271
+ env: { ZUPLO_ENVIRONMENT: "from-env-var" },
272
+ expected: "my-feature",
273
+ },
274
+ ];
275
+ for (const { name, flags, env, expected } of cases) {
276
+ it(`resolves the branch from ${name}`, async () => {
277
+ Object.assign(process.env, env);
278
+ const args = [...baseArgs, ...flags];
279
+ process.argv = ["node", "zuplo", ...args];
280
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
281
+ .post("/v1/deployments/source-url")
282
+ .reply(200, {
283
+ uploadUrl: "https://storage.example.com/upload/abc123",
284
+ deploymentId: "deployment-456",
285
+ });
286
+ const uploadScope = nock("https://storage.example.com")
287
+ .put("/upload/abc123")
288
+ .reply(200, {});
289
+ await yargs([])
290
+ .env("ZUPLO")
291
+ .command(deployCommand)
292
+ .help(false)
293
+ .version(false)
294
+ .exitProcess(false)
295
+ .parse(args);
296
+ expect(scope.isDone()).toBe(true);
297
+ expect(uploadScope.isDone()).toBe(true);
298
+ expect(mockArchive).toHaveBeenCalledTimes(1);
299
+ expect(mockArchive.mock.calls[0][0]).toMatchObject({
300
+ environment: expected,
301
+ });
302
+ });
303
+ }
304
+ });
189
305
  describe("Self-hosted deployment", () => {
190
306
  it("should intercept POST request to self-hosted build endpoint", async () => {
191
307
  const selfHostedEndpoint = "https://my-zuplo.company.com";
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/deploy.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the deploy command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport deployCommand from \"../../cmds/deploy.js\";\nimport {\n cleanupTest,\n RequestCapture,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\n// Mock file system validator\njest.mock(\"../../common/validators/file-system-validator.js\", () => ({\n validDeployDirectoryValidator: {\n validate: jest.fn().mockResolvedValue(true),\n },\n}));\n\n// Mock archive functionality to return predictable metadata\njest.mock(\"../../deploy/archive.js\", () => ({\n archive: jest.fn().mockResolvedValue({\n tarball: \"/tmp/test-archive.tar.gz\",\n metadata: {\n branch: \"main\",\n sha: \"abc123\",\n repoUrl: \"https://github.com/user/repo.git\",\n },\n }),\n generateMetadata: jest.fn().mockResolvedValue({\n branch: \"main\",\n sha: \"abc123\",\n repoUrl: \"https://github.com/user/repo.git\",\n }),\n}));\n\n// Mock file upload but allow the HTTP call to be intercepted\njest.mock(\"../../deploy/file-upload.js\", () => ({\n upload: jest.fn(async ({ uploadUrl }) => {\n // Make actual HTTP call to allow nock interception\n const response = await fetch(uploadUrl, {\n method: \"PUT\",\n body: \"mock-tarball-content\",\n });\n return {\n ok: response.ok,\n status: response.status,\n };\n }),\n}));\n\n// Mock polling functions to simulate deployment success\njest.mock(\"../../deploy/poll-deployment.js\", () => ({\n pollDeployment: jest.fn().mockResolvedValue({\n url: \"https://test-project-main.zuplo.app\",\n logUrl: \"https://portal.zuplo.com/logs/deployment-123\",\n }),\n pollBuild: jest.fn().mockResolvedValue({\n conditionType: \"Complete\",\n }),\n}));\n\n// Mock link functionality for self-hosted\njest.mock(\"../../common/populate.js\", () => ({\n pullSystemConfig: jest.fn().mockResolvedValue(undefined),\n pullLocalConfig: jest.fn().mockResolvedValue(undefined),\n}));\n\n// Mock environment functions\njest.mock(\"../../deploy/environments.js\", () => ({\n retrieveOrCreateEnvironment: jest.fn().mockResolvedValue({\n name: \"development\",\n type: \"development\",\n }),\n UnableToAutoLinkToExistingProject: class extends Error {},\n // Used by confirmProductionDeploy middleware. Default to false so the\n // test's default (interactive=false) path is preserved; tests that want\n // to assert the prompt path can override this mock.\n isProductionBranchName: jest.fn().mockReturnValue(false),\n}));\n\n// Mock file system functions\njest.mock(\"node:fs\", () => ({\n existsSync: jest.fn().mockReturnValue(false),\n readFileSync: jest.fn((filePath: string) => {\n if (filePath.endsWith(\"tls.crt\")) {\n return \"mock-cert-content\";\n }\n if (filePath.endsWith(\"tls.key\")) {\n return \"mock-key-content\";\n }\n return Buffer.from(\"mock-tarball-content\");\n }),\n readdirSync: jest.fn().mockReturnValue([]),\n writeFileSync: jest.fn(),\n}));\n\n/**\n * Execute deploy command with given arguments using actual yargs command\n */\nasync function executeDeployCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n environment?: string;\n \"self-hosted-endpoint\"?: string;\n \"mtls-certificates-dir\"?: string;\n dir?: string;\n}) {\n const yargsInstance = yargs([])\n .command(deployCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"deploy\"];\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 // Always pass an environment. Without it the `fetchEnvironment` middleware\n // infers one from git, which shells out to the real repository the tests run\n // in — `nock.disableNetConnect()` cannot intercept a git subprocess. On a\n // detached HEAD (how CI checks out tags) that path runs `git fetch --all`\n // against the live remote and fails whenever a tracking ref moved since the\n // clone. The deploy payload's environment comes from the mocked `archive()`\n // metadata, so pinning this here does not change what the tests assert.\n commandArgs.push(\"--environment\", args.environment ?? \"main\");\n\n if (args[\"self-hosted-endpoint\"]) {\n commandArgs.push(\"--self-hosted-endpoint\", args[\"self-hosted-endpoint\"]);\n }\n\n if (args[\"mtls-certificates-dir\"]) {\n commandArgs.push(\"--mtls-certificates-dir\", args[\"mtls-certificates-dir\"]);\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 mockReadFileSync = jest.mocked(require(\"node:fs\").readFileSync);\nconst mockReaddirSync = jest.mocked(require(\"node:fs\").readdirSync);\nconst mockExistsSync = jest.mocked(require(\"node:fs\").existsSync);\n\ndescribe(\"Deploy 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 mockExistsSync.mockReturnValue(false);\n mockReaddirSync.mockReturnValue([]);\n mockReadFileSync.mockImplementation((filePath: string) => {\n if (filePath.endsWith(\"tls.crt\")) {\n return \"mock-cert-content\";\n }\n if (filePath.endsWith(\"tls.key\")) {\n return \"mock-key-content\";\n }\n return Buffer.from(\"mock-tarball-content\");\n });\n });\n\n afterEach(() => {\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"SaaS deployment\", () => {\n it(\"should intercept POST request to get source upload URL\", async () => {\n const mockUploadUrlResponse = {\n uploadUrl: \"https://storage.example.com/upload/abc123\",\n deploymentId: \"deployment-456\",\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, mockUploadUrlResponse);\n\n // Mock the upload to cloud storage\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/abc123\")\n .reply(200, { message: \"Upload successful\" });\n\n [scope, uploadScope].forEach((s) => {\n s.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n expect(uploadScope.isDone()).toBe(true);\n\n // Verify successful deployment message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n \"Deployed to https://test-project-main.zuplo.app\"\n ),\n expect.any(Object) // spinner object\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-saas-requests\");\n\n // Verify the source-url request payload\n const sourceUrlRequest = capturedRequests.find(\n (req) => req.path === \"/v1/deployments/source-url\"\n );\n expect(sourceUrlRequest?.method).toBe(\"POST\");\n expect(sourceUrlRequest?.body).toEqual({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n environment: \"main\",\n repositoryUrl: \"https://github.com/user/repo.git\",\n sha: \"abc123\",\n });\n });\n\n it(\"should handle upload URL request failure\", async () => {\n const errorResponse = {\n error: \"Forbidden\",\n message: \"Insufficient permissions\",\n statusCode: 403,\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(403, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await expect(\n executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-saas-error-requests\");\n });\n });\n\n describe(\"Self-hosted deployment\", () => {\n it(\"should intercept POST request to self-hosted build endpoint\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const mockBuildResponse = {\n buildName: \"build-789\",\n };\n\n const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .post(\"/v1/deployments/build\")\n .reply(200, mockBuildResponse);\n\n // Mock deployment retrieval after build completion\n const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .get(\"/v1/deployments/test-project-main\")\n .reply(200, {\n deploymentUrl: \"https://my-zuplo.company.com/test-project-main\",\n });\n\n [buildScope, deploymentScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n dir: \".\",\n });\n\n expect(buildScope.isDone()).toBe(true);\n expect(deploymentScope.isDone()).toBe(true);\n\n // Verify successful deployment message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n \"Deployed to https://my-zuplo.company.com/test-project-main\"\n ),\n expect.any(Object) // spinner object\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-self-hosted-requests\");\n\n // Verify the build request\n const buildRequest = capturedRequests.find(\n (req) => req.path === \"/v1/deployments/build\"\n );\n expect(buildRequest?.method).toBe(\"POST\");\n // Note: FormData body won't be easily testable in snapshots, but we can verify it exists\n expect(buildRequest?.body).toBeDefined();\n }, 60000); // Increase timeout for this complex test\n\n it(\"should handle self-hosted build failure\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const errorResponse = {\n error: \"Bad Request\",\n message: \"Invalid project configuration\",\n statusCode: 400,\n };\n\n const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .post(\"/v1/deployments/build\")\n .reply(400, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"deploy-self-hosted-error-requests\"\n );\n }, 60000); // Increase timeout for this test too\n\n it(\"should include mtls certificates from mtls-certificates-dir\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const mockBuildResponse = {\n buildName: \"build-789\",\n };\n let capturedBuildRequestBody = \"\";\n\n mockReaddirSync.mockReturnValue([\n {\n name: \"cert-name-2\",\n isDirectory: () => true,\n },\n {\n name: \"cert-name-1\",\n isDirectory: () => true,\n },\n ]);\n mockReadFileSync.mockImplementation((filePath: string) => {\n if (/cert-name-1[\\\\/]tls\\.crt$/.test(filePath)) {\n return \"CERT_1\";\n }\n if (/cert-name-1[\\\\/]tls\\.key$/.test(filePath)) {\n return \"KEY_1\";\n }\n if (/cert-name-2[\\\\/]tls\\.crt$/.test(filePath)) {\n return \"CERT_2\";\n }\n if (/cert-name-2[\\\\/]tls\\.key$/.test(filePath)) {\n return \"KEY_2\";\n }\n return Buffer.from(\"mock-tarball-content\");\n });\n\n const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .post(\"/v1/deployments/build\", (body) => {\n capturedBuildRequestBody = Buffer.isBuffer(body)\n ? body.toString(\"utf-8\")\n : String(body);\n return true;\n })\n .reply(200, mockBuildResponse);\n\n const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .get(\"/v1/deployments/test-project-main\")\n .reply(200, {\n deploymentUrl: \"https://my-zuplo.company.com/test-project-main\",\n });\n\n [buildScope, deploymentScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n \"mtls-certificates-dir\": \"./mtls\",\n dir: \".\",\n });\n\n expect(buildScope.isDone()).toBe(true);\n expect(deploymentScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n const buildRequest = capturedRequests.find(\n (req) => req.path === \"/v1/deployments/build\"\n );\n\n expect(buildRequest?.method).toBe(\"POST\");\n expect(capturedBuildRequestBody).toContain('name=\"mtlsCertificate\"');\n expect(\n (capturedBuildRequestBody.match(/name=\"mtlsCertificate\"/g) || []).length\n ).toBe(2);\n expect(capturedBuildRequestBody).toContain('\"certName\":\"cert-name-1\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsCrt\":\"CERT_1\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsKey\":\"KEY_1\"');\n expect(capturedBuildRequestBody).toContain('\"certName\":\"cert-name-2\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsCrt\":\"CERT_2\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsKey\":\"KEY_2\"');\n\n expect(\n capturedBuildRequestBody.indexOf('\"certName\":\"cert-name-1\"')\n ).toBeLessThan(\n capturedBuildRequestBody.indexOf('\"certName\":\"cert-name-2\"')\n );\n }, 60000);\n });\n\n describe(\"Directory argument handling\", () => {\n it(\"should work when --dir is not specified (uses default)\", async () => {\n const mockUploadUrlResponse = {\n uploadUrl: \"https://storage.example.com/upload/default-test\",\n deploymentId: \"deployment-default\",\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, mockUploadUrlResponse);\n\n // Mock the upload to cloud storage\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/default-test\")\n .reply(200, { message: \"Upload successful\" });\n\n [scope, uploadScope].forEach((s) => {\n s.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Note: Not passing dir argument at all - should default to \".\"\n await executeDeployCommand({\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(uploadScope.isDone()).toBe(true);\n\n // Verify successful deployment message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n \"Deployed to https://test-project-main.zuplo.app\"\n ),\n expect.any(Object)\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-default-dir-requests\");\n });\n\n it(\"should work with explicit --dir=.\", async () => {\n const mockUploadUrlResponse = {\n uploadUrl: \"https://storage.example.com/upload/explicit-dot\",\n deploymentId: \"deployment-dot\",\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, mockUploadUrlResponse);\n\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/explicit-dot\")\n .reply(200, { message: \"Upload successful\" });\n\n [scope, uploadScope].forEach((s) => {\n s.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n expect(uploadScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-explicit-dot-requests\");\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers for SaaS deployment\", async () => {\n const mockResponse = {\n uploadUrl: \"https://storage.example.com/upload/abc123\",\n deploymentId: \"deployment-456\",\n };\n\n const scope = nock(TEST_API_BASE)\n .post(\"/v1/deployments/source-url\")\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockResponse);\n\n // Mock the upload endpoint\n nock(\"https://storage.example.com\").put(\"/upload/abc123\").reply(200);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-headers-validation\");\n });\n });\n});\n"]}
1
+ {"version":3,"file":"deploy.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/deploy.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the deploy command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport deployCommand from \"../../cmds/deploy.js\";\nimport {\n cleanupTest,\n RequestCapture,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\n// Mock file system validator\njest.mock(\"../../common/validators/file-system-validator.js\", () => ({\n validDeployDirectoryValidator: {\n validate: jest.fn().mockResolvedValue(true),\n },\n}));\n\n// Mock archive functionality to return predictable metadata\njest.mock(\"../../deploy/archive.js\", () => ({\n archive: jest.fn().mockResolvedValue({\n tarball: \"/tmp/test-archive.tar.gz\",\n metadata: {\n branch: \"main\",\n sha: \"abc123\",\n repoUrl: \"https://github.com/user/repo.git\",\n },\n }),\n generateMetadata: jest.fn().mockResolvedValue({\n branch: \"main\",\n sha: \"abc123\",\n repoUrl: \"https://github.com/user/repo.git\",\n }),\n}));\n\n// Mock file upload but allow the HTTP call to be intercepted\n// Only reached when no branch is typed. Stands in for the git checkout so the\n// test never shells out to the real repository.\njest.mock(\"simple-git\", () => ({\n simpleGit: () => ({\n checkIsRepo: jest.fn().mockResolvedValue(true),\n status: jest.fn().mockResolvedValue({ current: \"git-branch\" }),\n }),\n}));\n\njest.mock(\"../../deploy/file-upload.js\", () => ({\n upload: jest.fn(async ({ uploadUrl }) => {\n // Make actual HTTP call to allow nock interception\n const response = await fetch(uploadUrl, {\n method: \"PUT\",\n body: \"mock-tarball-content\",\n });\n return {\n ok: response.ok,\n status: response.status,\n };\n }),\n}));\n\n// Mock polling functions to simulate deployment success\njest.mock(\"../../deploy/poll-deployment.js\", () => ({\n pollDeployment: jest.fn().mockResolvedValue({\n url: \"https://test-project-main.zuplo.app\",\n logUrl: \"https://portal.zuplo.com/logs/deployment-123\",\n }),\n pollBuild: jest.fn().mockResolvedValue({\n conditionType: \"Complete\",\n }),\n}));\n\n// Mock link functionality for self-hosted\njest.mock(\"../../common/populate.js\", () => ({\n pullSystemConfig: jest.fn().mockResolvedValue(undefined),\n pullLocalConfig: jest.fn().mockResolvedValue(undefined),\n}));\n\n// Mock environment functions\njest.mock(\"../../deploy/environments.js\", () => ({\n retrieveOrCreateEnvironment: jest.fn().mockResolvedValue({\n name: \"development\",\n type: \"development\",\n }),\n UnableToAutoLinkToExistingProject: class extends Error {},\n // Used by confirmProductionDeploy middleware. Default to false so the\n // test's default (interactive=false) path is preserved; tests that want\n // to assert the prompt path can override this mock.\n isProductionBranchName: jest.fn().mockReturnValue(false),\n}));\n\n// Mock file system functions\njest.mock(\"node:fs\", () => ({\n existsSync: jest.fn().mockReturnValue(false),\n readFileSync: jest.fn((filePath: string) => {\n if (filePath.endsWith(\"tls.crt\")) {\n return \"mock-cert-content\";\n }\n if (filePath.endsWith(\"tls.key\")) {\n return \"mock-key-content\";\n }\n return Buffer.from(\"mock-tarball-content\");\n }),\n readdirSync: jest.fn().mockReturnValue([]),\n writeFileSync: jest.fn(),\n}));\n\n/**\n * Execute deploy command with given arguments using actual yargs command\n */\nasync function executeDeployCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n environment?: string;\n \"self-hosted-endpoint\"?: string;\n \"mtls-certificates-dir\"?: string;\n dir?: string;\n}) {\n const yargsInstance = yargs([])\n .command(deployCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"deploy\"];\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 // Always pass a branch. Without it the `fetchEnvironment` middleware\n // infers one from git, which shells out to the real repository the tests run\n // in — `nock.disableNetConnect()` cannot intercept a git subprocess. On a\n // detached HEAD (how CI checks out tags) that path runs `git fetch --all`\n // against the live remote and fails whenever a tracking ref moved since the\n // clone. The deploy payload's environment comes from the mocked `archive()`\n // metadata, so pinning this here does not change what the tests assert.\n commandArgs.push(\"--branch\", args.environment ?? \"main\");\n\n if (args[\"self-hosted-endpoint\"]) {\n commandArgs.push(\"--self-hosted-endpoint\", args[\"self-hosted-endpoint\"]);\n }\n\n if (args[\"mtls-certificates-dir\"]) {\n commandArgs.push(\"--mtls-certificates-dir\", args[\"mtls-certificates-dir\"]);\n }\n\n if (args.dir) {\n commandArgs.push(\"--dir\", args.dir);\n }\n\n // The command inspects process.argv to tell typed flags from env vars\n process.argv = [\"node\", \"zuplo\", ...commandArgs];\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 mockReadFileSync = jest.mocked(require(\"node:fs\").readFileSync);\nconst mockReaddirSync = jest.mocked(require(\"node:fs\").readdirSync);\nconst mockExistsSync = jest.mocked(require(\"node:fs\").existsSync);\n\ndescribe(\"Deploy Command Integration Tests\", () => {\n let requestCapture: RequestCapture;\n const originalArgv = process.argv;\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 mockExistsSync.mockReturnValue(false);\n mockReaddirSync.mockReturnValue([]);\n mockReadFileSync.mockImplementation((filePath: string) => {\n if (filePath.endsWith(\"tls.crt\")) {\n return \"mock-cert-content\";\n }\n if (filePath.endsWith(\"tls.key\")) {\n return \"mock-key-content\";\n }\n return Buffer.from(\"mock-tarball-content\");\n });\n });\n\n afterEach(() => {\n process.argv = originalArgv;\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"SaaS deployment\", () => {\n it(\"should intercept POST request to get source upload URL\", async () => {\n const mockUploadUrlResponse = {\n uploadUrl: \"https://storage.example.com/upload/abc123\",\n deploymentId: \"deployment-456\",\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, mockUploadUrlResponse);\n\n // Mock the upload to cloud storage\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/abc123\")\n .reply(200, { message: \"Upload successful\" });\n\n [scope, uploadScope].forEach((s) => {\n s.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n expect(uploadScope.isDone()).toBe(true);\n\n // Verify successful deployment message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n \"Deployed to https://test-project-main.zuplo.app\"\n ),\n expect.any(Object) // spinner object\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-saas-requests\");\n\n // Verify the source-url request payload\n const sourceUrlRequest = capturedRequests.find(\n (req) => req.path === \"/v1/deployments/source-url\"\n );\n expect(sourceUrlRequest?.method).toBe(\"POST\");\n expect(sourceUrlRequest?.body).toEqual({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n environment: \"main\",\n repositoryUrl: \"https://github.com/user/repo.git\",\n sha: \"abc123\",\n });\n });\n\n it(\"should handle upload URL request failure\", async () => {\n const errorResponse = {\n error: \"Forbidden\",\n message: \"Insufficient permissions\",\n statusCode: 403,\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(403, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await expect(\n executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-saas-error-requests\");\n });\n });\n\n describe(\"Branch flag spellings\", () => {\n const mockArchive = jest.mocked(require(\"../../deploy/archive.js\").archive);\n const baseArgs = [\n \"deploy\",\n \"--account\",\n TEST_ACCOUNT_NAME,\n \"--project\",\n TEST_PROJECT_NAME,\n \"--api-key\",\n TEST_AUTH_TOKEN,\n ];\n\n afterEach(() => {\n delete process.env.ZUPLO_ENVIRONMENT;\n delete process.env.ZUPLO_BRANCH;\n });\n\n // Every spelling existing scripts use must keep resolving to the same\n // branch; `--branch` must match them.\n const cases: {\n name: string;\n flags: string[];\n env: Record<string, string>;\n expected: string;\n }[] = [\n {\n name: \"--environment X\",\n flags: [\"--environment\", \"my-feature\"],\n env: {},\n expected: \"my-feature\",\n },\n {\n name: \"--environment=X\",\n flags: [\"--environment=my-feature\"],\n env: {},\n expected: \"my-feature\",\n },\n {\n name: \"ZUPLO_ENVIRONMENT\",\n flags: [],\n env: { ZUPLO_ENVIRONMENT: \"my-feature\" },\n expected: \"my-feature\",\n },\n {\n name: \"--branch X\",\n flags: [\"--branch\", \"my-feature\"],\n env: {},\n expected: \"my-feature\",\n },\n {\n name: \"--branch=X\",\n flags: [\"--branch=my-feature\"],\n env: {},\n expected: \"my-feature\",\n },\n {\n name: \"ZUPLO_BRANCH alone: ignored, falls back to git as before\",\n flags: [],\n env: { ZUPLO_BRANCH: \"from-branch-var\" },\n expected: \"git-branch\",\n },\n {\n name: \"no flag or env var: git branch\",\n flags: [],\n env: {},\n expected: \"git-branch\",\n },\n {\n name: \"--branch overrides --environment\",\n flags: [\"--branch\", \"my-feature\", \"--environment\", \"other\"],\n env: {},\n expected: \"my-feature\",\n },\n {\n name: \"--branch overrides ZUPLO_ENVIRONMENT\",\n flags: [\"--branch\", \"my-feature\"],\n env: { ZUPLO_ENVIRONMENT: \"from-env-var\" },\n expected: \"my-feature\",\n },\n {\n name: \"a typed flag beats ZUPLO_ENVIRONMENT\",\n flags: [\"--environment\", \"my-feature\"],\n env: { ZUPLO_ENVIRONMENT: \"from-env-var\" },\n expected: \"my-feature\",\n },\n ];\n\n for (const { name, flags, env, expected } of cases) {\n it(`resolves the branch from ${name}`, async () => {\n Object.assign(process.env, env);\n const args = [...baseArgs, ...flags];\n process.argv = [\"node\", \"zuplo\", ...args];\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, {\n uploadUrl: \"https://storage.example.com/upload/abc123\",\n deploymentId: \"deployment-456\",\n });\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/abc123\")\n .reply(200, {});\n\n await yargs([])\n .env(\"ZUPLO\")\n .command(deployCommand)\n .help(false)\n .version(false)\n .exitProcess(false)\n .parse(args);\n\n expect(scope.isDone()).toBe(true);\n expect(uploadScope.isDone()).toBe(true);\n expect(mockArchive).toHaveBeenCalledTimes(1);\n expect(mockArchive.mock.calls[0][0]).toMatchObject({\n environment: expected,\n });\n });\n }\n });\n\n describe(\"Self-hosted deployment\", () => {\n it(\"should intercept POST request to self-hosted build endpoint\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const mockBuildResponse = {\n buildName: \"build-789\",\n };\n\n const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .post(\"/v1/deployments/build\")\n .reply(200, mockBuildResponse);\n\n // Mock deployment retrieval after build completion\n const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .get(\"/v1/deployments/test-project-main\")\n .reply(200, {\n deploymentUrl: \"https://my-zuplo.company.com/test-project-main\",\n });\n\n [buildScope, deploymentScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n dir: \".\",\n });\n\n expect(buildScope.isDone()).toBe(true);\n expect(deploymentScope.isDone()).toBe(true);\n\n // Verify successful deployment message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n \"Deployed to https://my-zuplo.company.com/test-project-main\"\n ),\n expect.any(Object) // spinner object\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-self-hosted-requests\");\n\n // Verify the build request\n const buildRequest = capturedRequests.find(\n (req) => req.path === \"/v1/deployments/build\"\n );\n expect(buildRequest?.method).toBe(\"POST\");\n // Note: FormData body won't be easily testable in snapshots, but we can verify it exists\n expect(buildRequest?.body).toBeDefined();\n }, 60000); // Increase timeout for this complex test\n\n it(\"should handle self-hosted build failure\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const errorResponse = {\n error: \"Bad Request\",\n message: \"Invalid project configuration\",\n statusCode: 400,\n };\n\n const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .post(\"/v1/deployments/build\")\n .reply(400, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"deploy-self-hosted-error-requests\"\n );\n }, 60000); // Increase timeout for this test too\n\n it(\"should include mtls certificates from mtls-certificates-dir\", async () => {\n const selfHostedEndpoint = \"https://my-zuplo.company.com\";\n const mockBuildResponse = {\n buildName: \"build-789\",\n };\n let capturedBuildRequestBody = \"\";\n\n mockReaddirSync.mockReturnValue([\n {\n name: \"cert-name-2\",\n isDirectory: () => true,\n },\n {\n name: \"cert-name-1\",\n isDirectory: () => true,\n },\n ]);\n mockReadFileSync.mockImplementation((filePath: string) => {\n if (/cert-name-1[\\\\/]tls\\.crt$/.test(filePath)) {\n return \"CERT_1\";\n }\n if (/cert-name-1[\\\\/]tls\\.key$/.test(filePath)) {\n return \"KEY_1\";\n }\n if (/cert-name-2[\\\\/]tls\\.crt$/.test(filePath)) {\n return \"CERT_2\";\n }\n if (/cert-name-2[\\\\/]tls\\.key$/.test(filePath)) {\n return \"KEY_2\";\n }\n return Buffer.from(\"mock-tarball-content\");\n });\n\n const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .post(\"/v1/deployments/build\", (body) => {\n capturedBuildRequestBody = Buffer.isBuffer(body)\n ? body.toString(\"utf-8\")\n : String(body);\n return true;\n })\n .reply(200, mockBuildResponse);\n\n const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))\n .get(\"/v1/deployments/test-project-main\")\n .reply(200, {\n deploymentUrl: \"https://my-zuplo.company.com/test-project-main\",\n });\n\n [buildScope, deploymentScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n \"self-hosted-endpoint\": selfHostedEndpoint,\n \"mtls-certificates-dir\": \"./mtls\",\n dir: \".\",\n });\n\n expect(buildScope.isDone()).toBe(true);\n expect(deploymentScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n const buildRequest = capturedRequests.find(\n (req) => req.path === \"/v1/deployments/build\"\n );\n\n expect(buildRequest?.method).toBe(\"POST\");\n expect(capturedBuildRequestBody).toContain('name=\"mtlsCertificate\"');\n expect(\n (capturedBuildRequestBody.match(/name=\"mtlsCertificate\"/g) || []).length\n ).toBe(2);\n expect(capturedBuildRequestBody).toContain('\"certName\":\"cert-name-1\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsCrt\":\"CERT_1\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsKey\":\"KEY_1\"');\n expect(capturedBuildRequestBody).toContain('\"certName\":\"cert-name-2\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsCrt\":\"CERT_2\"');\n expect(capturedBuildRequestBody).toContain('\"mtlsKey\":\"KEY_2\"');\n\n expect(\n capturedBuildRequestBody.indexOf('\"certName\":\"cert-name-1\"')\n ).toBeLessThan(\n capturedBuildRequestBody.indexOf('\"certName\":\"cert-name-2\"')\n );\n }, 60000);\n });\n\n describe(\"Directory argument handling\", () => {\n it(\"should work when --dir is not specified (uses default)\", async () => {\n const mockUploadUrlResponse = {\n uploadUrl: \"https://storage.example.com/upload/default-test\",\n deploymentId: \"deployment-default\",\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, mockUploadUrlResponse);\n\n // Mock the upload to cloud storage\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/default-test\")\n .reply(200, { message: \"Upload successful\" });\n\n [scope, uploadScope].forEach((s) => {\n s.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Note: Not passing dir argument at all - should default to \".\"\n await executeDeployCommand({\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(uploadScope.isDone()).toBe(true);\n\n // Verify successful deployment message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n \"Deployed to https://test-project-main.zuplo.app\"\n ),\n expect.any(Object)\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-default-dir-requests\");\n });\n\n it(\"should work with explicit --dir=.\", async () => {\n const mockUploadUrlResponse = {\n uploadUrl: \"https://storage.example.com/upload/explicit-dot\",\n deploymentId: \"deployment-dot\",\n };\n\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .post(\"/v1/deployments/source-url\")\n .reply(200, mockUploadUrlResponse);\n\n const uploadScope = nock(\"https://storage.example.com\")\n .put(\"/upload/explicit-dot\")\n .reply(200, { message: \"Upload successful\" });\n\n [scope, uploadScope].forEach((s) => {\n s.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n expect(uploadScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-explicit-dot-requests\");\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers for SaaS deployment\", async () => {\n const mockResponse = {\n uploadUrl: \"https://storage.example.com/upload/abc123\",\n deploymentId: \"deployment-456\",\n };\n\n const scope = nock(TEST_API_BASE)\n .post(\"/v1/deployments/source-url\")\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockResponse);\n\n // Mock the upload endpoint\n nock(\"https://storage.example.com\").put(\"/upload/abc123\").reply(200);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n await executeDeployCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(scope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"deploy-headers-validation\");\n });\n });\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/cmds/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;;;;qBAaV,IAAI,KAAG,IAAI,CAAC,OAAO,CAAC;oBAgEf,OAAO;;AAnE/B,wBAuEE"}
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/cmds/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;;;;qBAcV,IAAI,KAAG,IAAI,CAAC,OAAO,CAAC;oBAsEf,OAAO;;AAzE/B,wBA6EE"}