char/fjson

friendly JSON implementation in TypeScript

git clone https://git.t4t.associates/char/fjson

Charlotte Somrestructurebb03820

main
4.4 KiB158 linesraw
1// vibed
2
3import {
4  deepStrictEqual,
5  equal,
6  ok,
7  strictEqual,
8  throws,
9} from "node:assert/strict";
10import { FjsonError, parse, stringify } from "./mod.ts";
11
12Deno.test("pretty output uses fjson keys and balanced nested indentation", () => {
13  const value = {
14    name: "fJSON",
15    options: { enabled: true, targets: ["linux", "macos"] },
16    "@scope/pkg": { $valid_key2: 'šŸ˜€\nquoted "text"' },
17  };
18  const printed = stringify(value);
19  strictEqual(
20    printed,
21    `name: "fJSON"
22options: {
23  enabled: true
24  targets: ["linux", "macos"]
25}
26"@scope/pkg": {
27  $valid_key2: "šŸ˜€\\nquoted \\"text\\""
28}`,
29  );
30  deepStrictEqual(parse(printed), value);
31});
32
33Deno.test("long arrays expand and printing is stable", () => {
34  const value = {
35    records: [{ x: [1, 2] }, { x: [null, false] }],
36    long: Array.from({ length: 40 }, (_, i) => i),
37  };
38  const text = stringify(value);
39  ok(text.includes("long: [\n"), text);
40  for (const line of text.split("\n")) {
41    ok([...line].length <= 80, line);
42  }
43  ok(text.includes("    x: [1, 2]\n  }"), text);
44  deepStrictEqual(parse(text), value);
45  strictEqual(stringify(parse(text)), text);
46});
47
48Deno.test("parser and printer accept the same nesting depths", () => {
49  for (const leaf of [null, [], {}]) {
50    let value: unknown = leaf;
51    for (let depth = 0; depth < 132; depth += 1) {
52      for (const candidate of [value, { root: value }]) {
53        const encoded = JSON.stringify(candidate);
54        let parseKind: string | undefined;
55        try {
56          parse(encoded);
57        } catch (error) {
58          ok(error instanceof FjsonError);
59          parseKind = error.kind;
60        }
61        let printed: string | undefined;
62        let printKind: string | undefined;
63        try {
64          printed = stringify(candidate);
65        } catch (error) {
66          ok(error instanceof FjsonError);
67          printKind = error.kind;
68        }
69        strictEqual(printKind === undefined, parseKind === undefined, encoded);
70        if (printed !== undefined) {
71          deepStrictEqual(parse(printed), candidate);
72        } else {
73          strictEqual(printKind, "nesting");
74          strictEqual(parseKind, printKind);
75        }
76      }
77      value = [value];
78    }
79  }
80});
81
82Deno.test("arbitrary json values round trip including escaped keys", () => {
83  const values: unknown[] = [
84    null,
85    true,
86    42,
87    -0.125,
88    " \tšŸ˜€",
89    [],
90    {},
91    [{}, [], null],
92    JSON.parse(
93      '{"0": 1, "": 2, "true": 3, "some-key": 4, "ę—„ęœ¬čŖž": "\\r\\n", "__proto__": {"ok": true}}',
94    ),
95  ];
96  for (const value of values) {
97    const text = stringify(value);
98    deepStrictEqual(parse(text), JSON.parse(JSON.stringify(value)), text);
99  }
100});
101
102Deno.test("roots print without a final newline", () => {
103  strictEqual(stringify(null), "null");
104  strictEqual(stringify(false), "false");
105  strictEqual(stringify(42), "42");
106  strictEqual(stringify("šŸ˜€"), '"šŸ˜€"');
107  strictEqual(stringify([]), "[]");
108  strictEqual(stringify({}), "{}");
109  strictEqual(stringify({ a: 1 }), "a: 1");
110  strictEqual(stringify([1, true]), "[1, true]");
111  strictEqual(stringify({ nested: {} }), "nested: {}");
112});
113
114Deno.test("non-finite numbers print as null like JSON", () => {
115  strictEqual(stringify(NaN), "null");
116  strictEqual(stringify(Infinity), "null");
117  strictEqual(stringify([NaN]), "[null]");
118});
119
120Deno.test("unserializable values throw TypeError", () => {
121  for (
122    const value of [
123      undefined,
124      () => {},
125      Symbol("x"),
126      10n,
127      (() => {
128        const circular: Record<string, unknown> = {};
129        circular["self"] = circular;
130        return circular;
131      })(),
132    ]
133  ) {
134    throws(() => stringify(value), TypeError);
135  }
136});
137
138Deno.test("custom limits control printing", () => {
139  let value: unknown = null;
140  for (let i = 0; i < 6; i += 1) value = [value];
141  const text = stringify(value, { maxDepth: 6 });
142  deepStrictEqual(parse(text, { maxDepth: 6 }), value);
143  const error = (() => {
144    try {
145      stringify(value, { maxDepth: 4 });
146    } catch (caught) {
147      ok(caught instanceof FjsonError);
148      return caught;
149    }
150    throw new Error("expected nesting error");
151  })();
152  strictEqual(error.kind, "nesting");
153  ok(error.message.includes("4 levels"), error.message);
154  ok(parse(text, { maxDepth: 6 }));
155  throws(() => parse(text, { maxDepth: 4 }), FjsonError);
156  equal(stringify({ a: [0] }, { maxDepth: 1 }), "a: [0]");
157  throws(() => stringify({ a: [[0]] }, { maxDepth: 1 }), FjsonError);
158});