cbor-object
Version:
CBOR: deterministic encoder/decoder, diagnostic notation encoder/decoder, and utilities
1,086 lines (956 loc) • 32.5 kB
JavaScript
// Testing CBOR.js API
import CBOR from 'cbor-object';
let failures = 0;
let test = 0;
let name = '';
function assertTrue(text, bool) {
if (!bool) throw Error("Assertion: " + text);
}
function assertFalse(text, bool) {
if (bool) throw Error("Assertion: " + text);
}
function success() {
console.log('Test ' + name + ' was successful');
}
let TESTS=[
{name:'base64.js',
file:String.raw`// Testing the B64U/B64 converters
let bin = new Uint8Array(256);
for (let i = 0; i < bin.length; i++) {
bin[i] = i;
}
let b64U = CBOR.toBase64Url(bin);
assertFalse("cmp1", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64U)));
// This is what "btoa" returns for bin:
let b64 = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissL\
S4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY\
2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYm\
ZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz\
9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==';
// fromBase64Url is "permissive" and takes Base64 with padding as well...
assertFalse("cmp2", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64)));
assertFalse("cmp3", CBOR.compareArrays(CBOR.fromBase64Url('oQVkZGF0YQ'),
CBOR.fromHex('a1056464617461')));
// Zero data is compliant
assertFalse("cmp4", CBOR.compareArrays(CBOR.fromBase64Url(''), new Uint8Array()));
assertTrue("cmp4", CBOR.toBase64Url(new Uint8Array()) == "");
success();
`}
,
{name:'check-for-unread.js',
file:String.raw`// Testing the "checkForUnread()" feature
function oneTurn(create, access, errorString) {
let res = eval(create);
try {
res.checkForUnread();
if (errorString !== null) {
throw Error("no way");
}
} catch (error) {
if (!error.toString().includes('never read')) {
throw error;
}
}
try {
eval(access);
res.checkForUnread();
assertFalse("cfu1", errorString);
} catch (error) {
assertTrue("cfu2=" + error, errorString);
if (!error.toString().includes(errorString)) {
throw error;
}
}
eval(create).scan().checkForUnread();
}
oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
"res.get(0).get(CBOR.Int(1)).getString()");
oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
"res",
"Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
"res.get(0).get(CBOR.Int(1))",
"Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
oneTurn("CBOR.Array().add(CBOR.Map())",
"res",
"Array element of type=CBOR.Map with value={} was never read");
// Empty Map => nothing to read
oneTurn("CBOR.Array().add(CBOR.Map())",
"res.get(0)",
"Array element of type=CBOR.Map with value={} was never read");
oneTurn("CBOR.Array().add(CBOR.Map())",
"res.get(0).scan()",
null);
// Empty Array => nothing to read
oneTurn("CBOR.Array()",
"res",
"Data of type=CBOR.Array with value=[] was never read");
oneTurn("CBOR.Array()",
"res.scan()",
null);
oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
"res.get().get(CBOR.Int(1)).getString()");
oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
"res.get()",
"Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
oneTurn("CBOR.Tag(8n, CBOR.Map())",
"res.get()",
"Tagged object 8 of type=CBOR.Map with value={} was never read");
oneTurn("CBOR.Simple(8)",
"res",
"Data of type=CBOR.Simple with value=simple(8) was never read");
oneTurn("CBOR.Simple(8)",
"res.getSimple()",
null);
oneTurn("CBOR.Tag(8n, CBOR.Map())",
"res.get().scan()",
null);
// Date time specials
oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
"res.get()",
"Tagged object 0 of type=CBOR.String with value=\"2025-02-20T14:09:08Z\" was never read");
oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
"res.get().getString()",
null);
oneTurn("CBOR.Tag(8n, CBOR.Int(2))",
"res.get()",
"Tagged object 8 of type=CBOR.Int with value=2 was never read");
oneTurn("CBOR.Int(1)",
"res.getInt()");
success();
`}
,
{name:'clone.js',
file:String.raw`// Testing the "clone()" and "equals() methods
let object = CBOR.Map()
.set(CBOR.Int(2), CBOR.Array()
.add(CBOR.Boolean(false)));
assertTrue("clone+equals", object.equals(object.clone()));
let copy = object.clone().set(CBOR.Int(1), CBOR.String("Hi"));
assertFalse("copy+equals+clone", copy.equals(object));
success();
`}
,
{name:'cotx.js',
file:String.raw`// Testing the COTX identifier
function oneTurn(hex, dn, ok) {
try {
let object = CBOR.decode(CBOR.fromHex(hex));
assertTrue("Should not execute", ok);
if (object.toString() != dn.toString() || !object.equals(CBOR.decode(object.encode()))) {
throw Error("non match:" + dn + " " + object.toString());
}
} catch (error) {
if (ok) console.log(error.toString());
assertFalse("Must succeed", ok);
}
}
oneTurn('d903f2623737', '1010("77")', false);
oneTurn('d903f281623737', '1010(["77"])', false);
oneTurn('d903f28206623737', '1010([6, "77"])', false);
oneTurn('d903f28262373707', '1010(["77", 7])', true);
success();
`}
,
{name:'diagnostic.js',
file:String.raw`// Testing "diagnostic notation"
function oneTurn(cborText, ok, compareWithOrNull) {
try {
let compareText = compareWithOrNull ? compareWithOrNull : cborText;
let result = CBOR.diagDecode(cborText);
assertTrue("Should not", ok);
let sequence = CBOR.diagDecodeSequence(cborText);
if (result.toString() != compareText) {
throw Error("input:\n" + cborText + "\nresult:\n" + result);
}
assertTrue("seq", sequence.length == 1);
if (sequence[0].toString() != compareText) {
throw Error("input:\n" + cborText + "\nresult:\n" + result);
}
} catch (error) {
assertFalse("Err: " + error, ok);
}
}
function oneBinaryTurn(diag, hex) {
assertTrue("bin", CBOR.toHex(CBOR.diagDecode(diag).encode()) == hex);
}
oneTurn("2", true, null);
oneTurn("2.0", true, null);
oneTurn("123456789012345678901234567890", true, null);
oneTurn("Infinity", true, null);
oneTurn("-Infinity", true, null);
oneTurn("NaN", true, null);
oneTurn("0.0", true, null);
oneTurn("-0.0", true, null);
oneTurn('{\n 4: "hi"\n}', true, null);
oneTurn('[4, true, false, null]', true, null);
oneTurn('"next\nline\r\\\ncont\r\nk"', true, '"next\\nline\\ncont\\nk"');
oneTurn('{1:<< 5 , 7 >>}', true, "{\n 1: h'0507'\n}");
oneTurn('<<[3.0]>>', true, "h'81f94200'");
oneTurn('0b100_000000001', true, "2049");
oneTurn('4.0e+500', false, null);
oneTurn('4.0e+5', true, "400000.0");
oneTurn('"missing', false, null);
oneTurn('simple(21)', true, 'true');
oneTurn('simple(59)', true, 'simple(59)');
oneBinaryTurn('"\\ud800\\udd51"', "64f0908591");
oneBinaryTurn("'\\u20ac'", "43e282ac");
oneBinaryTurn('"\\"\\\\\\b\\f\\n\\r\\t"', "67225c080c0a0d09");
let cborObject = CBOR.decode(CBOR.fromHex('a20169746578740a6e6578740284fa3380000147a10564646\
17461a1f5f4c074323032332d30362d30325430373a35333a31395a'));
let cborText = '{\n 1: "text\\nnext",\n 2: [5.960465188081798e-8, h\'a1056464617461\', {\n\
true: false\n }, 0("2023-06-02T07:53:19Z")]\n}';
assertTrue("pretty", cborObject.toDiag(true) == cborText);
assertTrue("oneline", cborObject.toDiag(false) ==
cborText.replaceAll('\n', '').replaceAll(' ',''));
assertTrue("parse", CBOR.diagDecode(cborText).equals(cborObject));
let sequence = CBOR.diagDecodeSequence('45,{4:7}');
assertTrue("seq2", sequence.length == 2);
assertTrue("seq3", sequence[0].getInt() == 45);
assertTrue("seq4", sequence[1].equals(CBOR.Map().set(CBOR.Int(4),CBOR.Int(7))));
success();
`}
,
{name:'float.js',
file:String.raw`// Test program for floating-point "edge cases"
function overflow(cborObject, length) {
let test = 'cborObject.getFloat' + length + '()';
try {
eval(test);
assertTrue("Should fail", false);
} catch (error) {
if (!error.toString().includes('Value out of range:')) {
throw error;
}
}
}
function oneTurn(valueText, expected, invalidFloats) {
let decoder = CBOR.initDecoder(CBOR.fromHex(expected), CBOR.REJECT_INVALID_FLOATS);
let value = Number(valueText);
let text = valueText;
while (text.length < 25) {
text += ' ';
}
let cbor = CBOR.Float(value).encode();
let got = CBOR.toHex(cbor);
if (got != expected) {
got = '***=' + got;
} else {
got = '';
}
let decodedValue = CBOR.decode(cbor);
if (valueText == 'NaN') {
decodedValue.getFloat16();
if (decodedValue.length > 2) {
throw Error("Failed decoding: " + value);
}
} else if (expected.length == 6) {
if (decodedValue.getFloat16() != value ||
decodedValue.getFloat32() != value || decodedValue.getFloat64() != value) {
throw Error("Failed decoding: " + value);
}
} else if (expected.length <= 10) {
if (decodedValue.getFloat32() != value || decodedValue.getFloat64() != value) {
throw Error("Failed decoding: " + value);
}
overflow(decodedValue, "16");
} else {
overflow(decodedValue, "16");
overflow(decodedValue, "32");
}
if (decodedValue.toString() != valueText) {
throw Error("Failed encoding: " + valueText + " " + decodedValue.toString());
}
while (expected.length < 20) {
expected += ' ';
}
if (got.length) {
throw Error(text + expected + got);
}
try {
decoder.decodeWithOptions();
assertFalse('Should not execute', invalidFloats);
} catch (error) {
assertTrue("Decode ME1", error.toString().includes('"NaN" and "Infinity"'));
}
}
oneTurn('0.0', 'f90000');
oneTurn('-0.0', 'f98000');
oneTurn('NaN', 'f97e00', true);
oneTurn('Infinity', 'f97c00', true);
oneTurn('-Infinity', 'f9fc00', true);
oneTurn('0.0000610649585723877', 'fa38801000');
oneTurn('10.559998512268066', 'fa4128f5c1');
oneTurn('65472.0', 'f97bfe');
oneTurn('65472.00390625', 'fa477fc001');
oneTurn('65503.0', 'fa477fdf00');
oneTurn('65504.0', 'f97bff');
oneTurn('65504.00000000001', 'fb40effc0000000001');
oneTurn('65504.00390625', 'fa477fe001');
oneTurn('65504.5', 'fa477fe080');
oneTurn('65505.0', 'fa477fe100');
oneTurn('131008.0', 'fa47ffe000');
oneTurn('-5.960464477539062e-8', 'fbbe6fffffffffffff');
oneTurn('-5.960464477539063e-8', 'f98001');
oneTurn('-5.960464477539064e-8', 'fbbe70000000000001');
oneTurn('-5.960465188081798e-8', 'fab3800001');
oneTurn('-5.963374860584736e-8', 'fab3801000');
oneTurn('-5.966285243630409e-8', 'fab3802000');
oneTurn('-8.940696716308594e-8', 'fab3c00000');
oneTurn('-0.00006097555160522461', 'f983ff');
oneTurn('-0.000060975551605224616', 'fbbf0ff80000000001');
oneTurn('-0.000060975555243203416', 'fab87fc001');
oneTurn('0.00006103515625', 'f90400');
oneTurn('0.00006103515625005551', 'fb3f10000000001000');
oneTurn('1.4012984643248169e-45', 'fb369fffffffffffff');
oneTurn('1.401298464324817e-45', 'fa00000001');
oneTurn('1.4012984643248174e-45', 'fb36a0000000000001');
oneTurn('1.4012986313726115e-45', 'fb36a0000020000000');
oneTurn('1.1754942106924411e-38', 'fa007fffff');
oneTurn('3.4028234663852886e+38', 'fa7f7fffff');
oneTurn('3.402823466385289e+38', 'fb47efffffe0000001');
oneTurn('0.00006109476089477539', 'f90401');
oneTurn('7.52316384526264e-37', 'fa03800000');
oneTurn('1.1754943508222875e-38', 'fa00800000');
oneTurn('5.0e-324', 'fb0000000000000001');
oneTurn('-1.7976931348623157e+308', 'fbffefffffffffffff');
success();
`}
,
{name:'hex.js',
file:String.raw`// Test of "hex" utility methods
const hex = '0123456789abcdefABCDEF';
let bin = CBOR.fromHex(hex);
let cnv = CBOR.toHex(bin);
assertFalse("hex", CBOR.compareArrays(bin, CBOR.fromHex(cnv)));
let ref = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef]);
assertFalse("bin", CBOR.compareArrays(bin, ref));
try {
CBOR.fromHex("AAA");
throw Error("should not");
} catch (error) {
if (!error.toString().includes("Unev")) {
console.log(error);
}
}
try {
CBOR.fromHex("Ag");
throw Error("should not");
} catch (error) {
if (!error.toString().includes("Bad hex")) {
console.log(error);
}
}
// Zero hex is accepted as well...
assertFalse("zero", CBOR.compareArrays(CBOR.fromHex(''), new Uint8Array()));
success();
`}
,
{name:'integer.js',
file:String.raw`// Test program for integer "edge cases"
function oneTurn(value, expected) {
let text = value.toString();
while (text.length < 25) {
text += ' ';
}
let cbor = CBOR.BigInt(value).encode();
let got = CBOR.toHex(cbor);
if (got != expected) {
got = '***=' + got;
} else {
got = '';
}
assertTrue("Failed decoding: " + value, CBOR.decode(cbor).getBigInt() == value);
while (expected.length < 20) {
expected += ' ';
}
if (got.length) {
fail(text + expected + got);
}
}
// -0 is treated as 0 for integers
assertTrue("minus-0", CBOR.toHex(CBOR.Int(-0).encode()) == "00");
oneTurn(0n, '00');
oneTurn(-1n, '20');
oneTurn(255n, '18ff');
oneTurn(256n, '190100');
oneTurn(-256n, '38ff');
oneTurn(-257n, '390100');
oneTurn(1099511627775n, '1b000000ffffffffff');
oneTurn(18446744073709551615n, '1bffffffffffffffff');
oneTurn(18446744073709551616n, 'c249010000000000000000');
oneTurn(-18446744073709551616n, '3bffffffffffffffff');
oneTurn(-18446744073709551617n, 'c349010000000000000000');
try {
CBOR.Int(1.1);
fail("Should not");
} catch (error) {
assertTrue("msg1", error.toString().includes("Invalid integer: 1.1"));
}
try {
CBOR.Int(Number.MAX_SAFE_INTEGER + 1);
fail("Should not");
} catch (error) {
assertTrue("msg1", error.toString().includes("Invalid integer: " + (Number.MAX_SAFE_INTEGER + 1)));
}
try {
CBOR.Int("10");
fail("Should not");
} catch (error) {
assertTrue("msg2", error.toString().includes("Argument is not a 'number'"));
}
try {
CBOR.BigInt("10");
fail("Should not");
} catch (error) {
assertTrue("msg3", error.toString().includes("Argument is not a 'bigint'"));
}
try {
CBOR.BigInt(1n, 7);
fail("Should not");
} catch (error) {
assertTrue("msg4", error.toString().includes("CBOR.BigInt expects 1 argument(s)"));
}
try {
CBOR.Int(1, 7);
fail("Should not");
} catch (error) {
assertTrue("msg4", error.toString().includes("CBOR.Int expects 1 argument(s)"));
}
success();
`}
,
{name:'int-ranges.js',
file:String.raw`// Testing range-constrained integers
function goodRun(method, value) {
let bigFlag = method.indexOf("64") > 0;
let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
let test = 'assertTrue("good", wrapper.' + method + '() == ' + (bigFlag ? value + 'n' : Number(value)) + ')';
eval(test);
}
function badRun(method, value) {
let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
let test = 'wrapper.' + method + '()';
try {
eval(test);
assertTrue("Should fail", false);
} catch (error) {
if (!error.toString().includes('Value out of range:')) {
throw error;
}
}
}
function innerTurn(method, signed, size) {
let min = signed ? -(1n << BigInt(size) - 1n) : 0n;
let max = signed ? (1n << BigInt(size) - 1n) - 1n : (1n << BigInt(size)) - 1n;
goodRun(method, min);
goodRun(method, max);
goodRun(method, 10n);
badRun(method, max + 1n);
badRun(method, min - 1n);
}
function oneTurn(size) {
innerTurn("getInt" + size, true, size);
innerTurn("getUint" + size, false, size);
}
oneTurn(8);
oneTurn(16);
oneTurn(32);
oneTurn(64);
success();
`}
,
{name:'maps.js',
file:String.raw`// Testing map operations
let map = CBOR.Map()
.set(CBOR.Int(3), CBOR.String("three"))
.set(CBOR.Int(4), CBOR.String("four"));
assertTrue("size-0", map.length == 2);
let keys = map.getKeys();
assertTrue("size-1", keys.length == 2);
assertTrue("get-0", map.get(keys[0]).getString() == "three");
assertTrue("get-1", map.get(keys[1]).getString() == "four");
assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
assertTrue("size-2", map.length == 1);
assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
map = map.merge(
CBOR.Map().set(CBOR.Int(1), CBOR.String("hi")).set(CBOR.Int(5), CBOR.String("yeah")));
assertTrue("size-3", map.length == 3);
assertTrue("merge-0", map.get(CBOR.Int(1)).getString() == "hi");
assertTrue("upd-0", map.update(CBOR.Int(1), CBOR.BigInt(-8n), true).getString() == "hi");
assertTrue("upd-1", map.get(CBOR.Int(1)).getBigInt() == -8n);
assertTrue("upd-2", map.update(CBOR.Int(10), CBOR.BigInt(-8n), false) == null);
assertTrue("upd-3", map.get(CBOR.Int(10)).getBigInt() == -8n);
function badKey(js) {
try {
eval(js);
fail("Must fail!");
} catch (error) {
if (!error.toString().includes('Map key')) {
throw error;
}
}
}
let immutableKey1 = CBOR.Array();
let immutableKey2 = CBOR.Array();
CBOR.Map().set(immutableKey1, CBOR.Int(4));
badKey("immutableKey1.add(CBOR.Int(6))");
let mutableValue = CBOR.Array();
CBOR.Map().set(CBOR.Int(5), mutableValue);
mutableValue.add(CBOR.Map());
CBOR.Map().set(CBOR.Array().add(immutableKey2), CBOR.Int(5));
badKey("immutableKey2.add(CBOR.Int(6))");
success();
`}
,
{name:'arrays.js',
file:String.raw`// Testing array operations
let array = CBOR.Array()
.add(CBOR.String("three"))
.add(CBOR.String("four"));
assertTrue("size-0", array.length == 2);
assertTrue("get-0", array.get(0).getString() == "three");
assertTrue("get-1", array.get(1).getString() == "four");
let arrayElements = array.toArray();
assertTrue("size-1", arrayElements.length == 2);
assertTrue("arr-0", arrayElements[0].getString() == "three");
assertTrue("arr-1", arrayElements[1].getString() == "four");
assertTrue("upd-1", array.update(1, CBOR.Int(1)).getString() == "four");
assertTrue("upd-2", array.get(1).getInt8() == 1);
assertTrue("size-1", array.length == 2);
assertTrue("upd-3", array.get(0).getString() == "three");
/*
assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
assertTrue("size-2", map.length == 1);
assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
*/
success();
`}
,
{name:'miscellaneous.js',
file:String.raw`// miscellaneous tests
let bin = new Uint8Array([0xa5, 0x01, 0xd9, 0x01, 0xf4, 0x81, 0x18, 0x2d, 0x02, 0xf9, 0x80, 0x10,
0x04, 0x64, 0x53, 0x75, 0x72, 0x65, 0x05, 0xa2, 0x08, 0x69, 0x59, 0x65,
0x0a, 0x01, 0x61, 0x68, 0xe2, 0x82, 0xac, 0x09, 0x85, 0x66, 0x42, 0x79,
0x74, 0x65, 0x73, 0x21, 0x45, 0x01, 0x02, 0x03, 0x04, 0x05, 0xf5, 0xf4,
0xf6, 0x06, 0xc2, 0x4b, 0x66, 0x1e, 0xfd, 0xf2, 0xe3, 0xb1, 0x9f, 0x7c,
0x04, 0x5f, 0x15]);
let cbor = CBOR.Map()
.set(CBOR.Int(5),
CBOR.Map()
.set(CBOR.Int(8), CBOR.String("Ye\n\u0001ah€"))
.set(CBOR.Int(9),
CBOR.Array()
.add(CBOR.String("Bytes!"))
.add(CBOR.Bytes(new Uint8Array([1,2,3,4,5])))
.add(CBOR.Boolean(true))
.add(CBOR.Boolean(false))
.add(CBOR.Null())))
.set(CBOR.Int(4), CBOR.String("Sure"))
.set(CBOR.Int(2), CBOR.Float(-9.5367431640625e-7))
.set(CBOR.Int(6), CBOR.BigInt(123456789123456789123456789n))
.set(CBOR.Int(1), CBOR.Tag(500n, CBOR.Array().add(CBOR.Int(45)))).encode();
assertFalse("cmp1", CBOR.compareArrays(bin, cbor));
let array = CBOR.decode(cbor).get(CBOR.Int(5)).get(CBOR.Int(9));
assertTrue("bool1", array.get(2).getBoolean());
assertFalse("bool1", array.get(3).getBoolean());
assertFalse("null1", array.get(3).isNull());
assertTrue("null2", array.get(4).isNull());
assertFalse("cmp2", CBOR.compareArrays(CBOR.diagDecode(CBOR.decode(cbor).toString()).encode(), bin));
assertTrue("version", CBOR.version == "1.0.12");
success();
`}
,
{name:'nondeterministic.js',
file:String.raw`// Testing "deterministic" code checks
function oneTurn(hex, dn) {
try {
CBOR.decode(CBOR.fromHex(hex));
throw Error("Should not fail on: " + dn);
} catch (error) {
if (!error.toString().includes("Non-d")) {
throw error;
}
}
let object = CBOR.initDecoder(CBOR.fromHex(hex),
dn.includes("{") ? CBOR.LENIENT_MAP_DECODING : CBOR.LENIENT_NUMBER_DECODING).decodeWithOptions();
if (object.toString() != dn || !object.equals(CBOR.decode(object.encode()))) {
throw Error("non match:" + dn);
}
}
oneTurn('1900ff', '255');
oneTurn('1817', '23');
oneTurn('A2026374776F01636F6E65', '{\n 1: "one",\n 2: "two"\n}');
oneTurn('FB7FF8000000000000', 'NaN');
oneTurn('FA7FC00000', 'NaN');
oneTurn('FB3ff0000000000000', '1.0');
oneTurn('c2480100000000000000', '72057594037927936');
oneTurn('c24900ffffffffffffffff', '18446744073709551615');
oneTurn('c240', '0');
oneTurn('f97e01', 'NaN');
oneTurn('c240', '0');
// This one is actually deterministic...
try {
oneTurn('fa7f7fffff', '3.4028234663852886e+38');
} catch (error) {
if (!error.toString().includes('Should not')) {
throw error;
}
}
success();
`}
,
{name:'out-of-range.js',
file:String.raw`// Number overflow tests.
const TOO_BIG = Number.MAX_SAFE_INTEGER + 1;
const IN_RANGE = Number.MAX_SAFE_INTEGER;
try {
CBOR.Int(TOO_BIG);
throw Error('Should not');
} catch (error) {
if (error.toString().includes('Should not')) {
throw error;
}
}
let cbor = CBOR.BigInt(BigInt(TOO_BIG)).encode();
try {
CBOR.decode(cbor).getInt();
throw Error('Should not');
} catch (error) {
if (error.toString().includes('Should not')) {
throw error;
}
}
assertTrue("big", BigInt(TOO_BIG) == CBOR.decode(cbor).getBigInt());
cbor = CBOR.Int(IN_RANGE).encode();
assertTrue("R0", CBOR.decode(cbor).getInt() == IN_RANGE);
cbor = CBOR.Int(-IN_RANGE).encode();
assertTrue("R0", CBOR.decode(cbor).getInt() == -IN_RANGE);
success();
`}
,
{name:'sequence.js',
file:String.raw`// Testing the "sequence" option
let cbor = new Uint8Array([0x05, 0xa1, 0x05, 0x42, 0x6a, 0x6a])
try {
CBOR.decode(cbor);
throw Error("Should not");
} catch (error) {
if (!error.toString().includes('Unexpected')) console.log(error);
}
let decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
let total = new Uint8Array();
let object;
while (object = decoder.decodeWithOptions()) {
total = CBOR.addArrays(total, object.encode());
}
assertFalse("Comp", CBOR.compareArrays(total, cbor));
assertTrue("Comp2", total.length == decoder.getByteCount());
decoder = CBOR.initDecoder(new Uint8Array(), CBOR.SEQUENCE_MODE);
assertFalse("Comp3", decoder.decodeWithOptions());
assertTrue("Comp4", decoder.getByteCount() == 0);
success();
`}
,
{name:'tags.js',
file:String.raw`// Testing "tag"
let object = CBOR.Array().add(CBOR.String("https://example.com/myobject")).add(CBOR.Int(6));
let cbor = CBOR.Tag(CBOR.Tag.TAG_COTX, object).encode();
let tag = CBOR.decode(cbor);
assertTrue("t3", tag.getTagNumber()== CBOR.Tag.TAG_COTX);
assertTrue("t3.1", object.equals(tag.get()));
tag = CBOR.decode(cbor);
assertTrue("t3.2", object.equals(tag.get()));
cbor = CBOR.Tag(0xf0123456789abcden, object).encode();
assertTrue("t14", CBOR.decode(cbor).getTagNumber()== 0xf0123456789abcden);
assertTrue("t5", CBOR.toHex(cbor) ==
"dbf0123456789abcde82781c68747470733a2f2f6578616d706c652e636f6d2f6d796f626a65637406");
tag = CBOR.Tag(5n, CBOR.String("hi"));
assertTrue("u1", tag.update(CBOR.Int(6)).getString() == "hi");
assertTrue("u2", tag.get().getInt() == 6);
[-1n, 0x10000000000000000n].forEach(tagNumber => {
try {
CBOR.Tag(tagNumber, CBOR.String("any"));
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("out of range")) {
throw error;
}
}
});
[].forEach(tagNumber => {
try {
CBOR.Tag(tagNumber, CBOR.String("any"));
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("'bigint'")) {
throw error;
}
}
});
[].forEach(tagNumber => {
try {
CBOR.Tag(tagNumber, CBOR.Boolean(true));
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("got: CBOR.Boolean")) {
throw error;
}
}
});
success();
`}
,
{name:'simple.js',
file:String.raw`// Testing "simple"
[-1, 256, 24, 31].forEach(value => {
try {
CBOR.Simple(value);
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("out of range")) {
throw error;
}
}
});
function oneTurn(value, hex) {
let s = CBOR.Simple(value);
let s2 = CBOR.decode(s.encode());
assertTrue("v", s.getSimple() == value);
assertTrue("v2", s2.getSimple() == value);
assertTrue("b", CBOR.toHex(s2.encode()) == hex);
}
oneTurn(0, "e0");
oneTurn(23, "f7");
oneTurn(32, "f820");
oneTurn(255, "f8ff");
success();
`}
,
{name:'dates.js',
file:String.raw`// Testing date methods
function oneDateTime(epoch, isoString) {
assertTrue("date1", CBOR.String(isoString).getDateTime().getTime() == epoch);
let cbor = CBOR.decode(CBOR.String(isoString).encode());
assertTrue("date2", cbor.getDateTime().getTime() == epoch);
assertTrue("date3", CBOR.Tag(0n, CBOR.String(isoString))
.getDateTime().getTime() == epoch);
assertTrue("date3", CBOR.Tag(1n, CBOR.Int(epoch / 1000))
.getEpochTime().getTime() == epoch);
assertTrue("date31", CBOR.Int(epoch / 1000)
.getEpochTime().getTime() == epoch);
assertTrue("date4", CBOR.Tag(1n, CBOR.Float(epoch / 1000))
.getEpochTime().getTime() == epoch);
assertTrue("date5", CBOR.Tag(1n, CBOR.Float((epoch + 3.0) / 1000))
.getEpochTime().getTime() == epoch + 3);
assertTrue("date5", CBOR.Float((epoch - 3.0) / 1000)
.getEpochTime().getTime() == epoch - 3);
}
function badDate(hexBor, err) {
try {
CBOR.decode(CBOR.fromHex(hexBor));
fail("must not");
} catch (error) {
if (!error.toString().includes(err)) {
throw error;
}
}
}
function oneEpoch(hexBor, epoch, err) {
assertTrue("epoch1", CBOR.decode(CBOR.fromHex(hexBor))
.getEpochTime().getTime() == epoch * 1000);
let date = CBOR.decode(CBOR.fromHex(hexBor));
try {
date.checkForUnread();
fail("must not");
} catch (error) {
if (!error.toString().includes(err)) {
throw error;
}
}
date.getEpochTime();
date.checkForUnread();
}
oneDateTime(1740060548000, "2025-02-20T14:09:08+00:00");
oneDateTime(1740060548000, "2025-02-20T14:09:08Z");
oneDateTime(1740060548000, "2025-02-20T15:09:08+01:00");
oneDateTime(1740060548000, "2025-02-20T15:39:08+01:30");
oneDateTime(1740060548000, "2025-02-20T12:09:08-02:00");
oneDateTime(1740060548000, "2025-02-20T11:39:08-02:30");
badDate("c001", "got: CBOR.Int");
badDate("c06135", "Invalid ISO date string: 5");
badDate("c16135", "got: CBOR.String");
oneEpoch("FB41D9EDCDE113645A", 1740060548.303, "Data of type=CBOR.Float with value=174");
oneEpoch("c1FB41D9EDCDE113645A", 1740060548.303, "Tagged object 1 of type=CBOR.Float");
oneEpoch("00", 0, "Data of type=CBOR.Int");
try {
// Z or -+local offset needed.
CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43"));
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("ISO")) {
throw error;
}
}
try {
// 24 hour is incorrect.
CBOR.Tag(0n, CBOR.String("2023-06-22T24:01:43Z"));
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("ISO")) {
throw error;
}
}
success();`}
,
{name:'dynamic.js',
file:String.raw`// dynamic tests
assertTrue("dyn", CBOR.Map().setDynamic(wr =>
wr.set(CBOR.Int(1), CBOR.Boolean(true))).get(CBOR.Int(1)).getBoolean());
let option = "on";
assertTrue("dyn", CBOR.Map().setDynamic(wr => {
if (option) {
wr.set(CBOR.Int(1), CBOR.String(option));
}
return wr;
}).get(CBOR.Int(1)).getString() == option);
function lambda(wr) {
wr.set(CBOR.Int(1), CBOR.Boolean(true));
return wr;
}
assertTrue("dyn", CBOR.Map().setDynamic(lambda).get(CBOR.Int(1)).getBoolean());
success();
`}
,
{name:'utf8.js',
file:String.raw`// Test of "utf8" converters
function utf8EncoderTest(string, ok) {
try {
CBOR.String(string).encode();
assertTrue("enc", ok);
} catch (error) {
assertFalse("No good", ok);
}
}
function utf8DecoderTest(hex, ok) {
let cbor = CBOR.fromHex(hex);
let roundTrip;
try {
roundTrip = CBOR.decode(cbor).encode();
} catch (error) {
assertFalse("No good", ok);
return;
}
assertTrue("OK", ok);
assertFalse("Conv", CBOR.compareArrays(cbor, roundTrip));
}
utf8DecoderTest("62c328", false);
utf8DecoderTest("64f0288cbc", false);
utf8DecoderTest("64f0908cbc", true);
utf8EncoderTest("Hi", true)
utf8EncoderTest("\uD83D", false);
utf8EncoderTest("\uD83D\uDE2D", true);
success();
`}
,
{name:'xyz-encoder.js',
file:String.raw`// Simple "encoder" API
class XYZEncoder {
static COUNTER = CBOR.Int(1);
static TEMPERATURE = CBOR.Int(2);
static GREETING = CBOR.Int(3);
constructor() {
this.
}
setCounter = function(intVal) {
this.
return this;
}
setTemperature = function(floatVal) {
this.
return this;
}
setGreeting = function(stringVal) {
this.
return this;
}
encode = function() {
assertTrue("incomplete", this.
return this.
}
}
let cbor = new XYZEncoder()
.setCounter(2)
.setGreeting('Hi!')
.setTemperature(53.0001)
.encode();
assertTrue("bad code", CBOR.toHex(cbor) == 'a3010202fb404a800346dc5d640363486921');
success();
`}
,
{name:'xyz-decoder.js',
file:String.raw`// Simple "decoder" API
class XYZDecoder {
static COUNTER = CBOR.Int(1);
static TEMPERATURE = CBOR.Int(2);
static GREETING = CBOR.Int(3);
constructor(cbor) {
// There MUST be exactly three key/value pairs.
// CBOR data items are type-checked as well.
let map = CBOR.decode(cbor);
// If the top-level object is not a CBOR map, the next
// JavaScript line will throw an exception because there is
// only one get-method that has a CBOR wrapper as input parameter.
this.
this.
this.
// We got more than we asked for?
map.checkForUnread();
}
get counter() {
return this.
}
get temperature() {
return this.
}
get greeting() {
return this.
}
}
let cbor = CBOR.fromHex('a3010202fb404a800346dc5d640363486921');
let xyz = new XYZDecoder(cbor);
assertTrue("counter", xyz.counter == 2);
assertTrue("temperature", xyz.temperature == 53.0001);
assertTrue("greeting", xyz.greeting == 'Hi!');
success();
`}
];
function runTest() {
test = 0;
failures = 0;
for (let test = 0; test < TESTS.length; test++) {
name = TESTS[test].name;
try {
eval(TESTS[test].file);
} catch (error) {
failures++;
console.log(name + " FAILED: " + error);
}
}
if (failures) {
console.log('There were ' + failures + ' errors');
} else {
console.log('PASSED');
}
}
runTest();