Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import fc from "fast-check";
import { CURRENT_SCHEMA_VERSION } from "../src/constants/constants.js";
import { CONTROL_KIND_VALUES } from "../src/types/control-kind.types.js";
import type {
Control,
ControlNode,
Group,
} from "../src/types/control-node.types.js";
import type { ControlSurface } from "../src/types/control-surface.types.js";
import { validateSurface } from "../src/validate/validate.js";
import { walkSurface } from "../src/walk/walk.js";
const idArb = fc.uuidV(7).map((s) => s.toString());
const controlKindArb = fc.constantFrom(...CONTROL_KIND_VALUES);
const controlLeafArb: fc.Arbitrary<Control> = fc.record({
type: fc.constant("control" as const),
id: idArb,
kind: controlKindArb,
transform: fc.record({
position: fc.record({
x: fc.integer({ min: -1000, max: 1000 }),
y: fc.integer({ min: -1000, max: 1000 }),
}),
}),
});
const nodeArb: fc.Arbitrary<ControlNode> = fc.letrec<{
node: ControlNode;
group: Group;
}>((tie) => ({
node: fc.oneof({ depthSize: "small" }, controlLeafArb, tie("group")),
group: fc.record({
type: fc.constant("group" as const),
id: idArb,
layout: fc.constant({ kind: "absolute" as const }),
transform: fc.record({
position: fc.record({
x: fc.integer({ min: -100, max: 100 }),
y: fc.integer({ min: -100, max: 100 }),
}),
}),
children: fc.array(tie("node"), { maxLength: 4 }),
}),
})).node;
const surfaceArb: fc.Arbitrary<ControlSurface> = fc.record({
id: idArb,
name: fc.string({ minLength: 1, maxLength: 16 }),
version: fc.constant(CURRENT_SCHEMA_VERSION),
createdAt: fc.integer({ min: 0, max: 2 ** 40 }),
updatedAt: fc.integer({ min: 0, max: 2 ** 40 }),
skin: fc.record({ id: fc.constant("default") }),
root: nodeArb,
});
function dedupeIds(
node: ControlNode,
seen: Set<string>,
depth = 0
): ControlNode {
let id = node.id;
while (seen.has(id)) id = `${node.id}-${seen.size}-${depth}`;
seen.add(id);
if (node.type === "control") return { ...node, id };
return {
...node,
id,
children: node.children.map((c) => dedupeIds(c, seen, depth + 1)),
};
}
describe("property tests", () => {
it("validator accepts well-formed random surfaces", {
timeout: 5000,
}, () => {
fc.assert(
fc.property(surfaceArb, (s) => {
const surface: ControlSurface = {
...s,
root: dedupeIds(s.root, new Set()),
};
const result = validateSurface(surface);
if (result.error !== undefined) {
throw result.error;
}
expect(result.surface).toBeDefined();
}),
{ numRuns: 50 }
);
});
it("walkSurface visits every node exactly once", { timeout: 5000 }, () => {
fc.assert(
fc.property(surfaceArb, (s) => {
const surface: ControlSurface = {
...s,
root: dedupeIds(s.root, new Set()),
};
const validated = validateSurface(surface);
expect(validated.surface).toBeDefined();
const seen = new Set<string>();
walkSurface(validated.surface!, (n) => seen.add(n.id));
let count = 0;
const tally = (n: ControlNode): void => {
count += 1;
if (n.type === "group") {
for (const c of n.children) tally(c);
}
};
tally(validated.surface!.root);
expect(seen.size).toBe(count);
}),
{ numRuns: 30 }
);
});
});

View File

@@ -0,0 +1,74 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { CURRENT_SCHEMA_VERSION } from "../src/constants/constants.js";
import type { ControlSurface } from "../src/types/control-surface.types.js";
import { validateSurface } from "../src/validate/validate.js";
import { walkSurface } from "../src/walk/walk.js";
const fixture: ControlSurface = {
id: "round-trip",
name: "8-channel mixer",
version: CURRENT_SCHEMA_VERSION,
createdAt: 1700_000_000_000,
updatedAt: 1700_000_001_000,
skin: { id: "default", vars: { "--accent": "#0af" } },
root: {
type: "group",
id: "root",
layout: { kind: "stack", direction: "row", gap: 4 },
transform: { position: { x: 0, y: 0 } },
children: Array.from({ length: 8 }, (_, channel) => ({
type: "group",
id: `ch-${channel}`,
layout: { kind: "stack", direction: "column", gap: 2 },
transform: { position: { x: channel * 60, y: 0 } },
children: [
{
type: "control",
id: `ch-${channel}-vol`,
kind: "slider-v",
transform: { position: { x: 0, y: 0 } },
range: { min: 0, max: 1, default: 0.75, units: "lin" },
binding: {
kind: "pluginParam",
pluginId: "mixer",
paramPath: `channels/${channel}/volume`,
curve: "log",
},
},
{
type: "control",
id: `ch-${channel}-mute`,
kind: "button-latching",
transform: { position: { x: 0, y: 80 } },
binding: {
kind: "midiOut",
portId: "main",
channel: 0,
cc: 16 + channel,
},
},
],
})),
},
};
describe("round-trip — JSON → validate → walk → re-serialise", () => {
it("preserves every node through JSON.parse(JSON.stringify(...)) and re-validation", {
timeout: 5000,
}, () => {
const first = validateSurface(fixture);
expect(first.surface).toBeDefined();
const jsonString = JSON.stringify(first.surface);
const parsed = JSON.parse(jsonString);
const second = validateSurface(parsed);
expect(second.surface).toEqual(first.surface);
const ids: string[] = [];
walkSurface(second.surface!, (n) => ids.push(n.id));
// 1 root + 8 channel groups + 8 volume + 8 mute = 25
expect(ids.length).toBe(25);
expect(ids[0]).toBe("root");
});
});