Files
helper-tools/tests/helpers/structured-time.test.ts
2026-09-01 02:33:45 +02:00

78 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
formatDuration,
formatTimestamp,
parseCsv,
parseDuration,
parseTimestamp,
safeJsonParse,
stableStringify,
stringifyCsv,
} from "../../src/helpers";
describe("bounded structured data", () => {
it("parses hardened JSON and serializes deterministic key order", () => {
expect(safeJsonParse('{"b":2,"a":[true,null]}')).toEqual({
b: 2,
a: [true, null],
});
expect(stableStringify({ z: 1, a: { d: 4, c: 3 } }, 2)).toBe(
'{\n "a": {\n "c": 3,\n "d": 4\n },\n "z": 1\n}',
);
expect(() => safeJsonParse('{"__proto__":{"polluted":true}}')).toThrow(
/Dangerous/u,
);
expect(() => safeJsonParse("[[[0]]]", { maxDepth: 2 })).toThrow(/depth/u);
expect(() => safeJsonParse("[1,2,3]", { maxNodes: 3 })).toThrow(/node/u);
const cycle: Record<string, unknown> = {};
cycle.self = cycle;
expect(() => stableStringify(cycle)).toThrow(/cyclic/u);
expect(() => stableStringify({ value: 1n })).toThrow(/BigInt/u);
});
it("handles quoted CSV, embedded newlines, and round trips", () => {
const rows = [
["name", "note"],
["Ada", 'comma, quote " and\nnewline'],
["", "last"],
];
const csv = stringifyCsv(rows);
expect(csv).toContain('"comma, quote "" and\nnewline"');
expect(parseCsv(csv)).toEqual(rows);
expect(parseCsv("a;b\r\n1;2", { delimiter: ";" })).toEqual([
["a", "b"],
["1", "2"],
]);
expect(() => parseCsv('"unterminated')).toThrow(/unterminated/u);
expect(() => parseCsv("a,b,c", { maxColumns: 2 })).toThrow(/column/u);
});
});
describe("timestamps and durations", () => {
it("normalizes ISO and Unix timestamps", () => {
expect(parseTimestamp(0, "seconds")).toMatchObject({
epochMilliseconds: 0,
epochSeconds: 0,
iso: "1970-01-01T00:00:00.000Z",
});
expect(parseTimestamp("2000-01-01T00:00:00Z").epochSeconds).toBe(946684800);
expect(formatTimestamp(0, "UTC")).toMatch(/1970/u);
expect(() => parseTimestamp("not a date")).toThrow(/range/u);
});
it("parses ISO, clock, and token durations and formats them", () => {
expect(parseDuration("P1DT2H3M4.5S")).toBe(93_784_500);
expect(parseDuration("01:02:03.004")).toBe(3_723_004);
expect(parseDuration("2h 30m")).toBe(9_000_000);
expect(parseDuration("-2h 30m")).toBe(-9_000_000);
expect(parseDuration(formatDuration(-9_000_000, "human"))).toBe(-9_000_000);
expect(formatDuration(93_784_500, "iso")).toBe("P1DT2H3M4.5S");
expect(formatDuration(3_723_004, "clock")).toBe("01:02:03.004");
expect(formatDuration(9_000_000, "human")).toBe("2h 30m");
expect(formatDuration(86_400_000, "iso")).toBe("P1D");
expect(formatDuration(59_999.6, "clock")).toBe("00:01:00.000");
expect(() => parseDuration("1 month")).toThrow(/Duration/u);
});
});