@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.
- package/oclif.manifest.json +1 -1
- package/package.json +3 -4
- package/dist/deploy/build-cancel.test.d.ts +0 -2
- package/dist/deploy/build-cancel.test.js +0 -90
- package/dist/deploy/build-initiate.test.d.ts +0 -2
- package/dist/deploy/build-initiate.test.js +0 -93
- package/dist/deploy/build-project.test.d.ts +0 -2
- package/dist/deploy/build-project.test.js +0 -68
- package/dist/deploy/deployment-cancel.test.d.ts +0 -2
- package/dist/deploy/deployment-cancel.test.js +0 -89
- package/dist/deploy/deployment-complete.test.d.ts +0 -2
- package/dist/deploy/deployment-complete.test.js +0 -77
- package/dist/deploy/deployment-initiate.test.d.ts +0 -2
- package/dist/deploy/deployment-initiate.test.js +0 -156
- package/dist/deploy/get-upload-files.test.d.ts +0 -2
- package/dist/deploy/get-upload-files.test.js +0 -67
- package/dist/deploy/health-check.test.d.ts +0 -2
- package/dist/deploy/health-check.test.js +0 -92
- package/dist/deploy/metadata.test.d.ts +0 -2
- package/dist/deploy/metadata.test.js +0 -170
- package/dist/deploy/upload-files.test.d.ts +0 -2
- package/dist/deploy/upload-files.test.js +0 -224
- package/dist/utils/utils.test.d.ts +0 -2
- package/dist/utils/utils.test.js +0 -158
@@ -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,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,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,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
|
-
});
|