Initial Commit
This commit is contained in:
@@ -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 }
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user