Release Data Tools 0.2.0
Verify / verify (push) Canceled after 0s

This commit is contained in:
2026-09-02 12:50:51 +02:00
parent e773a2ffe8
commit a8eb8e53b3
36 changed files with 2168 additions and 235 deletions
+115
View File
@@ -0,0 +1,115 @@
import { describe, expect, it } from "vitest";
import { DATA_ADAPTERS } from "../../src/core/adapters";
import { EXPORT_ADAPTERS, convertDocument } from "../../src/core/convert";
import { applyNodeEdit, parseEditValue } from "../../src/core/edit";
import { normalizeValue } from "../../src/core/model";
import { IMPORT_ADAPTERS, parseDataDocument } from "../../src/core/parse";
import { inferSchema, serializeSchema } from "../../src/core/schema";
import { DATA_FORMATS } from "../../src/core/types";
describe("modular adapters", () => {
it("has an explicit import and export implementation for every descriptor", () => {
expect(DATA_ADAPTERS.map((adapter) => adapter.format)).toEqual(
DATA_FORMATS,
);
expect(Object.keys(IMPORT_ADAPTERS)).toEqual([...DATA_FORMATS]);
expect(Object.keys(EXPORT_ADAPTERS)).toEqual([...DATA_FORMATS]);
});
});
describe("loss-aware schema inference", () => {
it("merges record samples, presence and exact numeric representation", () => {
const document = parseDataDocument({
source:
'{"people":[{"id":9007199254740993123456789,"name":"Ada"},{"id":2,"nickname":null}]}',
format: "json",
});
const inference = inferSchema(document.root);
expect(inference.fields).toEqual(
expect.arrayContaining([
expect.objectContaining({
pointer: "/people/*/id",
required: true,
types: ["integer"],
}),
expect.objectContaining({ pointer: "/people/*/name", required: false }),
expect.objectContaining({
pointer: "/people/*/nickname",
nullable: true,
}),
]),
);
const schema = JSON.parse(serializeSchema(inference)) as Record<
string,
unknown
>;
expect(schema.$schema).toBe("https://json-schema.org/draft/2020-12/schema");
expect(JSON.stringify(schema)).toContain("9007199254740993123456789");
});
it("enforces the field budget across nested objects", () => {
const branch = (prefix: string) =>
Object.fromEntries(
Array.from({ length: 3_000 }, (_, index) => [
`${prefix}${index}`,
index,
]),
);
const inference = inferSchema(
normalizeValue({ left: branch("a"), right: branch("b") }),
);
expect(inference.fields).toHaveLength(5_000);
expect(inference.truncated).toBe(true);
});
});
describe("reviewable structural editing", () => {
const document = parseDataDocument({
source: '{"items":[1,2],"name":"before"}',
format: "json",
});
it("uses strict scalar syntax and immutable RFC 6901 operations", () => {
const replaced = applyNodeEdit(document.root, {
operation: "replace",
pointer: "/name",
value: parseEditValue("string", "after"),
});
const inserted = applyNodeEdit(replaced, {
operation: "add",
pointer: "/items/1",
value: parseEditValue("number", "9007199254740993123456789"),
});
expect(
convertDocument({ ...document, root: inserted }, "json").text,
).toContain("9007199254740993123456789");
expect(convertDocument(document, "json").text).toContain(
'"name": "before"',
);
expect(() => parseEditValue("number", "01")).toThrow(/strict JSON/iu);
expect(() =>
applyNodeEdit(document.root, {
operation: "add",
pointer: "/__proto__",
value: parseEditValue("null", ""),
}),
).toThrow(/prohibited/iu);
});
it("exposes serialization loss before an edit is applied", () => {
const toml = parseDataDocument({
source: "answer = 42\n",
format: "toml",
});
const edited = applyNodeEdit(toml.root, {
operation: "add",
pointer: "/missing",
value: parseEditValue("null", ""),
});
expect(convertDocument({ ...toml, root: edited }, "toml").events).toEqual(
expect.arrayContaining([
expect.objectContaining({ code: "toml.null", severity: "loss" }),
]),
);
});
});