import { describe, expect, it } from "vitest"; import { bufferReplacer, bufferReviver } from "../../utils/json"; describe("JSON utils", () => { describe("bufferReplacer", () => { it("should replace Buffer objects with serializable format", () => { const buffer = Buffer.from("hello world", "utf8"); const obj = { text: "normal string", data: buffer, nested: { buffer: Buffer.from("nested buffer"), number: 42, }, }; const serialized = JSON.stringify(obj, bufferReplacer); const parsed = JSON.parse(serialized); expect(parsed.text).toBe("normal string"); expect(parsed.data).toEqual({ $binary: buffer.toString("base64"), }); expect(parsed.nested.buffer).toEqual({ $binary: Buffer.from("nested buffer").toString("base64"), }); expect(parsed.nested.number).toBe(42); }); it("should handle empty buffers", () => { const emptyBuffer = Buffer.alloc(0); const obj = { empty: emptyBuffer }; const serialized = JSON.stringify(obj, bufferReplacer); const parsed = JSON.parse(serialized); expect(parsed.empty).toEqual({ $binary: "", }); }); it("should handle non-Buffer values normally", () => { const obj = { string: "test", number: 123, boolean: true, array: [1, 2, 3], object: { nested: "value" }, null: null, undefined, }; const serialized = JSON.stringify(obj, bufferReplacer); const parsed = JSON.parse(serialized); expect(parsed.string).toBe("test"); expect(parsed.number).toBe(123); expect(parsed.boolean).toBe(true); expect(parsed.array).toEqual([1, 2, 3]); expect(parsed.object).toEqual({ nested: "value" }); expect(parsed.null).toBeNull(); expect(parsed.undefined).toBeUndefined(); }); it("should handle arrays containing buffers", () => { const buffer1 = Buffer.from("buffer1"); const buffer2 = Buffer.from("buffer2"); const obj = { buffers: [buffer1, "string", buffer2, 123], }; const serialized = JSON.stringify(obj, bufferReplacer); const parsed = JSON.parse(serialized); expect(parsed.buffers[0]).toEqual({ $binary: buffer1.toString("base64"), }); expect(parsed.buffers[1]).toBe("string"); expect(parsed.buffers[2]).toEqual({ $binary: buffer2.toString("base64"), }); expect(parsed.buffers[3]).toBe(123); }); }); describe("bufferReviver", () => { it("should revive Buffer objects from serialized format", () => { const originalBuffer = Buffer.from("hello world", "utf8"); const serializedObj = { text: "normal string", data: { $binary: originalBuffer.toString("base64"), }, nested: { buffer: { $binary: Buffer.from("nested buffer").toString("base64"), }, number: 42, }, }; const serialized = JSON.stringify(serializedObj); const revived = JSON.parse(serialized, bufferReviver); expect(revived.text).toBe("normal string"); expect(Buffer.isBuffer(revived.data)).toBe(true); expect(revived.data.toString("utf8")).toBe("hello world"); expect(Buffer.isBuffer(revived.nested.buffer)).toBe(true); expect(revived.nested.buffer.toString("utf8")).toBe("nested buffer"); expect(revived.nested.number).toBe(42); }); it("should handle empty buffer objects", () => { const serializedObj = { empty: { $binary: "", }, }; const serialized = JSON.stringify(serializedObj); const revived = JSON.parse(serialized, bufferReviver); expect(Buffer.isBuffer(revived.empty)).toBe(true); expect(revived.empty.length).toBe(0); }); it("should handle non-buffer objects normally", () => { const obj = { string: "test", number: 123, boolean: true, array: [1, 2, 3], object: { nested: "value" }, null: null, }; const serialized = JSON.stringify(obj); const revived = JSON.parse(serialized, bufferReviver); expect(revived.string).toBe("test"); expect(revived.number).toBe(123); expect(revived.boolean).toBe(true); expect(revived.array).toEqual([1, 2, 3]); expect(revived.object).toEqual({ nested: "value" }); expect(revived.null).toBeNull(); }); it("should handle arrays containing buffer objects", () => { const buffer1Base64 = Buffer.from("buffer1").toString("base64"); const buffer2Base64 = Buffer.from("buffer2").toString("base64"); const serializedObj = { buffers: [ { $binary: buffer1Base64 }, "string", { $binary: buffer2Base64 }, 123, ], }; const serialized = JSON.stringify(serializedObj); const revived = JSON.parse(serialized, bufferReviver); expect(Buffer.isBuffer(revived.buffers[0])).toBe(true); expect(revived.buffers[0].toString("utf8")).toBe("buffer1"); expect(revived.buffers[1]).toBe("string"); expect(Buffer.isBuffer(revived.buffers[2])).toBe(true); expect(revived.buffers[2].toString("utf8")).toBe("buffer2"); expect(revived.buffers[3]).toBe(123); }); it("should not modify objects that look like buffer objects but aren't", () => { const obj = { notABuffer: { $binary: "this is not a valid base64", extraProperty: "should remain", }, almostBuffer: { binary: "base64data", }, emptyObject: {}, }; const serialized = JSON.stringify(obj); const revived = JSON.parse(serialized, bufferReviver); expect(revived.notABuffer).toEqual({ $binary: "this is not a valid base64", extraProperty: "should remain", }); expect(revived.almostBuffer).toEqual({ binary: "base64data", }); expect(revived.emptyObject).toEqual({}); }); }); describe("round-trip serialization", () => { it("should preserve data through serialization and deserialization", () => { const originalData = { string: "test string", number: 42, boolean: true, buffer: Buffer.from("test buffer content", "utf8"), nested: { data: "nested string", buffer: Buffer.from("nested buffer", "utf8"), array: [ "string item", Buffer.from("array buffer"), 123, { deepBuffer: Buffer.from("deep buffer"), }, ], }, arrayOfBuffers: [ Buffer.from("buffer1"), Buffer.from("buffer2"), Buffer.from("buffer3"), ], }; // Serialize with buffer replacer const serialized = JSON.stringify(originalData, bufferReplacer); // Deserialize with buffer reviver const deserialized = JSON.parse(serialized, bufferReviver); // Check primitive values expect(deserialized.string).toBe(originalData.string); expect(deserialized.number).toBe(originalData.number); expect(deserialized.boolean).toBe(originalData.boolean); // Check buffer preservation expect(Buffer.isBuffer(deserialized.buffer)).toBe(true); expect(deserialized.buffer.toString("utf8")).toBe("test buffer content"); // Check nested structure expect(deserialized.nested.data).toBe(originalData.nested.data); expect(Buffer.isBuffer(deserialized.nested.buffer)).toBe(true); expect(deserialized.nested.buffer.toString("utf8")).toBe("nested buffer"); // Check nested array expect(deserialized.nested.array[0]).toBe("string item"); expect(Buffer.isBuffer(deserialized.nested.array[1])).toBe(true); expect(deserialized.nested.array[1].toString("utf8")).toBe( "array buffer" ); expect(deserialized.nested.array[2]).toBe(123); expect(Buffer.isBuffer(deserialized.nested.array[3].deepBuffer)).toBe( true ); expect(deserialized.nested.array[3].deepBuffer.toString("utf8")).toBe( "deep buffer" ); // Check array of buffers expect(deserialized.arrayOfBuffers).toHaveLength(3); expect(Buffer.isBuffer(deserialized.arrayOfBuffers[0])).toBe(true); expect(deserialized.arrayOfBuffers[0].toString("utf8")).toBe("buffer1"); expect(Buffer.isBuffer(deserialized.arrayOfBuffers[1])).toBe(true); expect(deserialized.arrayOfBuffers[1].toString("utf8")).toBe("buffer2"); expect(Buffer.isBuffer(deserialized.arrayOfBuffers[2])).toBe(true); expect(deserialized.arrayOfBuffers[2].toString("utf8")).toBe("buffer3"); }); it("should handle large buffers", () => { const largeBuffer = Buffer.alloc(1024 * 1024, "a"); // 1MB buffer const obj = { large: largeBuffer }; const serialized = JSON.stringify(obj, bufferReplacer); const deserialized = JSON.parse(serialized, bufferReviver); expect(Buffer.isBuffer(deserialized.large)).toBe(true); expect(deserialized.large.length).toBe(1024 * 1024); expect(deserialized.large.toString("utf8")).toBe("a".repeat(1024 * 1024)); }); it("should handle binary data correctly", () => { const binaryData = Buffer.from([0, 1, 2, 3, 255, 254, 253]); const obj = { binary: binaryData }; const serialized = JSON.stringify(obj, bufferReplacer); const deserialized = JSON.parse(serialized, bufferReviver); expect(Buffer.isBuffer(deserialized.binary)).toBe(true); expect(Array.from(deserialized.binary)).toEqual([ 0, 1, 2, 3, 255, 254, 253, ]); }); }); });