storebound/tests/forgejo-release.test.ts

78 lines
3.1 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { anchor, changelogSection, isTestTag, mergeSums, pruneList, type Release, sectionSummary, shortNotes } from '../tools/forgejo-release';
const CL = `# Changelog
## Unreleased
Next things.
- a
## 0.2.0 · 2026-10-12
Summary line one
continues here. Second sentence.
- **Bullet.** detail
more detail
## 0.1.0 · 2026-10-10
- First bullet text
wraps here:
- nested
- second
`;
const rel = (id: number, tag: string, draft = false): Release => ({ id, tag_name: tag, name: tag, draft, prerelease: false, body: '', assets: [] });
describe('forgejo release notes', () => {
test('anchor matches Forgejo heading ids', () => {
expect(anchor('0.6.0 · 2026-10-12')).toBe('0-6-0-2026-10-12');
expect(anchor('Unreleased')).toBe('unreleased');
});
test('section and summary', () => {
expect(changelogSection(CL, '0.2.0')!.heading).toBe('0.2.0 · 2026-10-12');
expect(sectionSummary(changelogSection(CL, '0.2.0')!.body)).toBe('Summary line one continues here. Second sentence.');
expect(sectionSummary(changelogSection(CL, '0.1.0')!.body)).toBe('First bullet text wraps here.');
expect(changelogSection(CL, '0.3.0')).toBeNull();
});
test('short notes: summary, install, links; unknown version falls back to Unreleased', () => {
const n = shortNotes(CL, '0.2.0');
expect(n.split('\n')[0]).toBe('Summary line one continues here. Second sentence.');
expect(n).toContain('npx storebound@0.2.0 init');
expect(n).toContain('/src/tag/v0.2.0/CHANGELOG.md#0-2-0-2026-10-12');
expect(shortNotes(CL, '0.3.0')).toContain('CHANGELOG.md#unreleased');
});
test('the real CHANGELOG gives at most 8 short lines for every version', () => {
const real = readFileSync(join(import.meta.dir, '..', 'CHANGELOG.md'), 'utf8');
for (const v of [...real.matchAll(/^## (\d+\.\d+\.\d+)/gm)].map(m => m[1]!)) {
const lines = shortNotes(real, v).split('\n');
expect(lines.length).toBeLessThanOrEqual(8);
expect(lines.every(l => l.length <= 200)).toBe(true);
}
});
});
describe('forgejo release assets and pruning', () => {
test('SHA256SUMS merge: new entries win, sorted', () => {
const a = 'a'.repeat(64), b = 'b'.repeat(64), c = 'c'.repeat(64);
expect(mergeSums(`${a} x.deb\n${a} y.exe\n`, [{ file: 'y.exe', sha256: b }, { file: 'm.dmg', sha256: c }]))
.toBe(`${c} m.dmg\n${a} x.deb\n${b} y.exe\n`);
expect(mergeSums('', [])).toBe('\n');
});
test('prune keeps only the given tag, never when it is missing or a draft', () => {
const rs = [rel(3, 'v0.8.0'), rel(2, 'v0.7.0'), rel(1, 'test-abcdef1', true)];
expect(pruneList(rs, 'v0.8.0').remove.map(r => r.tag_name)).toEqual(['v0.7.0', 'test-abcdef1']);
expect(pruneList(rs, 'v0.9.0').problem).toContain('nothing is deleted');
expect(pruneList([rel(4, 'v1.0.0', true), ...rs], 'v1.0.0').problem).toContain('draft');
});
test('only test-<sha> tags may be deleted', () => {
expect(isTestTag('test-0a1b2c3')).toBe(true);
expect(isTestTag('v0.8.0')).toBe(false);
expect(isTestTag('test-latest')).toBe(false);
});
});