214 lines
6.6 KiB
JavaScript
214 lines
6.6 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
evaluateStoreGate,
|
|
expectedStoreGateJobs,
|
|
verifyStoreTestGate,
|
|
} from "../../scripts/mobile/verify-store-test-gate.mjs";
|
|
|
|
const sha = "a".repeat(40);
|
|
const successfulJobs = (platform) =>
|
|
expectedStoreGateJobs(platform).map((name, index) => ({
|
|
id: index + 1,
|
|
name,
|
|
run_attempt: 1,
|
|
status: "completed",
|
|
conclusion: "success",
|
|
}));
|
|
|
|
describe("mobile store test gates", () => {
|
|
it("maps Android to every Chromium mobile role shard", () => {
|
|
expect(expectedStoreGateJobs("android")).toEqual([
|
|
"E2E-full-Chromium-mobile-superuser-shard-1-of-2",
|
|
"E2E-full-Chromium-mobile-superuser-shard-2-of-2",
|
|
"E2E-full-Chromium-mobile-admin-shard-1-of-2",
|
|
"E2E-full-Chromium-mobile-admin-shard-2-of-2",
|
|
"E2E-full-Chromium-mobile-customer-shard-1-of-1",
|
|
"E2E-full-Chromium-mobile-subuser-shard-1-of-1",
|
|
]);
|
|
});
|
|
|
|
it("maps Apple to every WebKit mobile role shard", () => {
|
|
expect(expectedStoreGateJobs("apple")).toEqual([
|
|
"E2E-full-WebKit-mobile-superuser-shard-1-of-2",
|
|
"E2E-full-WebKit-mobile-superuser-shard-2-of-2",
|
|
"E2E-full-WebKit-mobile-admin-shard-1-of-2",
|
|
"E2E-full-WebKit-mobile-admin-shard-2-of-2",
|
|
"E2E-full-WebKit-mobile-customer-shard-1-of-1",
|
|
"E2E-full-WebKit-mobile-subuser-shard-1-of-1",
|
|
]);
|
|
});
|
|
|
|
it("rejects a missing, skipped, or failed required slice", () => {
|
|
const jobs = successfulJobs("android");
|
|
jobs.splice(3, 1);
|
|
jobs[3] = { ...jobs[3], conclusion: "failure" };
|
|
|
|
expect(evaluateStoreGate("android", jobs)).toMatchObject({
|
|
passed: false,
|
|
failures: [
|
|
"E2E-full-Chromium-mobile-admin-shard-2-of-2:missing",
|
|
"E2E-full-Chromium-mobile-customer-shard-1-of-1:completed/failure",
|
|
],
|
|
});
|
|
});
|
|
|
|
it("uses the newest execution of a rerun job while carrying over successful jobs from earlier attempts", () => {
|
|
const jobs = successfulJobs("android");
|
|
jobs.push({ ...jobs[0], id: 100, run_attempt: 2 });
|
|
|
|
expect(evaluateStoreGate("android", jobs)).toMatchObject({ passed: true, failures: [] });
|
|
|
|
jobs.push({ ...jobs[0], id: 101, run_attempt: 3, conclusion: "failure" });
|
|
expect(evaluateStoreGate("android", jobs)).toMatchObject({
|
|
passed: false,
|
|
failures: ["E2E-full-Chromium-mobile-superuser-shard-1-of-2:completed/failure"],
|
|
});
|
|
});
|
|
|
|
it("accepts only a completed push run for the exact source SHA", async () => {
|
|
const responses = [
|
|
{
|
|
id: 123,
|
|
name: "Automated Tests",
|
|
head_sha: sha,
|
|
head_branch: "master",
|
|
event: "push",
|
|
status: "completed",
|
|
conclusion: "success",
|
|
},
|
|
{ jobs: successfulJobs("apple") },
|
|
];
|
|
const paths = [];
|
|
const fetchImpl = async (url) => {
|
|
paths.push(new URL(url).pathname + new URL(url).search);
|
|
return {
|
|
ok: true,
|
|
json: async () => responses.shift(),
|
|
};
|
|
};
|
|
|
|
await expect(
|
|
verifyStoreTestGate({
|
|
platform: "apple",
|
|
sourceSha: sha,
|
|
runId: "123",
|
|
defaultBranch: "master",
|
|
repository: "owner/repo",
|
|
token: "test-token",
|
|
fetchImpl,
|
|
})
|
|
).resolves.toMatchObject({ passed: true, runId: 123, sourceSha: sha });
|
|
expect(paths).toEqual([
|
|
"/repos/owner/repo/actions/runs/123",
|
|
"/repos/owner/repo/actions/runs/123/jobs?filter=all&per_page=100&page=1",
|
|
]);
|
|
});
|
|
|
|
it("accepts carried-over successful jobs after a partial rerun", async () => {
|
|
const jobs = successfulJobs("apple");
|
|
jobs.push({ ...jobs[0], id: 100, run_attempt: 2 });
|
|
const responses = [
|
|
{
|
|
id: 123,
|
|
name: "Automated Tests",
|
|
head_sha: sha,
|
|
head_branch: "master",
|
|
event: "push",
|
|
status: "completed",
|
|
conclusion: "success",
|
|
},
|
|
{ jobs },
|
|
];
|
|
const paths = [];
|
|
const fetchImpl = async (url) => {
|
|
paths.push(new URL(url).pathname + new URL(url).search);
|
|
return { ok: true, json: async () => responses.shift() };
|
|
};
|
|
|
|
await expect(
|
|
verifyStoreTestGate({
|
|
platform: "apple",
|
|
sourceSha: sha,
|
|
runId: "123",
|
|
defaultBranch: "master",
|
|
repository: "owner/repo",
|
|
token: "test-token",
|
|
fetchImpl,
|
|
})
|
|
).resolves.toMatchObject({ passed: true });
|
|
expect(paths[1]).toContain("filter=all");
|
|
});
|
|
|
|
it("rejects a completed test run whose overall conclusion failed", async () => {
|
|
const fetchImpl = async () => ({
|
|
ok: true,
|
|
json: async () => ({
|
|
id: 123,
|
|
name: "Automated Tests",
|
|
head_sha: sha,
|
|
head_branch: "master",
|
|
event: "push",
|
|
status: "completed",
|
|
conclusion: "failure",
|
|
}),
|
|
});
|
|
|
|
await expect(
|
|
verifyStoreTestGate({
|
|
platform: "android",
|
|
sourceSha: sha,
|
|
runId: "123",
|
|
defaultBranch: "master",
|
|
repository: "owner/repo",
|
|
token: "test-token",
|
|
fetchImpl,
|
|
})
|
|
).rejects.toThrow("did not succeed (failure)");
|
|
});
|
|
|
|
it("fails closed when the run belongs to another branch", async () => {
|
|
const fetchImpl = async () => ({
|
|
ok: true,
|
|
json: async () => ({
|
|
id: 123,
|
|
name: "Automated Tests",
|
|
head_sha: sha,
|
|
head_branch: "dev",
|
|
event: "push",
|
|
status: "completed",
|
|
conclusion: "success",
|
|
}),
|
|
});
|
|
|
|
await expect(
|
|
verifyStoreTestGate({
|
|
platform: "android",
|
|
sourceSha: sha,
|
|
runId: "123",
|
|
defaultBranch: "master",
|
|
repository: "owner/repo",
|
|
token: "test-token",
|
|
fetchImpl,
|
|
})
|
|
).rejects.toThrow("not a push run for master");
|
|
});
|
|
|
|
it("resolves current master before any manual or tag Play upload", () => {
|
|
const source = readFileSync(join(process.cwd(), ".github/workflows/mobile-artifacts.yml"), "utf8");
|
|
|
|
expect(source).toContain("UPLOAD_TO_PLAY:");
|
|
expect(source).toContain("git/ref/heads/$DEFAULT_BRANCH");
|
|
expect(source).toContain('[[ "$latest_sha" != "$EXPECTED_SHA" && "$UPLOAD_TO_PLAY" == "true" ]]');
|
|
expect(source).toContain('echo "source_sha=$latest_sha" >> "$GITHUB_OUTPUT"');
|
|
expect(source).toContain("STORE_SOURCE_SHA: ${{ steps.release-guard.outputs.source_sha }}");
|
|
expect(source).toContain("Recheck live master before Play upload");
|
|
expect(source).toContain('[[ "$live_master_sha" == "$EXPECTED_SHA" ]]');
|
|
expect(source.indexOf("Recheck live master before Play upload")).toBeLessThan(
|
|
source.indexOf("Upload Android App Bundle to Google Play")
|
|
);
|
|
});
|
|
});
|