// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { assertEquals, assertThrows } from "@std/assert"; import { concat } from "@std/bytes"; import { random } from "./_common_test.ts"; import { encodeCbor } from "./encode_cbor.ts"; import { CborTag } from "./tag.ts"; Deno.test("encodeCbor() encoding undefined", () => { assertEquals( encodeCbor(undefined), new Uint8Array([0b111_10111]), ); }); Deno.test("encodeCbor() encoding null", () => { assertEquals(encodeCbor(null), new Uint8Array([0b111_10110])); }); Deno.test("encodeCbor() encoding true", () => { assertEquals(encodeCbor(true), new Uint8Array([0b111_10101])); }); Deno.test("encodeCbor() encoding false", () => { assertEquals(encodeCbor(false), new Uint8Array([0b111_10100])); }); Deno.test("encodeCbor() encoding numbers as Uint", () => { let num: number | bigint = random(0, 24); assertEquals(encodeCbor(num), new Uint8Array([0b000_00000 + num])); num = random(24, 2 ** 8); assertEquals(encodeCbor(num), new Uint8Array([0b000_11000, num])); num = random(2 ** 8, 2 ** 16); assertEquals( encodeCbor(num), new Uint8Array([0b000_11001, num >> 8 & 0xFF, num & 0xFF]), ); num = random(2 ** 16, 2 ** 32); assertEquals( encodeCbor(num), new Uint8Array([ 0b000_11010, num >> 24 & 0xFF, num >> 16 & 0xFF, num >> 8 & 0xFF, num & 0xFF, ]), ); // JavaScript fails at correctly bit-wising this many bits as a number. num = BigInt(Number.MAX_SAFE_INTEGER); assertEquals( encodeCbor(Number.MAX_SAFE_INTEGER), new Uint8Array( [ 0b000_11011, num >> 56n & 0xFFn, num >> 48n & 0xFFn, num >> 40n & 0xFFn, num >> 32n & 0xFFn, num >> 24n & 0xFFn, num >> 16n & 0xFFn, num >> 8n & 0xFFn, num & 0xFFn, ].map((x) => Number(x)), ), ); }); Deno.test("encodeCbor() encoding numbers as Int", () => { const num = -random(1, 24); // -0 === 0 assertEquals( encodeCbor(num), new Uint8Array([0b001_00000 + (-num - 1)]), ); }); Deno.test("encodeCbor() encoding numbers as Float", () => { const num = Math.random() * 2 ** 32; const view = new DataView(new ArrayBuffer(8)); view.setFloat64(0, num); assertEquals( encodeCbor(num), concat([new Uint8Array([0b111_11011]), new Uint8Array(view.buffer)]), ); }); Deno.test("encodeCbor() encoding bigints as Uint", () => { let num = BigInt(random(0, 24)); assertEquals( encodeCbor(num), new Uint8Array([0b000_00000 + Number(num)]), ); num = BigInt(random(24, 2 ** 8)); assertEquals(encodeCbor(num), new Uint8Array([0b000_11000, Number(num)])); num = BigInt(random(2 ** 8, 2 ** 16)); assertEquals( encodeCbor(num), new Uint8Array([ 0b000_11001, Number(num >> 8n & 0xFFn), Number(num & 0xFFn), ]), ); num = BigInt(random(2 ** 16, 2 ** 32)); assertEquals( encodeCbor(num), new Uint8Array([ 0b000_11010, Number(num >> 24n & 0xFFn), Number(num >> 16n & 0xFFn), Number(num >> 8n & 0xFFn), Number(num & 0xFFn), ]), ); num = BigInt(random(2 ** 32, 2 ** 64)); assertEquals( encodeCbor(num), new Uint8Array([ 0b000_11011, Number(num >> 56n & 0xFFn), Number(num >> 48n & 0xFFn), Number(num >> 40n & 0xFFn), Number(num >> 32n & 0xFFn), Number(num >> 24n & 0xFFn), Number(num >> 16n & 0xFFn), Number(num >> 8n & 0xFFn), Number(num & 0xFFn), ]), ); }); Deno.test("encodeCbor() encoding bigints as Int", () => { const num = -BigInt(random(1, 24)); // -0 === 0 assertEquals( encodeCbor(num), new Uint8Array([0b001_00000 + Number(-num - 1n)]), ); }); Deno.test("encodeCbor() encoding strings", () => { const decoder = new TextDecoder(); let bytes = new Uint8Array(random(0, 24)).map((_) => random(97, 123)); // Range: `a` - `z` assertEquals( encodeCbor(decoder.decode(bytes)), new Uint8Array([0b011_00000 + bytes.length, ...bytes]), ); bytes = new Uint8Array(random(24, 2 ** 8)).map((_) => random(97, 123)); // Range: `a` - `z` assertEquals( encodeCbor(decoder.decode(bytes)), new Uint8Array([0b011_11000, bytes.length, ...bytes]), ); bytes = new Uint8Array(random(2 ** 8, 2 ** 16)).map((_) => random(97, 123)); // Range: `a` - `z` assertEquals( encodeCbor(decoder.decode(bytes)), new Uint8Array([ 0b011_11001, bytes.length >> 8 & 0xFF, bytes.length & 0xFF, ...bytes, ]), ); bytes = new Uint8Array(random(2 ** 16, 2 ** 17)).map((_) => random(97, 123)); // Range: `a` - `z` assertEquals( encodeCbor(decoder.decode(bytes)), new Uint8Array([ 0b011_11010, bytes.length >> 24 & 0xFF, bytes.length >> 16 & 0xFF, bytes.length >> 8 & 0xFF, bytes.length & 0xFF, ...bytes, ]), ); // Can't test the next bracket up due to JavaScript limitations. }); Deno.test("encodeCbor() encoding Uint8Arrays", () => { let bytes = new Uint8Array(random(0, 24)); assertEquals( encodeCbor(bytes), new Uint8Array([0b010_00000 + bytes.length, ...bytes]), ); bytes = new Uint8Array(random(24, 2 ** 8)); assertEquals( encodeCbor(bytes), new Uint8Array([0b010_11000, bytes.length, ...bytes]), ); bytes = new Uint8Array(random(2 ** 8, 2 ** 16)); assertEquals( encodeCbor(bytes), new Uint8Array([ 0b010_11001, bytes.length >> 8 & 0xFF, bytes.length & 0xFF, ...bytes, ]), ); bytes = new Uint8Array(random(2 ** 16, 2 ** 17)); assertEquals( encodeCbor(bytes), new Uint8Array([ 0b010_11010, bytes.length >> 24 & 0xFF, bytes.length >> 16 & 0xFF, bytes.length >> 8 & 0xFF, bytes.length & 0xFF, ...bytes, ]), ); // Can't test the next bracket up due to JavaScript limitations. }); Deno.test("encodeCbor() encoding Dates", () => { const date = new Date(); assertEquals( encodeCbor(date), new Uint8Array([0b110_00001, ...encodeCbor(date.getTime() / 1000)]), ); }); Deno.test("encodeCbor() encoding arrays", () => { let array = new Array(random(0, 24)).fill(0); assertEquals( encodeCbor(array), new Uint8Array([ 0b100_00000 + array.length, ...array.map((x) => [...encodeCbor(x)]).flat(), ]), ); array = new Array(random(24, 2 ** 8)).fill(0); assertEquals( encodeCbor(array), new Uint8Array([ 0b100_11000, array.length, ...array.map((x) => [...encodeCbor(x)]).flat(), ]), ); array = new Array(random(2 ** 8, 2 ** 16)).fill(0); assertEquals( encodeCbor(array), new Uint8Array([ 0b100_11001, array.length >> 8 & 0xFF, array.length & 0xFF, ...array.map((x) => [...encodeCbor(x)]).flat(), ]), ); array = new Array(random(2 ** 16, 2 ** 17)).fill(0); assertEquals( encodeCbor(array), new Uint8Array([ 0b100_11010, array.length >> 24 & 0xFF, array.length >> 16 & 0xFF, array.length >> 8 & 0xFF, array.length & 0xFF, ...array.map((x) => [...encodeCbor(x)]).flat(), ]), ); // Can't test the next bracket up due to JavaScript limitations. }); Deno.test("encodeCbor() encoding objects", () => { let pairs = random(0, 24); let entries: [string, number][] = new Array(pairs).fill(0).map(( _, i, ) => [i.toString(), i]); assertEquals( encodeCbor(Object.fromEntries(entries)), new Uint8Array([ 0b101_00000 + pairs, ...entries.map(([k, v]) => [...encodeCbor(k), ...encodeCbor(v)]) .flat(), ]), ); pairs = random(24, 2 ** 8); entries = new Array(pairs).fill(0).map((_, i) => [i.toString(), i]); assertEquals( encodeCbor(Object.fromEntries(entries)), new Uint8Array([ 0b101_11000, pairs, ...entries.map(([k, v]) => [...encodeCbor(k), ...encodeCbor(v)]) .flat(), ]), ); pairs = random(2 ** 8, 2 ** 16); entries = new Array(pairs).fill(0).map((_, i) => [i.toString(), i]); assertEquals( encodeCbor(Object.fromEntries(entries)), new Uint8Array([ 0b101_11001, pairs >> 8 & 0xFF, pairs & 0xFF, ...entries.map(([k, v]) => [...encodeCbor(k), ...encodeCbor(v)]) .flat(), ]), ); // Can't test the next two bracket up due to JavaScript limitations. }); Deno.test("encodeCbor() encoding CborTag()", () => { const bytes = new Uint8Array(random(0, 24)).map((_) => random(0, 256)); assertEquals( encodeCbor(new CborTag(2, bytes)), new Uint8Array([0b110_00010, 0b010_00000 + bytes.length, ...bytes]), ); }); Deno.test("encodeCbor() rejecting numbers as Uint", () => { const num = 2 ** 65; assertThrows( () => { encodeCbor(num); }, RangeError, `Cannot encode number: It (${num}) exceeds 2 ** 64 - 1`, ); }); Deno.test("encodeCbor() rejecting numbers as Int", () => { const num = -(2 ** 65); assertThrows( () => { encodeCbor(num); }, RangeError, `Cannot encode number: It (${num}) exceeds -2 ** 64 - 1`, ); }); Deno.test("encodeCbor() rejecting bigints as Uint", () => { const num = 2n ** 65n; assertThrows( () => { encodeCbor(num); }, RangeError, `Cannot encode bigint: It (${num}) exceeds 2 ** 64 - 1`, ); }); Deno.test("encodeCbor() rejecting bigints as Int", () => { const num = -(2n ** 65n); assertThrows( () => { encodeCbor(num); }, RangeError, `Cannot encode bigint: It (${num}) exceeds -2 ** 64 - 1`, ); }); Deno.test("encodeCbor() rejecting CborTag()", () => { let num = -5; assertThrows( () => { encodeCbor( new CborTag( num, new Uint8Array(random(0, 24)).map((_) => random(0, 256)), ), ); }, RangeError, `Cannot encode Tag Item: Tag Number (${num}) is less than zero`, ); num = 2 ** 65; assertThrows( () => { encodeCbor( new CborTag( num, new Uint8Array(random(0, 24)).map((_) => random(0, 256)), ), ); }, RangeError, `Cannot encode Tag Item: Tag Number (${num}) exceeds 2 ** 64 - 1`, ); });