Release Helper Tools 0.1.0
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user