mirror of
https://github.com/VibedByKaKi/t3-code-android-nightly.git
synced 2026-10-09 11:51:15 +02:00
218 lines
7.3 KiB
JavaScript
218 lines
7.3 KiB
JavaScript
const assert = require("node:assert/strict");
|
|
const test = require("node:test");
|
|
const {
|
|
assertCommitOnDefaultBranch,
|
|
assertReleaseSource,
|
|
shouldReleaseNightly,
|
|
} = require("./check-nightly-release.cjs");
|
|
|
|
const now = Date.parse("2026-09-05T12:00:00Z");
|
|
const hour = 60 * 60 * 1000;
|
|
const nightly = (hoursAgo, overrides = {}) => ({
|
|
tag_name: "v1.0.1-nightly.20260905.123",
|
|
draft: false,
|
|
published_at: new Date(now - hoursAgo * hour).toISOString(),
|
|
...overrides,
|
|
});
|
|
|
|
function releaseSourceFixture({
|
|
comparisonStatus = "ahead",
|
|
eventName = "workflow_dispatch",
|
|
ref = "refs/heads/main",
|
|
sha = "candidate",
|
|
} = {}) {
|
|
const calls = [];
|
|
return {
|
|
calls,
|
|
options: {
|
|
context: {
|
|
eventName,
|
|
payload: { repository: { default_branch: "main" } },
|
|
ref,
|
|
repo: { owner: "example", repo: "app" },
|
|
sha,
|
|
},
|
|
github: {
|
|
rest: {
|
|
repos: {
|
|
async compareCommitsWithBasehead(params) {
|
|
calls.push(params);
|
|
return { data: { status: comparisonStatus } };
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
test("allows preview releases from any branch without consulting main", async () => {
|
|
const { options, calls } = releaseSourceFixture({ ref: "refs/heads/feature" });
|
|
await assertReleaseSource({ ...options, releaseChannel: "preview" });
|
|
assert.equal(calls.length, 0);
|
|
});
|
|
|
|
for (const releaseChannel of ["stable", "nightly"]) {
|
|
test(`rejects a manual ${releaseChannel} release from a feature branch`, async () => {
|
|
const { options, calls } = releaseSourceFixture({ ref: "refs/heads/feature" });
|
|
await assert.rejects(
|
|
assertReleaseSource({ ...options, releaseChannel }),
|
|
new RegExp(`${releaseChannel} releases must be dispatched from main`),
|
|
);
|
|
assert.equal(calls.length, 0);
|
|
});
|
|
|
|
test(`allows a manual ${releaseChannel} release from main`, async () => {
|
|
const { options, calls } = releaseSourceFixture();
|
|
await assertReleaseSource({ ...options, releaseChannel });
|
|
assert.equal(calls[0].basehead, "candidate...main");
|
|
});
|
|
}
|
|
|
|
for (const comparisonStatus of ["behind", "diverged"]) {
|
|
test(`rejects a release commit that is ${comparisonStatus} from main`, async () => {
|
|
const { options } = releaseSourceFixture({ comparisonStatus, eventName: "push" });
|
|
await assert.rejects(
|
|
assertCommitOnDefaultBranch({ ...options, sha: "release-commit" }),
|
|
new RegExp(`not contained in main \\(${comparisonStatus}\\)`),
|
|
);
|
|
});
|
|
}
|
|
|
|
test("accepts a release commit already contained in main", async () => {
|
|
for (const comparisonStatus of ["ahead", "identical"]) {
|
|
const { options } = releaseSourceFixture({ comparisonStatus, eventName: "push" });
|
|
await assertCommitOnDefaultBranch({ ...options, sha: "release-commit" });
|
|
}
|
|
});
|
|
|
|
function fixture({ releases = [nightly(7)], comparisonStatus = "ahead" } = {}) {
|
|
const calls = [];
|
|
return {
|
|
calls,
|
|
options: {
|
|
now,
|
|
context: { repo: { owner: "example", repo: "app" }, sha: "new" },
|
|
core: { info() {} },
|
|
github: {
|
|
rest: {
|
|
repos: {
|
|
listReleases() {},
|
|
async compareCommitsWithBasehead(params) {
|
|
calls.push(params);
|
|
return { data: { status: comparisonStatus } };
|
|
},
|
|
},
|
|
},
|
|
async paginate() {
|
|
return releases;
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
test("releases the first nightly when no nightly is published", async () => {
|
|
const { options } = fixture({
|
|
releases: [nightly(0, { tag_name: "v1.0.0" }), nightly(0, { draft: true })],
|
|
});
|
|
assert.equal(await shouldReleaseNightly(options), true);
|
|
});
|
|
|
|
test("waits six hours after publication, including manual nightlies", async () => {
|
|
for (const age of [0, 3, 6 - 1 / 3600]) {
|
|
const { options, calls } = fixture({ releases: [nightly(age)] });
|
|
assert.equal(await shouldReleaseNightly(options), false);
|
|
assert.equal(calls.length, 0);
|
|
}
|
|
});
|
|
|
|
test("releases new commits at six hours and after an idle period", async () => {
|
|
for (const age of [6, 7, 24]) {
|
|
const { options } = fixture({ releases: [nightly(age)] });
|
|
assert.equal(await shouldReleaseNightly(options), true);
|
|
}
|
|
});
|
|
|
|
test("skips unchanged commits after the gap", async () => {
|
|
const { options } = fixture({ comparisonStatus: "identical" });
|
|
assert.equal(await shouldReleaseNightly(options), false);
|
|
});
|
|
|
|
test("uses publication time, not release order or the tagged commit date", async () => {
|
|
const { options } = fixture({
|
|
releases: [nightly(10), nightly(1), nightly(20, { tag_name: "nightly-v0.9.0" })],
|
|
});
|
|
assert.equal(await shouldReleaseNightly(options), false);
|
|
});
|
|
|
|
test("ignores stable releases and drafts when checking the gap", async () => {
|
|
const { options } = fixture({
|
|
releases: [nightly(0, { tag_name: "v1.0.0" }), nightly(0, { draft: true }), nightly(7)],
|
|
});
|
|
assert.equal(await shouldReleaseNightly(options), true);
|
|
});
|
|
|
|
test("compares against the published tag, including legacy nightly tags", async () => {
|
|
const tag = "nightly-v0.9.0";
|
|
const { options, calls } = fixture({ releases: [nightly(7, { tag_name: tag })] });
|
|
assert.equal(await shouldReleaseNightly(options), true);
|
|
assert.equal(calls[0].basehead, `${tag}...new`);
|
|
});
|
|
|
|
test("fails instead of releasing when GitHub cannot supply release state", async () => {
|
|
const { options } = fixture();
|
|
options.github.paginate = async () => {
|
|
throw new Error("GitHub unavailable");
|
|
};
|
|
await assert.rejects(shouldReleaseNightly(options), /GitHub unavailable/);
|
|
});
|
|
|
|
for (const status of ["behind", "diverged"]) {
|
|
test(`skips a candidate commit that is ${status} relative to the last nightly`, async () => {
|
|
const { options } = fixture({ comparisonStatus: status });
|
|
assert.equal(await shouldReleaseNightly(options), false);
|
|
});
|
|
}
|
|
|
|
const { resolveLatestNightlyCommit } = require("./check-nightly-release.cjs");
|
|
|
|
function nightlyCommitFixture({ releases, commitSha = "abc123" }) {
|
|
const refs = [];
|
|
const { options } = fixture({ releases });
|
|
options.github.rest.repos.getCommit = async ({ ref }) => {
|
|
refs.push(ref);
|
|
return { data: { sha: commitSha } };
|
|
};
|
|
return { options, refs };
|
|
}
|
|
|
|
test("stable releases resolve the commit of the newest published nightly", async () => {
|
|
const { options, refs } = nightlyCommitFixture({
|
|
releases: [
|
|
nightly(10, { tag_name: "v1.0.1-nightly.20260905.100" }),
|
|
nightly(1, { tag_name: "v1.0.1-nightly.20260905.123" }),
|
|
nightly(0, { tag_name: "v1.0.0" }),
|
|
nightly(0, { draft: true, tag_name: "v1.0.1-nightly.20260905.999" }),
|
|
],
|
|
commitSha: "deadbeef",
|
|
});
|
|
assert.deepEqual(await resolveLatestNightlyCommit(options), {
|
|
tag: "v1.0.1-nightly.20260905.123",
|
|
sha: "deadbeef",
|
|
version: "1.0.1",
|
|
});
|
|
assert.deepEqual(refs, ["v1.0.1-nightly.20260905.123"]);
|
|
});
|
|
|
|
test("stable releases derive the version from legacy nightly tags", async () => {
|
|
const { options } = nightlyCommitFixture({
|
|
releases: [nightly(1, { tag_name: "nightly-v0.9.0-nightly.20260905.5" })],
|
|
});
|
|
assert.equal((await resolveLatestNightlyCommit(options)).version, "0.9.0");
|
|
});
|
|
|
|
test("stable releases fail without a published nightly", async () => {
|
|
const { options } = nightlyCommitFixture({ releases: [nightly(0, { tag_name: "v1.0.0" })] });
|
|
await assert.rejects(resolveLatestNightlyCommit(options), /No published nightly/);
|
|
});
|