mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
Context: Codex flagged that bun build --compile produces a self-contained binary, and the existing findMigrationsDir() in upgrade.ts:145 walks skills/migrations/v*.md on disk — which fails on a compiled install because the markdown files aren't bundled. The plan's fix is a TS registry: migrations are code, imported directly, visible to both source installs and compiled binaries. - src/commands/migrations/types.ts: shared Migration, OrchestratorOpts, OrchestratorResult types. - src/commands/migrations/index.ts: exports the migrations[] array, getMigration(version), and compareVersions() (semver comparator). The feature_pitch data that lived in the MD file frontmatter now lives here as a code constant on each Migration, so runPostUpgrade's post-upgrade pitch printer can consume it without a filesystem read. - src/commands/migrations/v0_11_0.ts: stub orchestrator + pitch. The full phase implementation lands in Lane C-1; for now the stub throws a clear "not yet implemented" so apply-migrations --list (Lane A-4) can still enumerate the migration. test/migrations-registry.test.ts: 9 tests covering ascending-semver ordering, feature_pitch shape invariants, getMigration lookup, and compareVersions edge cases (equal / newer / older / single-digit across major bumps). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
/**
|
|
* Tests for the TS migration registry (src/commands/migrations/index.ts).
|
|
*
|
|
* The registry replaces filesystem discovery of skills/migrations/*.md so
|
|
* the compiled `gbrain` binary can enumerate migrations without a readdir.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { migrations, getMigration, compareVersions } from '../src/commands/migrations/index.ts';
|
|
|
|
describe('migration registry', () => {
|
|
test('exports a non-empty migrations array', () => {
|
|
expect(migrations.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('every migration has version + featurePitch.headline + orchestrator', () => {
|
|
for (const m of migrations) {
|
|
expect(typeof m.version).toBe('string');
|
|
expect(m.version).toMatch(/^\d+\.\d+\.\d+$/);
|
|
expect(typeof m.featurePitch.headline).toBe('string');
|
|
expect(m.featurePitch.headline.length).toBeGreaterThan(0);
|
|
expect(typeof m.orchestrator).toBe('function');
|
|
}
|
|
});
|
|
|
|
test('migrations are in ascending semver order', () => {
|
|
for (let i = 1; i < migrations.length; i++) {
|
|
expect(compareVersions(migrations[i].version, migrations[i - 1].version)).toBe(1);
|
|
}
|
|
});
|
|
|
|
test('v0.11.0 is present', () => {
|
|
const m = getMigration('0.11.0');
|
|
expect(m).not.toBeNull();
|
|
expect(m!.featurePitch.headline).toContain('Minions');
|
|
});
|
|
|
|
test('getMigration returns null for unknown versions', () => {
|
|
expect(getMigration('99.99.99')).toBeNull();
|
|
expect(getMigration('')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('compareVersions', () => {
|
|
test('equal versions return 0', () => {
|
|
expect(compareVersions('1.2.3', '1.2.3')).toBe(0);
|
|
});
|
|
|
|
test('newer returns 1', () => {
|
|
expect(compareVersions('1.2.4', '1.2.3')).toBe(1);
|
|
expect(compareVersions('1.3.0', '1.2.9')).toBe(1);
|
|
expect(compareVersions('2.0.0', '1.99.99')).toBe(1);
|
|
});
|
|
|
|
test('older returns -1', () => {
|
|
expect(compareVersions('1.2.2', '1.2.3')).toBe(-1);
|
|
expect(compareVersions('0.11.0', '0.11.1')).toBe(-1);
|
|
expect(compareVersions('0.11.0', '0.12.0')).toBe(-1);
|
|
});
|
|
|
|
test('handles single-digit versions', () => {
|
|
expect(compareVersions('9.0.0', '10.0.0')).toBe(-1);
|
|
});
|
|
});
|