135 lines
5.2 KiB
TypeScript
135 lines
5.2 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import { test } from "node:test"
|
|
import { encodeName, ensureLinked, ensureTag, isPublished, publishBody } from "../scripts/release.ts"
|
|
import { buildEntry, bumpMinor, insertEntry, latestVersion } from "../scripts/version.ts"
|
|
|
|
const CHANGELOG = `# Changelog
|
|
|
|
## [Unreleased]
|
|
|
|
## [1.4.0] - 2026-09-01
|
|
|
|
### Changed
|
|
|
|
- Upstream sync: mattpocock/skills \`aaaaaaaaa\` -> \`bbbbbbbbb\`.
|
|
|
|
## [0.1.0] - 2026-08-01
|
|
`
|
|
|
|
test("latestVersion finds the first release heading", () => {
|
|
assert.equal(latestVersion(CHANGELOG), "1.4.0")
|
|
assert.throws(() => latestVersion("# empty\n"), /no release heading/)
|
|
})
|
|
|
|
test("bumpMinor: minor bump, prerelease untouched", () => {
|
|
assert.equal(bumpMinor("1.4.0"), "1.5.0")
|
|
assert.equal(bumpMinor("0.1.0"), "0.2.0")
|
|
assert.equal(bumpMinor("1.5.0-rc.1"), "1.5.0-rc.1")
|
|
})
|
|
|
|
test("buildEntry: minor bump + upstream diff lines; null when commit already released", () => {
|
|
const entry = buildEntry({
|
|
changelog: CHANGELOG,
|
|
fromCommit: "bbbbbbbbb123",
|
|
toCommit: "ccccccccc456",
|
|
toVersion: "1.4.0",
|
|
date: "2026-10-15",
|
|
diff: { added: ["engineering/new"], removed: [], changed: ["engineering/tdd"] },
|
|
})
|
|
assert.equal(entry?.version, "1.5.0")
|
|
assert.match(entry!.body, /^## \[1\.5\.0\] - 2026-10-15/)
|
|
assert.match(entry!.body, /`bbbbbbbbb` -> `ccccccccc`/)
|
|
assert.match(entry!.body, /added: engineering\/new/)
|
|
assert.match(entry!.body, /changed: engineering\/tdd/)
|
|
|
|
const again = buildEntry({
|
|
changelog: CHANGELOG + entry!.body,
|
|
fromCommit: "b",
|
|
toCommit: "ccccccccc456",
|
|
toVersion: "1.4.0",
|
|
date: "2026-10-15",
|
|
diff: { added: [], removed: [], changed: [] },
|
|
})
|
|
assert.equal(again, null, "idempotent for an already-released upstream commit")
|
|
})
|
|
|
|
test("insertEntry places the new version under Unreleased", () => {
|
|
const entry = { version: "1.5.0", date: "2026-10-15", body: "## [1.5.0] - 2026-10-15" }
|
|
const out = insertEntry(CHANGELOG, entry)
|
|
assert.match(out, /## \[Unreleased\]\n\n## \[1\.5\.0\] - 2026-10-15\n\n## \[1\.4\.0\]/)
|
|
assert.throws(() => insertEntry("# nothing\n", entry), /no `## \[Unreleased\]` heading/)
|
|
})
|
|
|
|
test("publish helpers: name encoding, published check, body hashes", () => {
|
|
assert.equal(encodeName("@haylan/opencode-pocock"), "@haylan%2Fopencode-pocock")
|
|
assert.equal(isPublished({ versions: { "1.0.0": {} } }, "1.0.0"), true)
|
|
assert.equal(isPublished({ versions: {} }, "1.0.0"), false)
|
|
assert.equal(isPublished({}, "1.0.0"), false)
|
|
|
|
const body = publishBody({ name: "@haylan/opencode-pocock", version: "1.0.0" }, Buffer.from("tar")) as {
|
|
"dist-tags": Record<string, string>
|
|
versions: Record<string, { dist: { shasum: string; integrity: string } }>
|
|
_attachments: Record<string, { data: string; length: number }>
|
|
}
|
|
assert.equal(body["dist-tags"].latest, "1.0.0")
|
|
assert.equal(body.versions["1.0.0"].dist.shasum.length, 40)
|
|
assert.match(body.versions["1.0.0"].dist.integrity, /^sha512-/)
|
|
const att = body._attachments["@haylan/opencode-pocock-1.0.0.tgz"]
|
|
assert.equal(att.data, Buffer.from("tar").toString("base64"))
|
|
assert.equal(att.length, 3)
|
|
})
|
|
|
|
function fakeFetch(routes: Record<string, { status: number; body?: unknown }>, seen: string[]) {
|
|
return (async (url: string | URL | Request, init?: RequestInit) => {
|
|
const u = String(url)
|
|
seen.push(`${init?.method ?? "GET"} ${u}`)
|
|
const match = Object.entries(routes).find(([suffix]) => u.endsWith(suffix))
|
|
const route = match?.[1] ?? { status: 404 }
|
|
return new Response(route.body !== undefined ? JSON.stringify(route.body) : null, {
|
|
status: route.status,
|
|
})
|
|
}) as typeof fetch
|
|
}
|
|
|
|
test("ensureTag: creates on 404, no-ops when it exists, fails loudly otherwise", async () => {
|
|
const base = "https://gitea.test/api/v1"
|
|
const creating = {
|
|
base,
|
|
headers: {},
|
|
fetchFn: (async (_url: string | URL | Request, init?: RequestInit) => {
|
|
if ((init?.method ?? "GET") === "GET") return new Response(null, { status: 404 })
|
|
return new Response(null, { status: 201 })
|
|
}) as typeof fetch,
|
|
}
|
|
assert.equal(await ensureTag(creating, "o/r", "1.0.0", "sha123"), "created")
|
|
|
|
const exists = {
|
|
base,
|
|
headers: {},
|
|
fetchFn: (async () => new Response(null, { status: 200 })) as typeof fetch,
|
|
}
|
|
assert.equal(await ensureTag(exists, "o/r", "1.0.0", "sha123"), "exists")
|
|
|
|
const broken = {
|
|
base,
|
|
headers: {},
|
|
fetchFn: (async () => new Response("boom", { status: 500 })) as typeof fetch,
|
|
}
|
|
await assert.rejects(() => ensureTag(broken, "o/r", "1.0.0", "sha123"), /tag lookup failed: 500/)
|
|
})
|
|
|
|
test("ensureLinked: links once, then reports already linked", async () => {
|
|
let linked = false
|
|
const fetchFn = (async (url: string | URL | Request, init?: RequestInit) => {
|
|
const method = init?.method ?? "GET"
|
|
if (method === "GET") {
|
|
return Response.json(linked ? { repository: { id: 1 } } : {}, { status: 200 })
|
|
}
|
|
linked = true
|
|
return new Response(null, { status: 204 })
|
|
}) as typeof fetch
|
|
const api = { base: "https://gitea.test/api/v1", headers: {}, fetchFn }
|
|
assert.equal(await ensureLinked(api, "haylan", "@haylan/opencode-pocock", "0.2.0", "r"), "linked")
|
|
assert.equal(await ensureLinked(api, "haylan", "@haylan/opencode-pocock", "0.2.0", "r"), "already linked")
|
|
})
|