import { describe, expect, it } from "vitest"; import { convertDocument } from "../../src/core/convert"; import { DATA_LIMITS } from "../../src/core/limits"; import { objectValue } from "../../src/core/model"; import { parseDataDocument } from "../../src/core/parse"; import { DataToolsError } from "../../src/core/types"; describe("bounded format parsers", () => { it("preserves JSON number lexemes exactly", () => { const source = '{"integer":9007199254740993123456789,"decimal":0.123456789012345678901}'; const document = parseDataDocument({ source, format: "json" }); expect(objectValue(document.root, "integer")).toMatchObject({ type: "number", raw: "9007199254740993123456789", representation: "exact", }); expect(convertDocument(document, "json").text).toContain( "0.123456789012345678901", ); }); it("rejects duplicate and prototype-affecting JSON keys", () => { expect(() => parseDataDocument({ source: '{"a":1,"a":2}', format: "json" }), ).toThrow(DataToolsError); expect(() => parseDataDocument({ source: '{"__proto__":1}', format: "json" }), ).toThrow(/dangerous object key/iu); }); it("uses YAML 1.2 core semantics and rejects custom tags and merges", () => { const document = parseDataDocument({ source: "answer: yes\nactual: true\ninteger: 9007199254740993\n", format: "yaml", }); expect(objectValue(document.root, "answer")).toEqual({ type: "string", value: "yes", }); expect(objectValue(document.root, "integer")).toMatchObject({ type: "number", raw: "9007199254740993", }); expect(() => parseDataDocument({ source: "value: !custom test", format: "yaml" }), ).toThrow(/tag/iu); expect(() => parseDataDocument({ source: "base: &base\n a: 1\ncopy:\n <<: *base\n", format: "yaml", }), ).toThrow(/merge/iu); }); it("keeps TOML integers exact and models date/time values", () => { const document = parseDataDocument({ source: 'title = "example"\nlarge = 9007199254740993\ncreated = 2026-09-01T10:15:30Z\n', format: "toml", }); expect(objectValue(document.root, "large")).toMatchObject({ type: "number", raw: "9007199254740993", representation: "bigint", }); expect(objectValue(document.root, "created")).toMatchObject({ type: "date", dateKind: "offset-date-time", }); }); it("rejects XML declarations with active resolution semantics", () => { expect(() => parseDataDocument({ source: ']>&x;', format: "xml", }), ).toThrow(/DOCTYPE/iu); expect(() => parseDataDocument({ source: '', format: "xml", }), ).toThrow(/XInclude/iu); expect(() => parseDataDocument({ source: '', format: "xml", }), ).toThrow(/XInclude/iu); }); it("preserves XML namespaces, attributes and ordered children explicitly", () => { const document = parseDataDocument({ source: 'beforeoneafter', format: "xml", }); expect(document.model).toBe("xml-explicit"); expect(objectValue(document.root, "name")).toEqual({ type: "string", value: "n:root", }); expect(objectValue(document.root, "namespace")).toEqual({ type: "string", value: "urn:test", }); expect(convertDocument(document, "xml").text).toContain( "one", ); }); it("parses delimited data as strings, including quoted fields", () => { const document = parseDataDocument({ source: 'name,value\r\nAda,"1,200"\r\nZero,001', format: "csv", }); expect(document.model).toBe("tabular"); expect(document.root).toMatchObject({ type: "array" }); expect(document.diagnostics).toEqual( expect.arrayContaining([ expect.objectContaining({ code: "delimited.strings" }), ]), ); }); it("preflights delimited dimensions before allocating the parsed table", () => { const row = new Array(DATA_LIMITS.maxColumns).fill("x").join(","); const source = new Array( Math.floor(DATA_LIMITS.maxCells / DATA_LIMITS.maxColumns) + 2, ) .fill(row) .join("\n"); expect(() => parseDataDocument({ source, format: "csv" })).toThrow( /Cell count/iu, ); }); it("reports an exact NDJSON line and ignores blank lines", () => { const document = parseDataDocument({ source: '{"id":1}\n\n{"id":2}\n', format: "ndjson", }); expect(document.root).toMatchObject({ type: "array", items: [{}, {}] }); expect(document.diagnostics).toEqual( expect.arrayContaining([ expect.objectContaining({ code: "ndjson.blank-lines" }), ]), ); expect(() => parseDataDocument({ source: '{"id":1}\n{"id":}\n', format: "ndjson", }), ).toThrow(/line 2/iu); }); it("detects by content or filename and discloses a mismatch", () => { expect( parseDataDocument({ source: '{"ok":true}\n{"ok":false}', format: "auto", }).format, ).toBe("ndjson"); const mismatch = parseDataDocument({ source: '{"looks":"json"}', format: "auto", filename: "values.yaml", }); expect(mismatch.format).toBe("yaml"); expect(mismatch.diagnostics).toEqual( expect.arrayContaining([ expect.objectContaining({ code: "format.possible-mismatch" }), ]), ); }); it("rejects inputs deeper than the structural cap before modelling", () => { const source = `${"[".repeat(DATA_LIMITS.maxDepth + 1)}0${"]".repeat(DATA_LIMITS.maxDepth + 1)}`; expect(() => parseDataDocument({ source, format: "json" })).toThrow( /depth/iu, ); }); });