@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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=delete.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/delete.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,162 @@
1
+ import nock from "nock";
2
+ import yargs from "yargs/yargs";
3
+ import deleteCommand from "../../cmds/delete.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 executeDeleteCommand(args) {
6
+ const yargsInstance = yargs([])
7
+ .command(deleteCommand)
8
+ .help(false)
9
+ .version(false)
10
+ .exitProcess(false);
11
+ const commandArgs = ["delete"];
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
+ commandArgs.push("--url", args.url);
22
+ if (args.wait) {
23
+ commandArgs.push("--wait");
24
+ }
25
+ if (args["self-hosted-endpoint"]) {
26
+ commandArgs.push("--self-hosted-endpoint", args["self-hosted-endpoint"]);
27
+ }
28
+ return await yargsInstance.parse(commandArgs);
29
+ }
30
+ const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
31
+ const mockPrintCriticalFailure = jest.mocked(require("../../common/output.js").printCriticalFailureToConsoleAndExit);
32
+ describe("Delete Command Integration Tests", () => {
33
+ let requestCapture;
34
+ beforeEach(() => {
35
+ setupTestEnvironment();
36
+ requestCapture = new RequestCapture();
37
+ nock.disableNetConnect();
38
+ jest.clearAllMocks();
39
+ });
40
+ afterEach(() => {
41
+ cleanupTest();
42
+ requestCapture.clear();
43
+ });
44
+ describe("SaaS version", () => {
45
+ it("should intercept DELETE request to remove deployment", async () => {
46
+ const testUrl = `https://test-deployment.zuplo.app`;
47
+ const deploymentName = "test-deployment";
48
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
49
+ .delete(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`)
50
+ .reply(200, { message: "Deployment deleted successfully" });
51
+ scope.on("request", (req, interceptor, body) => {
52
+ requestCapture.capture(req, interceptor, body);
53
+ });
54
+ await executeDeleteCommand({
55
+ account: TEST_ACCOUNT_NAME,
56
+ project: TEST_PROJECT_NAME,
57
+ "api-key": TEST_AUTH_TOKEN,
58
+ url: testUrl,
59
+ });
60
+ expect(scope.isDone()).toBe(true);
61
+ expect(mockPrintResult).toHaveBeenCalled();
62
+ const capturedRequests = requestCapture.getRequests();
63
+ expect(capturedRequests).toMatchSnapshot("delete-deployment-saas-requests");
64
+ });
65
+ it("should handle deployment not found error", async () => {
66
+ const testUrl = `https://nonexistent-deployment.zuplo.app`;
67
+ const deploymentName = "nonexistent-deployment";
68
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
69
+ .delete(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`)
70
+ .reply(404, { error: "Not Found", message: "Deployment not found" });
71
+ scope.on("request", (req, interceptor, body) => {
72
+ requestCapture.capture(req, interceptor, body);
73
+ });
74
+ await expect(executeDeleteCommand({
75
+ account: TEST_ACCOUNT_NAME,
76
+ project: TEST_PROJECT_NAME,
77
+ "api-key": TEST_AUTH_TOKEN,
78
+ url: testUrl,
79
+ })).rejects.toThrow("Process would exit");
80
+ expect(scope.isDone()).toBe(true);
81
+ const capturedRequests = requestCapture.getRequests();
82
+ expect(capturedRequests).toMatchSnapshot("delete-deployment-not-found-requests");
83
+ });
84
+ it("should handle wait option for polling deletion status", async () => {
85
+ const testUrl = `https://test-deployment.zuplo.app`;
86
+ const deploymentName = "test-deployment";
87
+ const deleteScope = setupAuthenticatedNock(nock(TEST_API_BASE))
88
+ .delete(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`)
89
+ .reply(200, { message: "Deletion initiated" });
90
+ const pollScope = nock("https://test-deployment.zuplo.app")
91
+ .get("/__zuplo/build")
92
+ .reply(200, { status: "alive" })
93
+ .get("/__zuplo/build")
94
+ .reply(404, { error: "Not Found" });
95
+ [deleteScope, pollScope].forEach((scope) => {
96
+ scope.on("request", (req, interceptor, body) => {
97
+ requestCapture.capture(req, interceptor, body);
98
+ });
99
+ });
100
+ await executeDeleteCommand({
101
+ account: TEST_ACCOUNT_NAME,
102
+ project: TEST_PROJECT_NAME,
103
+ "api-key": TEST_AUTH_TOKEN,
104
+ url: testUrl,
105
+ wait: true,
106
+ });
107
+ expect(deleteScope.isDone()).toBe(true);
108
+ expect(pollScope.isDone()).toBe(true);
109
+ const capturedRequests = requestCapture.getRequests();
110
+ expect(capturedRequests).toMatchSnapshot("delete-deployment-wait-requests");
111
+ });
112
+ });
113
+ describe("Self-hosted version", () => {
114
+ it("should intercept DELETE request to self-hosted endpoint", async () => {
115
+ const selfHostedEndpoint = "https://my-zuplo.company.com";
116
+ const testUrl = `https://test-deployment.my-zuplo.company.com`;
117
+ const deploymentName = "test-deployment";
118
+ const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))
119
+ .delete(`/v1/deployments/${deploymentName}`)
120
+ .reply(200, { message: "Deployment deleted successfully" });
121
+ scope.on("request", (req, interceptor, body) => {
122
+ requestCapture.capture(req, interceptor, body);
123
+ });
124
+ await executeDeleteCommand({
125
+ account: TEST_ACCOUNT_NAME,
126
+ project: TEST_PROJECT_NAME,
127
+ "api-key": TEST_AUTH_TOKEN,
128
+ url: testUrl,
129
+ "self-hosted-endpoint": selfHostedEndpoint,
130
+ });
131
+ expect(scope.isDone()).toBe(true);
132
+ expect(mockPrintResult).toHaveBeenCalled();
133
+ const capturedRequests = requestCapture.getRequests();
134
+ expect(capturedRequests).toMatchSnapshot("delete-deployment-self-hosted-requests");
135
+ });
136
+ });
137
+ describe("Request validation", () => {
138
+ it("should include correct headers and method", async () => {
139
+ const testUrl = `https://test-deployment.zuplo.app`;
140
+ const deploymentName = "test-deployment";
141
+ const scope = nock(TEST_API_BASE)
142
+ .delete(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments/${deploymentName}`)
143
+ .matchHeader("authorization", `Bearer ${TEST_AUTH_TOKEN}`)
144
+ .reply(200, { message: "Deployment deleted successfully" });
145
+ scope.on("request", (req, interceptor, body) => {
146
+ requestCapture.capture(req, interceptor, body);
147
+ });
148
+ await executeDeleteCommand({
149
+ account: TEST_ACCOUNT_NAME,
150
+ project: TEST_PROJECT_NAME,
151
+ "api-key": TEST_AUTH_TOKEN,
152
+ url: testUrl,
153
+ });
154
+ expect(scope.isDone()).toBe(true);
155
+ const capturedRequests = requestCapture.getRequests();
156
+ expect(capturedRequests).toMatchSnapshot("delete-deployment-headers-validation");
157
+ const request = capturedRequests[0];
158
+ expect(request.method).toBe("DELETE");
159
+ });
160
+ });
161
+ });
162
+ //# sourceMappingURL=delete.integration.test.js.map
@@ -0,0 +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 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 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"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deploy.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/deploy.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,249 @@
1
+ import nock from "nock";
2
+ import yargs from "yargs/yargs";
3
+ import deployCommand from "../../cmds/deploy.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.mock("../../common/validators/file-system-validator.js", () => ({
6
+ validDeployDirectoryValidator: {
7
+ validate: jest.fn().mockResolvedValue(true),
8
+ },
9
+ }));
10
+ jest.mock("../../deploy/archive.js", () => ({
11
+ archive: jest.fn().mockResolvedValue({
12
+ tarball: "/tmp/test-archive.tar.gz",
13
+ metadata: {
14
+ branch: "main",
15
+ sha: "abc123",
16
+ repoUrl: "https://github.com/user/repo.git",
17
+ },
18
+ }),
19
+ generateMetadata: jest.fn().mockResolvedValue({
20
+ branch: "main",
21
+ sha: "abc123",
22
+ repoUrl: "https://github.com/user/repo.git",
23
+ }),
24
+ }));
25
+ jest.mock("../../deploy/file-upload.js", () => ({
26
+ upload: jest.fn(async ({ uploadUrl, tarballPath, deploymentId, ...payload }) => {
27
+ const response = await fetch(uploadUrl, {
28
+ method: "PUT",
29
+ body: "mock-tarball-content",
30
+ });
31
+ return {
32
+ ok: response.ok,
33
+ status: response.status,
34
+ };
35
+ }),
36
+ }));
37
+ jest.mock("../../deploy/poll-deployment.js", () => ({
38
+ pollDeployment: jest.fn().mockResolvedValue({
39
+ url: "https://test-project-main.zuplo.app",
40
+ logUrl: "https://portal.zuplo.com/logs/deployment-123",
41
+ }),
42
+ pollBuild: jest.fn().mockResolvedValue({
43
+ conditionType: "Complete",
44
+ }),
45
+ }));
46
+ jest.mock("../../link/populate.js", () => ({
47
+ pullSystemConfig: jest.fn().mockResolvedValue(undefined),
48
+ }));
49
+ jest.mock("../../deploy/environments.js", () => ({
50
+ retrieveOrCreateEnvironment: jest.fn().mockResolvedValue({
51
+ name: "development",
52
+ type: "development",
53
+ }),
54
+ UnableToAutoLinkToExistingProject: class extends Error {
55
+ },
56
+ }));
57
+ jest.mock("node:fs", () => ({
58
+ existsSync: jest.fn().mockReturnValue(false),
59
+ readFileSync: jest.fn().mockReturnValue(Buffer.from("mock-tarball-content")),
60
+ writeFileSync: jest.fn(),
61
+ }));
62
+ async function executeDeployCommand(args) {
63
+ const yargsInstance = yargs([])
64
+ .command(deployCommand)
65
+ .help(false)
66
+ .version(false)
67
+ .exitProcess(false);
68
+ const commandArgs = ["deploy"];
69
+ if (args.account) {
70
+ commandArgs.push("--account", args.account);
71
+ }
72
+ if (args.project) {
73
+ commandArgs.push("--project", args.project);
74
+ }
75
+ if (args["api-key"]) {
76
+ commandArgs.push("--api-key", args["api-key"]);
77
+ }
78
+ if (args.environment) {
79
+ commandArgs.push("--environment", args.environment);
80
+ }
81
+ if (args["self-hosted-endpoint"]) {
82
+ commandArgs.push("--self-hosted-endpoint", args["self-hosted-endpoint"]);
83
+ }
84
+ if (args.dir) {
85
+ commandArgs.push("--dir", args.dir);
86
+ }
87
+ return await yargsInstance.parse(commandArgs);
88
+ }
89
+ const mockPrintResult = jest.mocked(require("../../common/output.js").printResultToConsoleAndExitGracefully);
90
+ describe("Deploy Command Integration Tests", () => {
91
+ let requestCapture;
92
+ beforeEach(() => {
93
+ setupTestEnvironment();
94
+ requestCapture = new RequestCapture();
95
+ nock.disableNetConnect();
96
+ jest.clearAllMocks();
97
+ });
98
+ afterEach(() => {
99
+ cleanupTest();
100
+ requestCapture.clear();
101
+ });
102
+ describe("SaaS deployment", () => {
103
+ it("should intercept POST request to get source upload URL", async () => {
104
+ const mockUploadUrlResponse = {
105
+ uploadUrl: "https://storage.example.com/upload/abc123",
106
+ deploymentId: "deployment-456",
107
+ };
108
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
109
+ .post("/v1/deployments/source-url")
110
+ .reply(200, mockUploadUrlResponse);
111
+ const uploadScope = nock("https://storage.example.com")
112
+ .put("/upload/abc123")
113
+ .reply(200, { message: "Upload successful" });
114
+ [scope, uploadScope].forEach((s) => {
115
+ s.on("request", (req, interceptor, body) => {
116
+ requestCapture.capture(req, interceptor, body);
117
+ });
118
+ });
119
+ await executeDeployCommand({
120
+ account: TEST_ACCOUNT_NAME,
121
+ project: TEST_PROJECT_NAME,
122
+ "api-key": TEST_AUTH_TOKEN,
123
+ dir: ".",
124
+ });
125
+ expect(scope.isDone()).toBe(true);
126
+ expect(uploadScope.isDone()).toBe(true);
127
+ expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining("Deployed to https://test-project-main.zuplo.app"), expect.any(Object));
128
+ const capturedRequests = requestCapture.getRequests();
129
+ expect(capturedRequests).toMatchSnapshot("deploy-saas-requests");
130
+ const sourceUrlRequest = capturedRequests.find((req) => req.path === "/v1/deployments/source-url");
131
+ expect(sourceUrlRequest?.method).toBe("POST");
132
+ expect(sourceUrlRequest?.body).toEqual({
133
+ accountName: TEST_ACCOUNT_NAME,
134
+ projectName: TEST_PROJECT_NAME,
135
+ environment: "main",
136
+ repositoryUrl: "https://github.com/user/repo.git",
137
+ sha: "abc123",
138
+ });
139
+ });
140
+ it("should handle upload URL request failure", async () => {
141
+ const errorResponse = {
142
+ error: "Forbidden",
143
+ message: "Insufficient permissions",
144
+ statusCode: 403,
145
+ };
146
+ const scope = setupAuthenticatedNock(nock(TEST_API_BASE))
147
+ .post("/v1/deployments/source-url")
148
+ .reply(403, errorResponse);
149
+ scope.on("request", (req, interceptor, body) => {
150
+ requestCapture.capture(req, interceptor, body);
151
+ });
152
+ await expect(executeDeployCommand({
153
+ account: TEST_ACCOUNT_NAME,
154
+ project: TEST_PROJECT_NAME,
155
+ "api-key": TEST_AUTH_TOKEN,
156
+ dir: ".",
157
+ })).rejects.toThrow("Process would exit");
158
+ expect(scope.isDone()).toBe(true);
159
+ const capturedRequests = requestCapture.getRequests();
160
+ expect(capturedRequests).toMatchSnapshot("deploy-saas-error-requests");
161
+ });
162
+ });
163
+ describe("Self-hosted deployment", () => {
164
+ it("should intercept POST request to self-hosted build endpoint", async () => {
165
+ const selfHostedEndpoint = "https://my-zuplo.company.com";
166
+ const mockBuildResponse = {
167
+ buildName: "build-789",
168
+ };
169
+ const buildScope = setupAuthenticatedNock(nock(selfHostedEndpoint))
170
+ .post("/v1/deployments/build")
171
+ .reply(200, mockBuildResponse);
172
+ const deploymentScope = setupAuthenticatedNock(nock(selfHostedEndpoint))
173
+ .get("/v1/deployments/test-project-main")
174
+ .reply(200, {
175
+ deploymentUrl: "https://my-zuplo.company.com/test-project-main",
176
+ });
177
+ [buildScope, deploymentScope].forEach((scope) => {
178
+ scope.on("request", (req, interceptor, body) => {
179
+ requestCapture.capture(req, interceptor, body);
180
+ });
181
+ });
182
+ await executeDeployCommand({
183
+ account: TEST_ACCOUNT_NAME,
184
+ project: TEST_PROJECT_NAME,
185
+ "api-key": TEST_AUTH_TOKEN,
186
+ "self-hosted-endpoint": selfHostedEndpoint,
187
+ dir: ".",
188
+ });
189
+ expect(buildScope.isDone()).toBe(true);
190
+ expect(deploymentScope.isDone()).toBe(true);
191
+ expect(mockPrintResult).toHaveBeenCalledWith(expect.stringContaining("Deployed to https://my-zuplo.company.com/test-project-main"), expect.any(Object));
192
+ const capturedRequests = requestCapture.getRequests();
193
+ expect(capturedRequests).toMatchSnapshot("deploy-self-hosted-requests");
194
+ const buildRequest = capturedRequests.find((req) => req.path === "/v1/deployments/build");
195
+ expect(buildRequest?.method).toBe("POST");
196
+ expect(buildRequest?.body).toBeDefined();
197
+ }, 60000);
198
+ it("should handle self-hosted build failure", async () => {
199
+ const selfHostedEndpoint = "https://my-zuplo.company.com";
200
+ const errorResponse = {
201
+ error: "Bad Request",
202
+ message: "Invalid project configuration",
203
+ statusCode: 400,
204
+ };
205
+ const scope = setupAuthenticatedNock(nock(selfHostedEndpoint))
206
+ .post("/v1/deployments/build")
207
+ .reply(400, errorResponse);
208
+ scope.on("request", (req, interceptor, body) => {
209
+ requestCapture.capture(req, interceptor, body);
210
+ });
211
+ await executeDeployCommand({
212
+ account: TEST_ACCOUNT_NAME,
213
+ project: TEST_PROJECT_NAME,
214
+ "api-key": TEST_AUTH_TOKEN,
215
+ "self-hosted-endpoint": selfHostedEndpoint,
216
+ dir: ".",
217
+ });
218
+ expect(scope.isDone()).toBe(true);
219
+ const capturedRequests = requestCapture.getRequests();
220
+ expect(capturedRequests).toMatchSnapshot("deploy-self-hosted-error-requests");
221
+ }, 60000);
222
+ });
223
+ describe("Request validation", () => {
224
+ it("should include correct headers for SaaS deployment", async () => {
225
+ const mockResponse = {
226
+ uploadUrl: "https://storage.example.com/upload/abc123",
227
+ deploymentId: "deployment-456",
228
+ };
229
+ const scope = nock(TEST_API_BASE)
230
+ .post("/v1/deployments/source-url")
231
+ .matchHeader("authorization", `Bearer ${TEST_AUTH_TOKEN}`)
232
+ .reply(200, mockResponse);
233
+ nock("https://storage.example.com").put("/upload/abc123").reply(200);
234
+ scope.on("request", (req, interceptor, body) => {
235
+ requestCapture.capture(req, interceptor, body);
236
+ });
237
+ await executeDeployCommand({
238
+ account: TEST_ACCOUNT_NAME,
239
+ project: TEST_PROJECT_NAME,
240
+ "api-key": TEST_AUTH_TOKEN,
241
+ dir: ".",
242
+ });
243
+ expect(scope.isDone()).toBe(true);
244
+ const capturedRequests = requestCapture.getRequests();
245
+ expect(capturedRequests).toMatchSnapshot("deploy-headers-validation");
246
+ });
247
+ });
248
+ });
249
+ //# sourceMappingURL=deploy.integration.test.js.map
@@ -0,0 +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 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// 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(\n async ({ uploadUrl, tarballPath, deploymentId, ...payload }) => {\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\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(\"../../link/populate.js\", () => ({\n pullSystemConfig: 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}));\n\n// Mock file system functions\njest.mock(\"node:fs\", () => ({\n existsSync: jest.fn().mockReturnValue(false),\n readFileSync: jest.fn().mockReturnValue(Buffer.from(\"mock-tarball-content\")),\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 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 if (args.environment) {\n commandArgs.push(\"--environment\", args.environment);\n }\n\n if (args[\"self-hosted-endpoint\"]) {\n commandArgs.push(\"--self-hosted-endpoint\", args[\"self-hosted-endpoint\"]);\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);\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\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\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"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jest-mocks-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest-mocks-setup.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/jest-mocks-setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ const TEST_AUTH_TOKEN = "test-auth-token-123";
2
+ jest.mock("../../common/output.js", () => ({
3
+ __esModule: true,
4
+ default: jest.fn((argv) => argv),
5
+ printResultToConsoleAndExitGracefully: jest.fn().mockResolvedValue(undefined),
6
+ printTableToConsoleAndExitGracefully: jest.fn().mockResolvedValue(undefined),
7
+ printResultToConsole: jest.fn().mockResolvedValue(undefined),
8
+ printDiagnosticsToConsole: jest.fn(),
9
+ printWarningToConsole: jest.fn(),
10
+ printSpinnerToConsole: jest.fn().mockReturnValue({ stop: jest.fn() }),
11
+ printCriticalFailureToConsoleAndExit: jest
12
+ .fn()
13
+ .mockRejectedValue(new Error("Process would exit")),
14
+ textOrJson: jest.fn((text) => {
15
+ try {
16
+ return JSON.parse(text);
17
+ }
18
+ catch {
19
+ return text;
20
+ }
21
+ }),
22
+ }));
23
+ jest.mock("../../common/analytics/lib.js", () => ({
24
+ captureEvent: jest.fn().mockResolvedValue(undefined),
25
+ }));
26
+ jest.mock("../../common/middleware/authentication.js", () => ({
27
+ authenticate: jest.fn((argv) => {
28
+ return { ...argv, authToken: TEST_AUTH_TOKEN };
29
+ }),
30
+ }));
31
+ jest.mock("../../common/middleware/user-configuration.js", () => ({
32
+ configure: jest.fn((argv) => argv),
33
+ }));
34
+ jest.mock("../../common/middleware/user-identification.js", () => ({
35
+ identify: jest.fn((argv) => argv),
36
+ }));
37
+ jest.mock("../../common/validators/lib.js", () => ({
38
+ YargsChecker: jest.fn().mockImplementation(() => ({
39
+ check: jest.fn().mockResolvedValue(true),
40
+ })),
41
+ }));
42
+ jest.mock("../../common/handler.js", () => ({
43
+ groupHandler: jest.fn(),
44
+ }));
45
+ jest.mock("../../login/tokens.js", () => ({
46
+ getAuthToken: jest.fn().mockResolvedValue(TEST_AUTH_TOKEN),
47
+ refreshAccessToken: jest.fn().mockResolvedValue({
48
+ access_token: TEST_AUTH_TOKEN,
49
+ id_token: "mock-id-token",
50
+ expires_in: 3600,
51
+ token_type: "Bearer",
52
+ scope: "openid profile email",
53
+ }),
54
+ }));
55
+ jest.mock("../../login/server.js", () => ({
56
+ startAuthServer: jest.fn().mockResolvedValue("mock-auth-code"),
57
+ }));
58
+ export {};
59
+ //# sourceMappingURL=jest-mocks-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest-mocks-setup.js","sourceRoot":"","sources":["../../../src/__tests__/integration/jest-mocks-setup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Jest mocks setup - loaded before all test modules\n * This file contains all the Jest mocks that need to be applied before any modules are imported\n */\n\nconst TEST_AUTH_TOKEN = \"test-auth-token-123\";\n\n// Mock all output functions to prevent actual console output and process.exit calls\njest.mock(\"../../common/output.js\", () => ({\n __esModule: true,\n default: jest.fn((argv: unknown) => argv), // setBlocking middleware function\n printResultToConsoleAndExitGracefully: jest.fn().mockResolvedValue(undefined),\n printTableToConsoleAndExitGracefully: jest.fn().mockResolvedValue(undefined),\n printResultToConsole: jest.fn().mockResolvedValue(undefined),\n printDiagnosticsToConsole: jest.fn(),\n printWarningToConsole: jest.fn(),\n printSpinnerToConsole: jest.fn().mockReturnValue({ stop: jest.fn() }),\n printCriticalFailureToConsoleAndExit: jest\n .fn()\n .mockRejectedValue(new Error(\"Process would exit\")),\n textOrJson: jest.fn((text: string) => {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }),\n}));\n\n// Mock analytics\njest.mock(\"../../common/analytics/lib.js\", () => ({\n captureEvent: jest.fn().mockResolvedValue(undefined),\n}));\n\n// Mock authentication middleware to add authToken\njest.mock(\"../../common/middleware/authentication.js\", () => ({\n authenticate: jest.fn((argv: Record<string, unknown>) => {\n return { ...argv, authToken: TEST_AUTH_TOKEN };\n }),\n}));\n\n// Mock other middleware to pass through arguments\njest.mock(\"../../common/middleware/user-configuration.js\", () => ({\n configure: jest.fn((argv: unknown) => argv),\n}));\n\njest.mock(\"../../common/middleware/user-identification.js\", () => ({\n identify: jest.fn((argv: unknown) => argv),\n}));\n\n// Mock validators to pass through\njest.mock(\"../../common/validators/lib.js\", () => ({\n YargsChecker: jest.fn().mockImplementation(() => ({\n check: jest.fn().mockResolvedValue(true),\n })),\n}));\n\n// Mock group handler for commands with subcommands\njest.mock(\"../../common/handler.js\", () => ({\n groupHandler: jest.fn(),\n}));\n\n// Mock token functions to prevent file system and HTTP calls\njest.mock(\"../../login/tokens.js\", () => ({\n getAuthToken: jest.fn().mockResolvedValue(TEST_AUTH_TOKEN),\n refreshAccessToken: jest.fn().mockResolvedValue({\n access_token: TEST_AUTH_TOKEN,\n id_token: \"mock-id-token\",\n expires_in: 3600,\n token_type: \"Bearer\",\n scope: \"openid profile email\",\n }),\n}));\n\n// Mock login/server module that uses 'open' to prevent browser opening during tests\njest.mock(\"../../login/server.js\", () => ({\n startAuthServer: jest.fn().mockResolvedValue(\"mock-auth-code\"),\n}));\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jest-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest-setup.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/jest-setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ beforeEach(() => {
2
+ const envKeys = Object.keys(process.env).filter((key) => key.startsWith("ZUPLO_"));
3
+ envKeys.forEach((key) => {
4
+ delete process.env[key];
5
+ });
6
+ });
7
+ afterEach(() => {
8
+ jest.clearAllMocks();
9
+ jest.restoreAllMocks();
10
+ });
11
+ export {};
12
+ //# sourceMappingURL=jest-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest-setup.js","sourceRoot":"","sources":["../../../src/__tests__/integration/jest-setup.ts"],"names":[],"mappings":"AAMA,UAAU,CAAC,GAAG,EAAE;IAGd,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACtD,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CACzB,CAAC;IACF,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QAEtB,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IAEb,IAAI,CAAC,aAAa,EAAE,CAAC;IACrB,IAAI,CAAC,eAAe,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC","sourcesContent":["/**\n * Jest setup for integration tests\n */\n/// <reference types=\"jest\" />\n\n// Global test setup\nbeforeEach(() => {\n // Clear all environment variables before each test\n // eslint-disable-next-line node/no-process-env\n const envKeys = Object.keys(process.env).filter((key) =>\n key.startsWith(\"ZUPLO_\")\n );\n envKeys.forEach((key) => {\n // eslint-disable-next-line node/no-process-env\n delete process.env[key];\n });\n});\n\nafterEach(() => {\n // Clear all mocks after each test\n jest.clearAllMocks();\n jest.restoreAllMocks();\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=link.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/link.integration.test.ts"],"names":[],"mappings":""}