@shopify/oxygen-cli 1.12.2-unstable.202309141504.0 → 1.12.2-unstable.202309150831.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,67 +0,0 @@
1
- import { mkdir, appendFile, rmdir, touchFile } from '@shopify/cli-kit/node/fs';
2
- import { beforeAll, afterAll, describe, test, expect } from 'vitest';
3
- import { createTestConfig } from '../utils/test-helper.js';
4
- import { FileType } from './types.js';
5
- import { getUploadFiles } from './get-upload-files.js';
6
-
7
- const randomString = Math.random().toString(36).substring(7);
8
- const rootFolder = `/tmp/${randomString}`;
9
- const testConfig = createTestConfig(rootFolder);
10
- beforeAll(async () => {
11
- await mkdir(rootFolder);
12
- await mkdir(`${rootFolder}/worker`);
13
- appendFile(`${rootFolder}/worker/index.js`, 'console.log("Hello World")');
14
- await mkdir(`${rootFolder}/assets`);
15
- appendFile(`${rootFolder}/assets/image.png`, "foo");
16
- });
17
- afterAll(async () => {
18
- await rmdir(rootFolder, { force: true });
19
- });
20
- describe("GetUploadFiles", () => {
21
- test("GetUploadFiles creates a manifest of files", async () => {
22
- const manifest = await getUploadFiles(testConfig);
23
- const expectedManifest = [
24
- {
25
- filePath: "index.js",
26
- fileSize: 26,
27
- mimeType: "application/javascript",
28
- fileType: FileType.Worker,
29
- fileHash: "87a53c8019cbd3358025a289a46b1d25"
30
- },
31
- {
32
- filePath: "image.png",
33
- fileSize: 3,
34
- mimeType: "image/png",
35
- fileType: FileType.Asset,
36
- fileHash: "acbd18db4cc2f85cedef654fccc4a4d8"
37
- }
38
- ];
39
- expect(manifest).toEqual(expectedManifest);
40
- });
41
- test("GetUploadFiles manifest does not include non-worker files", async () => {
42
- await touchFile(`${rootFolder}/worker/image.jpg`);
43
- await touchFile(`${rootFolder}/worker/worker.ts`);
44
- await touchFile(`${rootFolder}/worker/index.js.map`);
45
- const manifest = await getUploadFiles(testConfig);
46
- const workerFileExist = (fileName) => {
47
- return manifest.some(
48
- (file) => file.filePath === fileName && file.fileType === FileType.Worker
49
- );
50
- };
51
- expect(workerFileExist("image.jpg")).toBe(false);
52
- expect(workerFileExist("worker.ts")).toBe(false);
53
- expect(workerFileExist("index.js.map")).toBe(true);
54
- expect(workerFileExist("index.js")).toBe(true);
55
- });
56
- test("GetUploadFiles manifest does not include source map files in assets", async () => {
57
- const testFilename = "index.js.MaP";
58
- await touchFile(`${rootFolder}/assets/${testFilename}`);
59
- const manifest = await getUploadFiles(testConfig);
60
- const assetSourcemapFileExist = (fileName) => {
61
- return manifest.some(
62
- (file) => file.filePath === fileName && file.fileType === FileType.Asset
63
- );
64
- };
65
- expect(assetSourcemapFileExist(testFilename)).toBe(false);
66
- });
67
- });
@@ -1,2 +0,0 @@
1
-
2
- export { }
@@ -1,92 +0,0 @@
1
- import { vi, describe, beforeEach, it, expect } from 'vitest';
2
- import { fetch } from '@shopify/cli-kit/node/http';
3
- import { Response } from 'node-fetch';
4
- import { stderrLogger } from '../utils/utils.js';
5
- import { createTestConfig } from '../utils/test-helper.js';
6
- import { HealthCheckError } from './types.js';
7
- import { healthCheck } from './health-check.js';
8
-
9
- vi.mock("@shopify/cli-kit/node/http", async () => {
10
- const actual = await vi.importActual("@shopify/cli-kit/node/http");
11
- return {
12
- ...actual,
13
- fetch: vi.fn()
14
- };
15
- });
16
- vi.mock("@shopify/cli-kit/node/output");
17
- const testConfig = createTestConfig("rootFolder");
18
- describe("healthCheck", () => {
19
- let setTimeoutSpy;
20
- const hooks = {
21
- onHealthCheckError: vi.fn(),
22
- onHealthCheckStart: vi.fn(),
23
- onHealthCheckComplete: vi.fn()
24
- };
25
- const url = "http://example.com";
26
- beforeEach(() => {
27
- vi.mocked(fetch).mockReset();
28
- setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((cb, _delay) => {
29
- cb();
30
- return {};
31
- });
32
- });
33
- it("resolves when URL is accessible", async () => {
34
- const response = new Response();
35
- vi.mocked(fetch).mockResolvedValueOnce(response);
36
- await healthCheck({ config: testConfig, url, logger: stderrLogger, hooks });
37
- expect(fetch).toHaveBeenCalledTimes(1);
38
- expect(fetch).toHaveBeenCalledWith(`${url}/.oxygen/deployment`);
39
- expect(hooks.onHealthCheckStart).toBeCalled();
40
- expect(hooks.onHealthCheckComplete).toBeCalled();
41
- });
42
- it("repeats request until URL is accessible with progressive timeout", async () => {
43
- const responseFail = new Response(null, { status: 404 });
44
- const responseSuccess = new Response(null, { status: 200 });
45
- let attempts = 0;
46
- vi.mocked(fetch).mockImplementation(() => {
47
- if (attempts === 30) {
48
- return Promise.resolve(responseSuccess);
49
- }
50
- attempts++;
51
- return Promise.resolve(responseFail);
52
- });
53
- await new Promise((resolve, reject) => {
54
- healthCheck({ config: testConfig, url, logger: stderrLogger, hooks }).then(() => {
55
- expect(fetch).toHaveBeenCalledTimes(attempts + 1);
56
- expect(fetch).toHaveBeenCalledWith(`${url}/.oxygen/deployment`);
57
- expect(setTimeoutSpy).toHaveBeenCalledTimes(attempts);
58
- expect(hooks.onHealthCheckStart).toBeCalled();
59
- expect(hooks.onHealthCheckComplete).toBeCalled();
60
- let expectedDuration = 500;
61
- setTimeoutSpy.mock.calls.forEach(
62
- (call, index) => {
63
- if (index >= 10 && index % 5 === 0) {
64
- expectedDuration += 5e3;
65
- }
66
- expect(call[1]).toBe(expectedDuration);
67
- }
68
- );
69
- resolve();
70
- }).catch((error) => {
71
- reject(error);
72
- });
73
- });
74
- });
75
- it("throws an error after max duration", async () => {
76
- vi.useFakeTimers();
77
- const responseFail = new Response(null, { status: 404 });
78
- vi.mocked(fetch).mockResolvedValue(responseFail);
79
- const healthCheckPromise = healthCheck({
80
- config: testConfig,
81
- url,
82
- logger: stderrLogger,
83
- hooks
84
- });
85
- vi.setSystemTime(Date.now() + testConfig.healthCheckMaxDuration * 1e3);
86
- await expect(healthCheckPromise).rejects.toThrow(HealthCheckError);
87
- expect(fetch).toHaveBeenCalledOnce();
88
- expect(hooks.onHealthCheckStart).toBeCalled();
89
- expect(hooks.onHealthCheckError).toBeCalled();
90
- vi.useRealTimers();
91
- });
92
- });
@@ -1,2 +0,0 @@
1
-
2
- export { }
@@ -1,170 +0,0 @@
1
- import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
2
- import { ciPlatform } from '@shopify/cli-kit/node/context/local';
3
- import { vi, describe, test, expect } from 'vitest';
4
- import { stderrLogger, maxLabelLength } from '../utils/utils.js';
5
- import { createTestConfig } from '../utils/test-helper.js';
6
- import { getMetadata, getEnvironmentInput, createLabels } from './metadata.js';
7
-
8
- vi.mock("@shopify/cli-kit/node/context/local");
9
- vi.mock("@shopify/cli-kit/node/git");
10
- vi.mock("@shopify/cli-kit/node/output");
11
- describe("getMetadata", () => {
12
- test("should return custom metadata from environment", async () => {
13
- vi.mocked(ciPlatform).mockReturnValueOnce({
14
- isCI: true,
15
- name: "circle",
16
- metadata: {
17
- actor: "circle_actor",
18
- commitSha: "circle_sha",
19
- url: "circle_url"
20
- }
21
- });
22
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
23
- const metadataResult = await getMetadata(testConfig, stderrLogger);
24
- expect(metadataResult.actor).toBe("circle_actor");
25
- expect(metadataResult.commitSha).toBe("circle_sha");
26
- expect(metadataResult.name).toBe("circle");
27
- expect(metadataResult.url).toBe("circle_url");
28
- });
29
- test("should return custom metadata when provided in config", async () => {
30
- vi.mocked(ciPlatform).mockReturnValueOnce({
31
- isCI: true,
32
- name: "circle",
33
- metadata: {
34
- actor: "circle_actor",
35
- commitSha: "circle_sha",
36
- url: "circle_url"
37
- }
38
- });
39
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
40
- testConfig.metadata = {
41
- user: "custom_user",
42
- version: "custom_version",
43
- url: "custom_url"
44
- };
45
- const metadataResult = await getMetadata(testConfig, stderrLogger);
46
- expect(metadataResult.actor).toBe("custom_user");
47
- expect(metadataResult.commitSha).toBe("custom_version");
48
- expect(metadataResult.name).toBe("circle");
49
- expect(metadataResult.url).toBe("custom_url");
50
- });
51
- test("should return commit data from latest commit through getLastestGitCommit", async () => {
52
- vi.mocked(ciPlatform).mockReturnValueOnce({
53
- isCI: false
54
- });
55
- vi.mocked(getLatestGitCommit).mockResolvedValueOnce({
56
- author_name: "gh_author",
57
- author_email: "test@github.com",
58
- hash: "gh_hash",
59
- message: "gh_message",
60
- refs: "gh_refs",
61
- date: "gh_date",
62
- body: "gh_body"
63
- });
64
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
65
- const metadataResult = await getMetadata(testConfig, stderrLogger);
66
- expect(metadataResult.actor).toBe("gh_author");
67
- expect(metadataResult.commitSha).toBe("gh_hash");
68
- expect(metadataResult.commitDate).toBe("gh_date");
69
- expect(metadataResult.commitMessage).toBe("gh_message");
70
- expect(metadataResult.name).toBe("none");
71
- expect(metadataResult.url).toBe(void 0);
72
- });
73
- test("should derive branch correctly from commit refs", async () => {
74
- vi.mocked(ciPlatform).mockReturnValue({
75
- isCI: false
76
- });
77
- const testCases = {
78
- "HEAD -> main, origin/main, origin/HEAD": "main",
79
- "HEAD -> dev, origin/dev, origin/HEAD": "dev",
80
- "HEAD -> feature/awesome-feature, origin/feature/awesome-feature, origin/HEAD": "feature/awesome-feature",
81
- "HEAD -> bugfix/123-fix-bug, origin/bugfix/123-fix-bug, origin/HEAD": "bugfix/123-fix-bug",
82
- "HEAD -> release/v1.0.0, origin/release/v1.0.0, origin/HEAD": "release/v1.0.0",
83
- "HEAD -> hotfix/urgent-fix, origin/hotfix/urgent-fix, origin/HEAD": "hotfix/urgent-fix",
84
- "HEAD -> test, origin/test, origin/HEAD": "test",
85
- "HEAD -> my-branch, origin/my-branch, origin/HEAD": "my-branch",
86
- "HEAD -> long-branch-name, origin/long-branch-name, origin/HEAD": "long-branch-name",
87
- "HEAD -> branch_with_underscores, origin/branch_with_underscores, origin/HEAD": "branch_with_underscores",
88
- "HEAD -> branch-with-dashes, origin/branch-with-dashes, origin/HEAD": "branch-with-dashes",
89
- "HEAD -> branch.with.dots, origin/branch.with.dots, origin/HEAD": "branch.with.dots"
90
- };
91
- for (const [refs, expectedBranch] of Object.entries(testCases)) {
92
- vi.mocked(getLatestGitCommit).mockResolvedValueOnce({
93
- author_name: "gh_author",
94
- author_email: "test@github.com",
95
- hash: "gh_hash",
96
- message: "gh_message",
97
- refs,
98
- date: "gh_date",
99
- body: "gh_body"
100
- });
101
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
102
- const metadataResult = await getMetadata(testConfig, stderrLogger);
103
- expect(metadataResult.branch).toBe(expectedBranch);
104
- }
105
- });
106
- });
107
- describe("getEnvironmentInput", () => {
108
- test("should return environment tag from config", () => {
109
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
110
- testConfig.environmentTag = "test_tag";
111
- const metadata = {
112
- branch: "test_branch"
113
- };
114
- const environmentInput = getEnvironmentInput(testConfig, metadata);
115
- expect(environmentInput.tag).toBe("test_tag");
116
- });
117
- test("should return environment tag from metadata", () => {
118
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
119
- delete testConfig.environmentTag;
120
- const metadata = {
121
- branch: "test_branch"
122
- };
123
- const environmentInput = getEnvironmentInput(testConfig, metadata);
124
- expect(environmentInput.tag).toBe("test_branch");
125
- });
126
- test("should return undefined if no environment tag is provided", () => {
127
- const testConfig = createTestConfig("/tmp/deploymentRoot/");
128
- delete testConfig.environmentTag;
129
- const metadata = {};
130
- expect(getEnvironmentInput(testConfig, metadata)).toBe(void 0);
131
- });
132
- });
133
- describe("createLabels", () => {
134
- test("should return labels from metadata", () => {
135
- const metadata = {
136
- name: "circle",
137
- run: "circle_1234",
138
- url: "http://circleci.com/workflow/123",
139
- actor: "circle_actor",
140
- commitSha: "circle_sha",
141
- branch: "main"
142
- };
143
- const labels = [
144
- 'circle-runId="circle_1234"',
145
- 'user="circle_actor"',
146
- 'branch="main"',
147
- 'version="circle_sha"',
148
- 'url="http://circleci.com/workflow/123"'
149
- ];
150
- expect(createLabels(metadata)).toEqual(labels);
151
- });
152
- test("should return labels from metadata only for populated fields", () => {
153
- const metadata = {
154
- name: "circle",
155
- run: "circle_run",
156
- attempt: "1"
157
- };
158
- const labels = ['circle-attempt="1"', 'circle-runId="circle_run"'];
159
- expect(createLabels(metadata)).toEqual(labels);
160
- });
161
- test("should throw when user metadata exceeds maximum length", () => {
162
- const metadata = {
163
- actor: "a".repeat(101),
164
- name: "unknown"
165
- };
166
- expect(() => createLabels(metadata)).toThrow(
167
- `Provided user metadata exceeds maximum length (max ${maxLabelLength} characters).`
168
- );
169
- });
170
- });
@@ -1,2 +0,0 @@
1
-
2
- export { }
@@ -1,224 +0,0 @@
1
- import { Readable } from 'stream';
2
- import { fetch } from '@shopify/cli-kit/node/http';
3
- import { vi, describe, beforeEach, it, expect } from 'vitest';
4
- import { Response } from 'node-fetch';
5
- import { createTestConfig } from '../utils/test-helper.js';
6
- import { stderrLogger, deployDefaults } from '../utils/utils.js';
7
- import { uploadFiles } from './upload-files.js';
8
-
9
- class NamedReadable extends Readable {
10
- name = "dummy";
11
- _read() {
12
- }
13
- }
14
- vi.mock("@shopify/cli-kit/node/output");
15
- vi.mock("@shopify/cli-kit/node/http", async () => {
16
- const actual = await vi.importActual("@shopify/cli-kit/node/http");
17
- return {
18
- ...actual,
19
- fetch: vi.fn()
20
- };
21
- });
22
- vi.mock("@shopify/cli-kit/node/fs", () => {
23
- return {
24
- createFileReadStream: vi.fn(() => {
25
- const readable = new NamedReadable();
26
- readable.push("dummy");
27
- readable.emit("end");
28
- return readable;
29
- })
30
- };
31
- });
32
- const testConfig = createTestConfig("/tmp/deploymentRoot");
33
- let hooks;
34
- describe("UploadFiles", () => {
35
- beforeEach(() => {
36
- vi.mocked(fetch).mockReset();
37
- hooks = {
38
- onUploadFilesComplete: vi.fn(),
39
- onUploadFilesError: vi.fn(),
40
- onUploadFilesStart: vi.fn()
41
- };
42
- });
43
- it("Performs a form upload", async () => {
44
- const response = new Response();
45
- vi.mocked(fetch).mockResolvedValueOnce(response);
46
- const testWorkerUpload = [
47
- {
48
- filePath: "index.js",
49
- fileSize: 1,
50
- uploadUrl: "https://storage.googleapis.com/the-bucket/",
51
- fileType: "WORKER",
52
- parameters: [{ name: "someName", value: "someValue" }]
53
- }
54
- ];
55
- await uploadFiles({
56
- config: testConfig,
57
- targets: testWorkerUpload,
58
- logger: stderrLogger,
59
- hooks
60
- });
61
- expect(vi.mocked(fetch)).toHaveBeenCalledTimes(1);
62
- expect(vi.mocked(fetch)).toHaveBeenCalledWith(
63
- "https://storage.googleapis.com/the-bucket/",
64
- expect.objectContaining({
65
- method: "POST",
66
- body: expect.objectContaining({
67
- _streams: expect.arrayContaining([
68
- expect.stringContaining('name="someName"'),
69
- expect.stringMatching("someValue")
70
- ]),
71
- _valuesToMeasure: expect.arrayContaining([
72
- expect.objectContaining({ name: "dummy" })
73
- ])
74
- })
75
- })
76
- );
77
- expect(hooks.onUploadFilesStart).toBeCalled();
78
- expect(hooks.onUploadFilesComplete).toBeCalled();
79
- });
80
- it("Retries a failed form upload until the max upload attempts then throws", async () => {
81
- vi.mocked(fetch).mockRejectedValue(new Error("some error"));
82
- const testWorkerUpload = [
83
- {
84
- filePath: "index.js",
85
- fileSize: 1,
86
- uploadUrl: "https://storage.googleapis.com/the-bucket/",
87
- fileType: "WORKER",
88
- parameters: [{ name: "someName", value: "someValue" }]
89
- }
90
- ];
91
- await expect(
92
- uploadFiles({
93
- config: testConfig,
94
- targets: testWorkerUpload,
95
- logger: stderrLogger,
96
- hooks
97
- })
98
- ).rejects.toThrow("Failed to upload file index.js");
99
- expect(vi.mocked(fetch)).toHaveBeenCalledTimes(
100
- Number(deployDefaults.maxUploadAttempts) + 1
101
- );
102
- expect(hooks.onUploadFilesError).toBeCalled();
103
- expect(hooks.onUploadFilesComplete).not.toBeCalled();
104
- });
105
- it("Performs a resumable upload", async () => {
106
- const headers = {
107
- "x-guploader-uploadid": "some-id",
108
- location: "https://upload-it-here.com/"
109
- };
110
- const response = new Response(null, { headers });
111
- vi.mocked(fetch).mockResolvedValue(response);
112
- const testWorkerUpload = [
113
- {
114
- filePath: "index.js",
115
- fileSize: 1,
116
- uploadUrl: "https://storage.googleapis.com/the-bucket/",
117
- fileType: "WORKER",
118
- parameters: null
119
- }
120
- ];
121
- await uploadFiles({
122
- config: testConfig,
123
- targets: testWorkerUpload,
124
- logger: stderrLogger,
125
- hooks
126
- });
127
- expect(vi.mocked(fetch)).toHaveBeenCalledTimes(2);
128
- const secondCall = vi.mocked(fetch).mock.calls[1];
129
- expect(secondCall[0]).toBe("https://upload-it-here.com/");
130
- const validateRequestBody = (req) => {
131
- return req.method === "PUT" && req.body instanceof NamedReadable;
132
- };
133
- expect(validateRequestBody(secondCall[1])).toBe(true);
134
- expect(hooks.onUploadFilesStart).toBeCalled();
135
- expect(hooks.onUploadFilesComplete).toBeCalled();
136
- });
137
- it("Resumes a resumable upload", async () => {
138
- console.error = () => {
139
- };
140
- let fetchCounter = 0;
141
- vi.mocked(fetch).mockImplementation(async () => {
142
- fetchCounter++;
143
- if (fetchCounter === 1) {
144
- const headers = {
145
- "x-guploader-uploadid": "some-id",
146
- location: "https://upload-it-here.com/"
147
- };
148
- const response = new Response(null, { headers });
149
- return Promise.resolve(response);
150
- } else if (fetchCounter === 4) {
151
- return Promise.resolve(new Response(null, { status: 200 }));
152
- } else {
153
- return Promise.reject(new Error("some error"));
154
- }
155
- });
156
- const testWorkerUpload = [
157
- {
158
- filePath: "index.js",
159
- fileSize: 1,
160
- uploadUrl: "https://storage.googleapis.com/the-bucket/",
161
- fileType: "WORKER",
162
- parameters: null
163
- }
164
- ];
165
- await uploadFiles({
166
- config: testConfig,
167
- targets: testWorkerUpload,
168
- logger: stderrLogger
169
- });
170
- expect(vi.mocked(fetch)).toHaveBeenCalledTimes(4);
171
- const statusCall = vi.mocked(fetch).mock.calls[2];
172
- expect(statusCall[0]).toBe("https://upload-it-here.com/");
173
- expect(statusCall[1]).toMatchObject({
174
- method: "PUT",
175
- headers: {
176
- "Content-Range": "bytes */1",
177
- "Content-Length": "0"
178
- }
179
- });
180
- });
181
- it("Throws when a resumable upload fails after exhausting attempt count", async () => {
182
- console.error = () => {
183
- };
184
- let fetchCounter = 0;
185
- vi.mocked(fetch).mockImplementation(async () => {
186
- fetchCounter++;
187
- if (fetchCounter === 1) {
188
- const headers = {
189
- "x-guploader-uploadid": "some-id",
190
- location: "https://upload-it-here.com/"
191
- };
192
- const response = new Response(null, { headers });
193
- return Promise.resolve(response);
194
- } else {
195
- return Promise.reject(new Error("some error"));
196
- }
197
- });
198
- const testWorkerUpload = [
199
- {
200
- filePath: "index.js",
201
- fileSize: 1,
202
- uploadUrl: "https://storage.googleapis.com/the-bucket/",
203
- fileType: "WORKER",
204
- parameters: null
205
- }
206
- ];
207
- await expect(
208
- uploadFiles({
209
- config: testConfig,
210
- targets: testWorkerUpload,
211
- logger: stderrLogger,
212
- hooks
213
- })
214
- ).rejects.toThrow(
215
- `Failed to upload file index.js after ${deployDefaults.maxResumabeUploadAttempts} attempts`
216
- );
217
- expect(vi.mocked(fetch)).toHaveBeenCalledTimes(
218
- // for each attempt, we make two calls to fetch, plus the initial attempt makes two calls
219
- Number(deployDefaults.maxResumabeUploadAttempts) * 2 + 2
220
- );
221
- expect(hooks.onUploadFilesError).toBeCalled();
222
- expect(hooks.onUploadFilesComplete).not.toBeCalled();
223
- });
224
- });
@@ -1,2 +0,0 @@
1
-
2
- export { }