93 lines
3.6 KiB
TypeScript
93 lines
3.6 KiB
TypeScript
|
|
import { describe, it, expect } from "vitest";
|
||
|
|
import { v2Layout, v2AllocatedMib, V2_BOOT, V2_RANCHER_MIB, V2_LONGHORN_MIB } from "../src/types/layout.js";
|
||
|
|
import { SUPPORTED_ROLES, roleInfo } from "../src/types/state.js";
|
||
|
|
|
||
|
|
/** A 1 TB disk is the smallest we will ever buy. 1e12 bytes in MiB. */
|
||
|
|
const ONE_TB_MIB = Math.floor(1_000_000_000_000 / (1024 * 1024)); // 953674
|
||
|
|
const VG_MIB_ON_1TB = ONE_TB_MIB - V2_BOOT.efiMib - V2_BOOT.bootMib;
|
||
|
|
|
||
|
|
describe("v2 layout fits the smallest disk we will buy", () => {
|
||
|
|
it.each(SUPPORTED_ROLES)("%s fits in a 1 TB disk with headroom left over", (role) => {
|
||
|
|
const used = v2AllocatedMib(role);
|
||
|
|
expect(used).toBeLessThan(VG_MIB_ON_1TB);
|
||
|
|
// The entire point of v2 is to STOP consuming the disk. Demand real slack.
|
||
|
|
const freeMib = VG_MIB_ON_1TB - used;
|
||
|
|
expect(freeMib).toBeGreaterThan(100 * 1024); // >100 GiB unallocated
|
||
|
|
});
|
||
|
|
|
||
|
|
it("the heaviest role still leaves >200 GiB free on 1 TB", () => {
|
||
|
|
const worst = Math.max(...SUPPORTED_ROLES.map((r) => v2AllocatedMib(r)));
|
||
|
|
expect(VG_MIB_ON_1TB - worst).toBeGreaterThan(200 * 1024);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("v2 gates volumes by capability, not role name", () => {
|
||
|
|
it.each(SUPPORTED_ROLES)("%s gets a rancher LV iff it runs k3s", (role) => {
|
||
|
|
const hasRancher = v2Layout(role).some((lv) => lv.name === "rancher");
|
||
|
|
expect(hasRancher).toBe(roleInfo(role).k3sMode !== null);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("labcontroller gets a rancher LV — the v1 bug this schema fixes", () => {
|
||
|
|
const lvs = v2Layout("labcontroller");
|
||
|
|
const rancher = lvs.find((lv) => lv.name === "rancher");
|
||
|
|
expect(rancher).toBeDefined();
|
||
|
|
expect(rancher!.sizeMib).toBe(V2_RANCHER_MIB);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("vanilla gets neither rancher nor longhorn", () => {
|
||
|
|
const names = v2Layout("vanilla").map((lv) => lv.name);
|
||
|
|
expect(names).not.toContain("rancher");
|
||
|
|
expect(names).not.toContain("longhorn");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("a control-plane-only role would get no longhorn LV", () => {
|
||
|
|
for (const role of SUPPORTED_ROLES) {
|
||
|
|
if (roleInfo(role).controlPlaneOnly) {
|
||
|
|
expect(v2Layout(role).map((l) => l.name)).not.toContain("longhorn");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("v2 never over-allocates", () => {
|
||
|
|
it("no volume is grow/unbounded — every size is a concrete number", () => {
|
||
|
|
for (const role of SUPPORTED_ROLES) {
|
||
|
|
for (const lv of v2Layout(role)) {
|
||
|
|
expect(Number.isFinite(lv.sizeMib)).toBe(true);
|
||
|
|
expect(lv.sizeMib).toBeGreaterThan(0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it("longhorn is fixed at 300 GiB, not grown", () => {
|
||
|
|
const lh = v2Layout("worker").find((lv) => lv.name === "longhorn");
|
||
|
|
expect(lh?.sizeMib).toBe(V2_LONGHORN_MIB);
|
||
|
|
expect(V2_LONGHORN_MIB).toBe(300 * 1024);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("rancher is sized for an image store AND local-path PVCs", () => {
|
||
|
|
// A single ROCm vLLM image measured 62 GiB extracted on 2026-09-17.
|
||
|
|
expect(V2_RANCHER_MIB).toBeGreaterThan(200 * 1024);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("Asahi variant", () => {
|
||
|
|
it("omitRoot drops the root LV — the vendor installer already made one", () => {
|
||
|
|
const names = v2Layout("infra", { omitRoot: true }).map((lv) => lv.name);
|
||
|
|
expect(names).not.toContain("root");
|
||
|
|
expect(names).toContain("var");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("omitRoot frees the root allocation and nothing else", () => {
|
||
|
|
const delta = v2AllocatedMib("infra") - v2AllocatedMib("infra", { omitRoot: true });
|
||
|
|
expect(delta).toBe(32768);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("layout is a pure value, safe to render three times", () => {
|
||
|
|
it("mutating a returned spec does not affect the next call", () => {
|
||
|
|
v2Layout("worker")[0]!.sizeMib = 999;
|
||
|
|
expect(v2Layout("worker")[0]!.sizeMib).not.toBe(999);
|
||
|
|
});
|
||
|
|
});
|