118 lines
3.8 KiB
TypeScript
118 lines
3.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { convertDocument } from "../../src/core/convert";
|
|
import { DATA_LIMITS } from "../../src/core/limits";
|
|
import { parseDataDocument } from "../../src/core/parse";
|
|
import {
|
|
flattenRows,
|
|
queryPointer,
|
|
querySafePath,
|
|
tableFromNode,
|
|
} from "../../src/core/query";
|
|
|
|
const document = parseDataDocument({
|
|
source:
|
|
'{"a/b":{"~key":7},"people":[{"name":"Ada","role":"math"},{"name":"Grace","role":"code"}]}',
|
|
format: "json",
|
|
});
|
|
|
|
describe("safe data projections", () => {
|
|
it("resolves RFC 6901 escaping", () => {
|
|
expect(queryPointer(document.root, "/a~1b/~0key")[0]?.node).toMatchObject({
|
|
type: "number",
|
|
raw: "7",
|
|
});
|
|
});
|
|
|
|
it("supports a deliberately small non-evaluating path syntax", () => {
|
|
expect(querySafePath(document.root, "$.people[*].name")).toHaveLength(2);
|
|
expect(
|
|
querySafePath(document.root, "$['a/b']['~key']")[0]?.node,
|
|
).toMatchObject({ raw: "7" });
|
|
expect(() => querySafePath(document.root, "$..name")).toThrow(
|
|
/unsupported path syntax/iu,
|
|
);
|
|
expect(() => querySafePath(document.root, "$.people[?(@.role)]")).toThrow(
|
|
/filters/iu,
|
|
);
|
|
expect(() =>
|
|
queryPointer(
|
|
document.root,
|
|
`/${"a".repeat(DATA_LIMITS.maxQueryCharacters)}`,
|
|
),
|
|
).toThrow(/Query character count/iu);
|
|
});
|
|
|
|
it("projects record arrays and flattened scalar leaves", () => {
|
|
const people = queryPointer(document.root, "/people")[0]?.node;
|
|
expect(people && tableFromNode(people)).toMatchObject({
|
|
columns: ["name", "role"],
|
|
rows: [
|
|
["Ada", "math"],
|
|
["Grace", "code"],
|
|
],
|
|
});
|
|
expect(flattenRows(document.root).rows).toEqual(
|
|
expect.arrayContaining([["/a~1b/~0key", "number", "7"]]),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("explicit conversions", () => {
|
|
it("neutralises spreadsheet formula cells only when requested", () => {
|
|
const csv = parseDataDocument({
|
|
source: 'name,value\ncalc,"=2+2"\nsafe,text',
|
|
format: "csv",
|
|
});
|
|
const protectedOutput = convertDocument(csv, "csv", {
|
|
spreadsheetSafe: true,
|
|
});
|
|
expect(protectedOutput.text).toContain("'=2+2");
|
|
expect(protectedOutput.events).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ code: "csv.formula-neutralized" }),
|
|
]),
|
|
);
|
|
const raw = convertDocument(csv, "csv", { spreadsheetSafe: false });
|
|
expect(raw.text).toContain("=2+2");
|
|
expect(raw.events).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ code: "csv.formula-unchecked" }),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it("turns unsafe decimals into strings rather than silently rounding", () => {
|
|
const precise = parseDataDocument({
|
|
source: '{"value":0.1234567890123456789012345}',
|
|
format: "json",
|
|
});
|
|
const yaml = convertDocument(precise, "yaml");
|
|
expect(yaml.events).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ code: "yaml.unsafe-number" }),
|
|
]),
|
|
);
|
|
expect(yaml.text).toContain("0.1234567890123456789012345");
|
|
});
|
|
|
|
it("reports null and root coercion when targeting TOML", () => {
|
|
const value = parseDataDocument({ source: "null", format: "json" });
|
|
const result = convertDocument(value, "toml");
|
|
expect(result.text).toContain('value = ""');
|
|
expect(result.events.map((event) => event.code)).toEqual(
|
|
expect.arrayContaining(["toml.null", "toml.root-table"]),
|
|
);
|
|
});
|
|
|
|
it("converts ordinary data to XML using a disclosed stable convention", () => {
|
|
const result = convertDocument(document, "xml");
|
|
expect(result.text).toContain("<people>");
|
|
expect(result.text).toContain("<name>Ada</name>");
|
|
expect(result.events).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ code: "xml.mapping" }),
|
|
]),
|
|
);
|
|
});
|
|
});
|