pi-mono/packages/chord/test/store/columns.test.ts
Mario Zechner e9c0ff4d84 feat(chord): Chord v2 is the package's root
src/next is src (the main export), src/next-node is src/node with short subpaths (/http, /workers, /plugins,
/plugins/runtime, /app, /surfaces, /web, /react, /tui, /tui/launcher); the foundations get /json and /watch;
Chord v1 moves to src/old (/old, /old/node) until p2 runs on v2; tests and docs move likewise (test/, docs/,
test/old, docs/old). Durable and env import the foundations from their subpaths.
2026-11-02 22:36:55 +00:00

67 lines
2.9 KiB
TypeScript

// Table columns (decisions "Flat columns", "Nullable columns and the `set` form"): descriptors and row types.
import { describe, expect, it } from "vitest";
import { arr, bool, int, num, obj, opt, str } from "../../src/contracts/index.ts";
import { type Columns, columnRecord, describeColumns, type PutRowOf, type RowOf } from "../../src/store/columns.ts";
import { json } from "../../src/store/json-column.ts";
import type { Equal } from "../support.ts";
describe("column descriptors", () => {
it("describes the helpers in declaration order", () => {
const columns = describeColumns(
{
id: str(),
n: int({ default: 0 }),
x: num(),
b: bool(),
note: opt(str()),
meta: opt(json(obj({}), { version: 2 })),
},
"t",
);
expect(columns.map(({ name, type, optional }) => [name, type, optional])).toEqual([
["id", "str", false],
["n", "int", false],
["x", "num", false],
["b", "bool", false],
["note", "str", true],
["meta", "json", true],
]);
expect(columns.map(columnRecord)).toEqual([
{ type: "str" },
{ type: "int", default: 0 },
{ type: "num" },
{ type: "bool" },
{ type: "str", optional: true },
{ type: "json", optional: true, version: 2, fingerprint: expect.any(String) },
]);
});
it("checks names: the pattern, no two equal ignoring case, no $", () => {
expect(() => describeColumns({ "a-b": str() }, "t")).toThrow("[A-Za-z_][A-Za-z0-9_]*");
expect(() => describeColumns({ a$v: str() }, "t")).toThrow('column "a$v"');
expect(() => describeColumns({ path: str(), Path: str() }, "t")).toThrow("only in case");
expect(describeColumns({ camelCase: str(), _x1: int() }, "t")).toHaveLength(2);
});
it("refuses other schemas, bounds, patterns, opt defaults and defaults of another type", () => {
const columns = (value: unknown) => ({ c: value }) as unknown as Columns;
expect(() => describeColumns(columns(arr(str())), "t")).toThrow("expected str()");
expect(() => describeColumns(columns(obj({})), "t")).toThrow("expected str()");
expect(() => describeColumns(columns(str({ max: 3 })), "t")).toThrow('"maxLength" is not supported');
expect(() => describeColumns(columns(str({ pattern: "a" })), "t")).toThrow('"pattern" is not supported');
expect(() => describeColumns(columns(opt(int({ default: 1 }))), "t")).toThrow("no default");
expect(() => describeColumns(columns(int({ default: 1.5 })), "t")).toThrow("not a int value");
expect(() => describeColumns(columns(str({ default: "a\0" })), "t")).toThrow("NUL");
});
});
describe("row types", () => {
it("types rows with optional opt(...) fields and puts with optional defaulted fields", () => {
const shape = { id: str(), hits: int({ default: 0 }), note: opt(str()) };
type S = typeof shape;
const row: Equal<RowOf<S>, { id: string; hits: number } & { note?: string }> = true;
const put: Equal<PutRowOf<S>, { id: string } & { hits?: number; note?: string }> = true;
expect([row, put]).toEqual([true, true]);
});
});