std/cbor/encode_cbor_test.ts

408 lines
10 KiB
TypeScript

// 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`,
);
});