Files
KoalaSync/scripts/prepare-release.test.mjs
T
2026-08-25 14:42:24 +02:00

91 lines
3.8 KiB
JavaScript

import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { afterEach, describe, expect, it } from 'vitest';
import { validateReleaseSourceVersion } from './release-preflight.mjs';
import { prepareRelease, replaceExactly } from './prepare-release.mjs';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const temporaryDirectories = [];
const releaseSourcePaths = [
'README.md',
'extension/manifest.base.json',
'package.json',
'package-lock.json',
'shared/constants.js',
'website/llms.txt',
'website/template.html',
'website/version.json'
];
function createReleaseFixture() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'koalasync-prepare-release-'));
temporaryDirectories.push(root);
for (const relativePath of releaseSourcePaths) {
const target = path.join(root, relativePath);
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.copyFileSync(path.join(repoRoot, relativePath), target);
}
return root;
}
function readFixture(root) {
return Object.fromEntries(releaseSourcePaths.map(relativePath => [
relativePath,
fs.readFileSync(path.join(root, relativePath), 'utf8')
]));
}
afterEach(() => {
for (const directory of temporaryDirectories.splice(0)) {
fs.rmSync(directory, { recursive: true, force: true });
}
});
describe('release preparation helpers', () => {
it('replaces one and only one version marker', () => {
expect(replaceExactly('version=3.1.4', /version=\d+\.\d+\.\d+/gu, 'version=3.1.5', 'fixture'))
.toBe('version=3.1.5');
expect(replaceExactly('New v3.1.4 Release!', /New v\d+\.\d+\.\d+ Release!/gu, 'New v3.1.5 Release!', 'README.md release banner'))
.toBe('New v3.1.5 Release!');
expect(() => replaceExactly('none', /version=\d+/gu, 'version=4', 'fixture'))
.toThrow('fixture must contain exactly one release-version marker');
expect(() => replaceExactly('version=1 version=2', /version=\d+/gu, 'version=3', 'fixture'))
.toThrow('fixture must contain exactly one release-version marker');
});
it('updates and validates every release-version source, including both README markers', () => {
const root = createReleaseFixture();
prepareRelease('9.8.7', new Date('2030-04-05T06:07:08Z'), root);
expect(() => validateReleaseSourceVersion('9.8.7', root)).not.toThrow();
expect(fs.readFileSync(path.join(root, 'README.md'), 'utf8')).toContain('Release-v9.8.7-blue');
expect(fs.readFileSync(path.join(root, 'README.md'), 'utf8')).toContain('New v9.8.7 Release!');
expect(JSON.parse(fs.readFileSync(path.join(root, 'website/version.json'), 'utf8')).date)
.toBe('2030-04-05T06:07:08Z');
});
it('is deterministic when repeated with the tag timestamp and does not duplicate markers', () => {
const root = createReleaseFixture();
const timestamp = new Date('2031-02-03T04:05:06Z');
prepareRelease('9.8.7', timestamp, root);
const once = readFixture(root);
prepareRelease('9.8.7', timestamp, root);
expect(readFixture(root)).toEqual(once);
expect(once['README.md'].match(/Release-v9\.8\.7-blue/gu)).toHaveLength(1);
expect(once['README.md'].match(/New v9\.8\.7 Release!/gu)).toHaveLength(1);
});
it('rejects invalid versions before changing release sources', () => {
const root = createReleaseFixture();
const before = readFixture(root);
expect(() => prepareRelease('9.8.7;echo-unsafe', new Date('2030-01-01T00:00:00Z'), root))
.toThrow('vMAJOR.MINOR.PATCH');
expect(readFixture(root)).toEqual(before);
});
});