import { describe, expect, test } from 'bun:test'; import { MIN_MAJOR, MIN_MINOR, MIN_VERSION_LABEL, meetsFloor, parseVersion } from './version'; // The version floor is the plugin's whole compatibility story: Headscale changed its admin API shape // repeatedly below 0.29, so `meetsFloor` is what stops a server with an incompatible data model being // registered at all. It has no I/O, so the interesting cases are cheap to pin down — and they were not // pinned down at all until now. describe('parseVersion', () => { test('reads major.minor from the shapes a server actually reports', () => { expect(parseVersion('0.29.0')).toEqual({ major: 0, minor: 29 }); expect(parseVersion('v0.29.0')).toEqual({ major: 0, minor: 29 }); expect(parseVersion(' 0.30.1 ')).toEqual({ major: 0, minor: 30 }); expect(parseVersion('1.0')).toEqual({ major: 1, minor: 0 }); }); test("returns null for 'dev', which is what a self-built image reports", () => { // Not an error case. probeVersion turns this into supported:'unknown' rather than a refusal, so that // someone building Headscale from source is not locked out. If this ever returned a version, those // servers would start being REJECTED — the failure would look like a compatibility bug. expect(parseVersion('dev')).toBeNull(); }); test('returns null rather than guessing at non-semver', () => { expect(parseVersion('')).toBeNull(); expect(parseVersion('unstable')).toBeNull(); expect(parseVersion('.29')).toBeNull(); }); }); describe('meetsFloor', () => { test('accepts the floor itself and anything above it', () => { expect(meetsFloor({ major: MIN_MAJOR, minor: MIN_MINOR })).toBe(true); expect(meetsFloor({ major: 0, minor: 30 })).toBe(true); expect(meetsFloor({ major: 1, minor: 0 })).toBe(true); }); test('refuses the releases whose API shape Officer cannot speak', () => { // 0.26 moved identifiers name→numeric, 0.28 collapsed forcedTags/validTags. Supporting these would // mean carrying several incompatible models, which is the cost the floor exists to avoid. expect(meetsFloor({ major: 0, minor: 28 })).toBe(false); expect(meetsFloor({ major: 0, minor: 26 })).toBe(false); expect(meetsFloor({ major: 0, minor: 0 })).toBe(false); }); test('a higher major wins regardless of minor — 1.0 is not below 0.29', () => { // The bug this guards: comparing minor first makes 1.0 (minor 0) fail against a floor of 0.29, so the // first stable Headscale release would be refused by the plugin as too old. expect(meetsFloor({ major: 1, minor: 0 })).toBe(true); }); test('the advertised label agrees with the numeric floor', () => { // Two constants describing one fact. They drift silently otherwise: the label is what users are told // to install, and the numbers are what actually gates them. expect(MIN_VERSION_LABEL).toBe(`${MIN_MAJOR}.${MIN_MINOR}`); }); });