version.test.ts

4.7 KB · raw

1// Covers the version a repo's header shows without a build: what the project declares, by Koh's2// own rule (Koh 3's src/build/version.v), and a build's version over it.34import { describe, expect, test } from "bun:test";5import { writeFile } from "node:fs/promises";6import { join } from "node:path";7import { openBucket } from "../src/bucket/reader.ts";8import { syncAll } from "../src/bucket/sync.ts";9import { declaredVersion } from "../src/describe/version.ts";10import { instance } from "./browser.ts";11import { makeRepos, writeRelease } from "./fixture.ts";1213const from = (files: Record<string, string>) => declaredVersion((path) => files[path] ?? null);1415describe("the declared version", () => {16	test("reads CR, LF and CRLF lines as Koh does", () => {17		for (const ending of ["\r", "\n", "\r\n"]) {18			expect(from({ "v.mod": `Module {${ending}version: '1.2.3'${ending}}` })).toBe("1.2.3");19			expect(20				from({ "app/build.gradle.kts": `android {${ending}versionName = "1.2.3"${ending}}` }),21			).toBe("1.2.3");22		}23	});2425	test("trims only Koh's ASCII whitespace and refuses an invalid first answer", () => {26		expect(from({ VERSION: " \n\t\v\f\r1.2.3 \n\t\v\f\r" })).toBe("1.2.3");27		for (const space of ["\u00a0", "\ufeff"]) {28			expect(29				from({ VERSION: `${space}1.2.3${space}`, "Cargo.toml": 'version = "9.0.0"' }),30			).toBeNull();31			expect(32				from({33					"Info.plist": `<key>CFBundleShortVersionString</key><string>${space}1.2.3${space}</string>`,34					"app/build.gradle.kts": 'versionName = "9.0.0"',35				}),36			).toBeNull();37		}38	});3940	test("is the first source that answers, VERSION first", () => {41		expect(from({ VERSION: " 2.0.1\n", "package.json": '{ "version": "1.0.0" }' })).toBe("2.0.1");42		expect(from({ "package.json": '{\n  "name": "x",\n  "version": "3.0.199",\n}' })).toBe(43			"3.0.199",44		);45		expect(46			from({ "build.zig.zon": '.{\n    .version = "0.14.1",\n}', "Cargo.toml": 'version = "9"' }),47		).toBe("0.14.1");48		expect(from({ "v.mod": "Module {\n\tname: 'koh'\n\tversion: '3.2.27'\n}" })).toBe("3.2.27");49		expect(from({ "Cargo.toml": '[package]\nversion = "0.5.0"' })).toBe("0.5.0");50		expect(51			from({52				"Info.plist": "<key>CFBundleShortVersionString</key>\n\t<string> 1.4 </string>",53			}),54		).toBe("1.4");55	});5657	test("reads Gradle's versionName literally, never a suffix, comment or variable", () => {58		const gradle = (text: string) => from({ "app/build.gradle.kts": text });59		expect(gradle('versionNameSuffix = "-dev"\nversionName = "2.17.43"')).toBe("2.17.43");60		expect(gradle('// versionName = "1.0"\nversionName "5.2.62"')).toBe("5.2.62");61		expect(gradle("versionName = libs.versions.app.get()")).toBeNull();62	});6364	test("skips a source with no literal, and is none when the answer is not a version", () => {65		expect(66			from({ "package.json": '"version": pkg.version', "Cargo.toml": 'version = "1.1"' }),67		).toBe("1.1");68		expect(from({ VERSION: "not a version!", "package.json": '"version": "1.0.0"' })).toBeNull();69		expect(from({})).toBeNull();70	});71});7273test("a repo's header shows its declared version, and a build's over it", async () => {74	const repos = await makeRepos(["app"]);75	await writeFile(76		join(repos.bucketDir, "..", "work", "app", "package.json"),77		'{\n  "name": "app",\n  "version": "1.2.3"\n}\n',78	);79	repos.koh("app", "save", "declare a version");80	repos.koh("app", "offer");81	const inst = await instance(repos.bucketDir);82	inst.kepr.run("UPDATE repos SET public = 1");83	const meta = async () =>84		/<p class="repo-meta">(.*?)<\/p>/85			.exec(await (await inst.app.request("/app")).text())?.[1]86			?.replace(/<[^>]+>/g, "");87	expect(await meta()).toStartWith("v1.2.3 · ");88	const head = JSON.parse(repos.koh("app", "view", "--json")).head_id as string;89	await writeRelease(repos.bucketDir, "app", {90		version: "2.0.0",91		save: head,92		files: [{ platform: "linux-x86_64", name: "app", bytes: new Uint8Array([1]) }],93	});94	await syncAll({95		bucket: openBucket({ kind: "folder", path: repos.bucketDir }),96		kepr: inst.kepr,97		mirrorDir: join(inst.dataDir, "mirror"),98	});99	expect(await meta()).toStartWith("v2.0.0 · ");100});101102test("a BOM in a saved VERSION is preserved and refuses a lower-priority version", async () => {103	const repos = await makeRepos(["app"]);104	const work = join(repos.bucketDir, "..", "work", "app");105	await writeFile(join(work, "VERSION"), "\ufeff1.2.3");106	await writeFile(join(work, "Cargo.toml"), 'version = "9.0.0"');107	repos.koh("app", "save", "declare a version with a BOM");108	repos.koh("app", "offer");109	const inst = await instance(repos.bucketDir);110	inst.kepr.run("UPDATE repos SET public = 1");111	const page = await (await inst.app.request("/app")).text();112	const meta = /<p class="repo-meta">(.*?)<\/p>/.exec(page)?.[1]?.replace(/<[^>]+>/g, "");113	expect(meta).toBeDefined();114	expect(meta).not.toStartWith("v");115});