cbor-object
Version:
CBOR: deterministic encoder/decoder, diagnostic notation encoder/decoder, and utilities
1,500 lines (1,331 loc) • 50 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.getInt32()");
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.fromDiagnostic(cborText);
assertTrue("Should not", ok);
let sequence = CBOR.fromDiagnosticSeq(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.fromDiagnostic(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: [\n' +
' 5.960465188081798e-8,\n' +
' h\'a1056464617461\',\n' +
' {\n' +
' true: false\n' +
' },\n' +
' 0("2023-06-02T07:53:19Z")\n' +
' ]\n' +
'}';
assertTrue("pretty", cborObject.toDiagnostic(true) == cborText);
assertTrue("oneline", cborObject.toDiagnostic(false) ==
cborText.replaceAll('\n', '').replaceAll(' ',''));
assertTrue("parse", CBOR.fromDiagnostic(cborText).equals(cborObject));
let sequence = CBOR.fromDiagnosticSeq('45,{4:7}');
assertTrue("seq2", sequence.length == 2);
assertTrue("seq3", sequence[0].getInt32() == 45);
assertTrue("seq4", sequence[1].equals(CBOR.Map().set(CBOR.Int(4),CBOR.Int(7))));
try {
CBOR.fromDiagnostic("float'000000'");
fail("bugf");
} catch (error) {
assertTrue("fp", error.toString().includes('floating-point'));
}
success();
`}
,
{name:'float.js',
file:String.raw`// Test program for floating-point "edge cases"
function overflow(decodedValue, length) {
let test = 'decodedValue.getFloat' + length + '()';
try {
eval(test);
assertTrue("Should fail", false);
} catch (error) {
if (!error.toString().includes('Value out of range for "Float')) {
throw error;
}
}
}
function shouldpass(decodedValue, value, length, valueText) {
assertTrue("p1", decodedValue.toString() == valueText);
let test = 'decodedValue.getFloat' + length + '()';
let float = eval(test);
assertTrue("p2", float == value);
if (length == "64") {
test = 'decodedValue.getExtendedFloat' + length + '()';
float = eval(test);
assertTrue("p3", float == value);
}
}
function oneTurn(valueText, expected) {
let value = Number(valueText);
if (Number.isFinite(value)) {
try {
CBOR.NonFinite(value);
fail("f1");
} catch (error) {
assertTrue("f2", error.toString().includes("bigint"));
}
let cbor = CBOR.Float(value).encode();
assertTrue("f3", CBOR.toHex(cbor) == expected);
let decodedValue = CBOR.decode(cbor);
switch (cbor.length) {
case 3:
shouldpass(decodedValue, value, "16", valueText);
shouldpass(decodedValue, value, "32", valueText);
shouldpass(decodedValue, value, "64", valueText);
break;
case 5:
shouldpass(decodedValue, value, "32", valueText);
shouldpass(decodedValue, value, "64", valueText);
overflow(decodedValue, "16");
break;
case 9:
shouldpass(decodedValue, value, "64", valueText);
overflow(decodedValue, "16");
overflow(decodedValue, "32");
break;
default:
fail("No such length");
}
} else {
try {
CBOR.Float(value);
fail('Should not execute');
} catch (error) {
assertTrue("nf1", error.toString().includes("Not permitted: 'NaN/Infinity'"));
}
let decodedValue = CBOR.Float.createExtendedFloat(value);
assertTrue("nf2", decodedValue.getExtendedFloat64().toString() == value.toString());
assertTrue("nf3", decodedValue.toString() == value.toString());
let cbor = decodedValue.encode();
assertTrue("nf4", CBOR.toHex(cbor) == expected);
assertTrue("nf5", CBOR.decode(cbor).equals(decodedValue));
let buf = new Uint8Array(8);
new DataView(buf.buffer, 0, 8).setFloat64(0, value, false);
assertTrue("nf6", decodedValue.getNonFinite64() == CBOR.toBigInt(buf));
}
assertTrue("d10", CBOR.toHex(CBOR.Float.createExtendedFloat(value).encode()) == expected);
}
const inNanWithPayload = new Uint8Array([0x7f, 0xf8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]);
let value = new DataView(inNanWithPayload.buffer, 0, 8).getFloat64(0, false);
let outNanWithPayload = new Uint8Array(8);
new DataView(outNanWithPayload.buffer, 0, 8).setFloat64(0, value, false);
let supportNanWithPayloads = true;
for (let q = 0; q < 8; q++) {
if (inNanWithPayload[q] != outNanWithPayload[q]) {
// console.log(outNanWithPayload.toString());
console.log('This implementation does not support NaN with payloads');
supportNanWithPayloads = false;
break;
}
}
function payloadOneTurn(payload, hex, dn) {
dn = dn == null ? "float'" + hex.substring(2) + "'" : dn;
let cbor = CBOR.NonFinite.createPayload(payload).encode();
let object = CBOR.decode(cbor);
assertTrue("plo1", object instanceof CBOR.NonFinite);
let nonFinite = object;
assertTrue("plo2", nonFinite.getPayload() == payload);
assertTrue("plo3", CBOR.toHex(cbor) == hex);
assertTrue("plo4", nonFinite.toString() == dn);
assertTrue("plo5", nonFinite.getNonFinite() == CBOR.toBigInt(CBOR.fromHex(hex.substring(2), 16)));
assertFalse("plo6", nonFinite.getSign());
let signedHex = hex.substring(0, 2) + "f" +hex.substring(3);
nonFinite.setSign(true);
assertTrue("plo7", nonFinite.getSign());
assertTrue("plo8", CBOR.toHex(nonFinite.encode()) == signedHex);
nonFinite = CBOR.NonFinite.createPayload(payload).setSign(true);
assertTrue("plo9", CBOR.toHex(nonFinite.encode()) == signedHex);
}
function oneNonFiniteTurn(value, binexpect, textexpect) {
let nonfinite = CBOR.NonFinite(value);
let text = nonfinite.toString();
let returnValue = nonfinite.getNonFinite();
let returnValue64 = nonfinite.getNonFinite64();
let textdecode = CBOR.fromDiagnostic(textexpect);
let cbor = nonfinite.encode();
let refcbor = CBOR.fromHex(binexpect);
let hexbin = CBOR.toHex(cbor);
assertTrue("eq1", text == textexpect);
assertTrue("eq2", hexbin == binexpect);
assertTrue("eq3", returnValue == CBOR.decode(cbor).getNonFinite());
assertTrue("eq4", returnValue == textdecode.getNonFinite());
assertTrue("eq5", CBOR.fromBigInt(returnValue).length == nonfinite.length);
assertTrue("eq7", CBOR.fromBigInt(returnValue64).length == 8);
assertTrue("eq8", nonfinite.equals(CBOR.decode(cbor)));
let rawcbor = CBOR.fromBigInt(value);
rawcbor = CBOR.addArrays(new Uint8Array([0xf9 + (rawcbor.length >> 2)]), rawcbor);
if (rawcbor.length > refcbor.length) {
try {
CBOR.decode(rawcbor);
fail("d1");
} catch(error) {
assertTrue("d2", error.toString().includes("Non-deterministic"));
}
} else {
CBOR.decode(rawcbor);
}
assertTrue("d3", CBOR.initDecoder(rawcbor, CBOR.LENIENT_NUMBER_DECODING)
.decodeWithOptions().equals(nonfinite));
let object = CBOR.decode(refcbor);
if (textexpect.includes("NaN") || textexpect.includes("Infinity")) {
assertTrue("d4", object.getExtendedFloat64().toString() == textexpect);
assertTrue("d5", object.isSimple());
assertTrue("d6", textexpect.includes("Infinity") ^ object.isNaN());
} else {
try {
object.getExtendedFloat64();
fail("d7");
} catch (error) {
assertTrue("d8", error.toString().includes("7e00"));
}
assertFalse("d9", object.isSimple());
}
}
oneTurn("0.0", "f90000");
oneTurn("-0.0", "f98000");
oneTurn("NaN", "f97e00");
oneTurn("Infinity", "f97c00");
oneTurn("-Infinity", "f9fc00");
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");
oneNonFiniteTurn(0x7e00n, "f97e00", "NaN");
oneNonFiniteTurn(0x7c01n, "f97c01", "float'7c01'");
oneNonFiniteTurn(0xfc01n, "f9fc01", "float'fc01'");
oneNonFiniteTurn(0x7fffn, "f97fff", "float'7fff'");
oneNonFiniteTurn(0xfe00n, "f9fe00", "float'fe00'");
oneNonFiniteTurn(0x7c00n, "f97c00", "Infinity");
oneNonFiniteTurn(0xfc00n, "f9fc00", "-Infinity");
oneNonFiniteTurn(0x7fc00000n, "f97e00", "NaN");
oneNonFiniteTurn(0x7f800001n, "fa7f800001", "float'7f800001'");
oneNonFiniteTurn(0xff800001n, "faff800001", "float'ff800001'");
oneNonFiniteTurn(0x7fffffffn, "fa7fffffff", "float'7fffffff'");
oneNonFiniteTurn(0xffc00000n, "f9fe00", "float'fe00'");
oneNonFiniteTurn(0x7f800000n, "f97c00", "Infinity");
oneNonFiniteTurn(0xff800000n, "f9fc00", "-Infinity");
oneNonFiniteTurn(0x7ff8000000000000n, "f97e00", "NaN");
oneNonFiniteTurn(0x7ff0000000000001n, "fb7ff0000000000001", "float'7ff0000000000001'");
oneNonFiniteTurn(0xfff0000000000001n, "fbfff0000000000001", "float'fff0000000000001'");
oneNonFiniteTurn(0x7fffffffffffffffn, "fb7fffffffffffffff", "float'7fffffffffffffff'");
oneNonFiniteTurn(0x7ff0000020000000n, "fa7f800001", "float'7f800001'");
oneNonFiniteTurn(0xfff0000020000000n, "faff800001", "float'ff800001'");
oneNonFiniteTurn(0xfff8000000000000n, "f9fe00", "float'fe00'");
oneNonFiniteTurn(0x7ff0040000000000n, "f97c01", "float'7c01'");
oneNonFiniteTurn(0x7ff0000000000000n, "f97c00", "Infinity");
oneNonFiniteTurn(0xfff0000000000000n, "f9fc00", "-Infinity");
// Very special, some platforms natively support NaN with payloads, but we don't care
// "signaling" NaN
try {
let nanWithPayload = new Uint8Array([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
CBOR.Float.createExtendedFloat(new DataView(nanWithPayload.buffer, 0, 8).getFloat64(0, false));
assertFalse("must not", supportNanWithPayloads);
} catch (error) {
assertTrue("not", error.toString().includes("payloads"));
}
let nonFinite = CBOR.Float.createExtendedFloat(Number.NaN);
assertTrue("conv", nonFinite instanceof CBOR.NonFinite);
assertTrue("truncated", nonFinite.getNonFinite64() == 0x7ff8000000000000n); // Returns "quiet" NaN
assertTrue("cbor", CBOR.toHex(nonFinite.encode()) == "f97e00"); // Encoded as it should
assertTrue("combined", Number.isNaN(nonFinite.getExtendedFloat64())); // Returns "Number"
assertTrue("nan", nonFinite.isNaN(false)); // Indeed it is
payloadOneTurn(0n, "f97c00", "Infinity");
payloadOneTurn(1n, "f97e00", "NaN");
payloadOneTurn(2n, "f97d00", null);
payloadOneTurn((1n << 10n) - 1n, "f97fff", null);
payloadOneTurn(1n << 10n, "fa7f801000", null);
payloadOneTurn((1n << 23n) - 1n, "fa7fffffff", null);
payloadOneTurn(1n << 23n, "fb7ff0000010000000", null);
payloadOneTurn((1n << 52n) - 1n, "fb7fffffffffffffff", null);
try {
CBOR.NonFinite.createPayload(1n << 52n).encode();
fail("pl8");
} catch(error) {
assertTrue("p18a", error.toString().includes("Payload out of range"));
}
function reducedOneTurn(f16, length, value, result) {
let ok = length != null;
let reduced;
try {
reduced = f16 ? CBOR.Float.createFloat16(value) : CBOR.Float.createFloat32(value);
assertTrue("Should not", ok);
assertTrue("Compare=" + reduced + " r=" + result, reduced.getFloat64() == result);
assertTrue("len", reduced.length == length);
assertTrue("equi", CBOR.decode(reduced.encode()).equals(reduced));
// console.log("Hi=" + result + " j=" + reduced + " l=" + reduced.length);
} catch (error) {
// console.log("EHi=" + result + " r=" + reduced + " v=" + value);
// console.log(error.toString());
assertFalse("should" + error.toString(), ok);
assertTrue("errtype", error.toString().includes( Number.isFinite(value) ? "out of range" : "NaN/"));
}
}
reducedOneTurn(true, null, Number.NaN, 0);
reducedOneTurn(true, 2, 60000, 60000);
reducedOneTurn(true, 2, 5.960464477539063e-8, 5.960464477539063e-8);
reducedOneTurn(true, 2, 3.0e-8, 5.960464477539063e-8);
reducedOneTurn(true, 2, 2.0e-8, 0);
reducedOneTurn(true, 2, 65504.0, 65504.0);
reducedOneTurn(true, 2, 65519.99, 65504.0);
reducedOneTurn(true, null, 65520, 65504.0);
reducedOneTurn(true, 2, 10, 10);
reducedOneTurn(true, 2, 10.003906, 10);
reducedOneTurn(true, 2, 10.003907, 10.0078125);
reducedOneTurn(true, 2, 6.097555160522461e-5, 6.097555160522461e-5);
reducedOneTurn(true, 2, 6.097e-5, 6.097555160522461e-5);
reducedOneTurn(true, 2, 6.09e-5, 6.091594696044922e-5);
reducedOneTurn(false, null, Number.NaN, 0);
reducedOneTurn(false, 2, 2.5, 2.5);
reducedOneTurn(false, 2, 65504.0, 65504.0);
reducedOneTurn(false, 2, 5.960464477539063e-8, 5.960464477539063e-8);
reducedOneTurn(false, 4, 2.0e-8, 1.999999987845058e-8);
reducedOneTurn(false, 2, 5.960464477539063e-8, 5.960464477539063e-8);
reducedOneTurn(false, 4, 1.401298464324817e-45, 1.401298464324817e-45);
reducedOneTurn(false, 4, 3.4028234663852886e+38, 3.4028234663852886e+38);
reducedOneTurn(false, 4, 3.4028235e+38, 3.4028234663852886e+38);
reducedOneTurn(false, null, 3.40282358e+38, 3.4028234663852886e+38);
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 'number'"));
}
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(type, value) {
let bigFlag = type.indexOf("64") > 0 || type.indexOf("128") > 0;
let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
let test = 'assertTrue("good", wrapper.get' + type + '() == ' + (bigFlag ? value + 'n' : Number(value)) + ')';
eval(test);
test = 'CBOR.' + (type.indexOf("128") > 0 ? 'BigInt' : 'Int') + '.create' + type + '(' + value + 'n)';
eval(test);
if (value == 10n) {
test = 'CBOR.' + (type.indexOf("128") > 0 ? 'BigInt' : 'Int') + '.create' + type + '(Number(' + value +'))';
eval(test);
}
}
function badRun(type, value) {
let bigFlag = type.indexOf("64") > 0 || type.indexOf("128") > 0;
let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
let test = 'wrapper.get' + type + '()';
try {
eval(test);
fail("Should fail");
} catch (error) {
if (!error.toString().includes('Value out of range for ')) {
throw error;
}
}
test = 'CBOR.' + (type.indexOf("128") > 0 ? 'BigInt' : 'Int') + '.create' + type + '(' + value + 'n)';
try {
eval(test);
fail("Should fail");
} catch (error) {
if (!error.toString().includes('Value out of range for ') &&
!(error.toString().includes('CBOR.Int') && bigFlag)) {
throw error;
}
}
}
function innerTurn(type, signed, size) {
let min = signed ? -(1n << BigInt(size) - 1n) : 0n;
let max = signed ? (1n << BigInt(size) - 1n) - 1n : (1n << BigInt(size)) - 1n;
if (size == 53) {
max = BigInt(Number.MAX_SAFE_INTEGER);
min = -max;
}
goodRun(type, min);
goodRun(type, max);
goodRun(type, 10n);
badRun(type, max + 1n);
badRun(type, min - 1n);
}
function oneTurn(size) {
innerTurn("Int" + size, true, size);
if (size != 53) {
innerTurn("Uint" + size, false, size);
}
}
oneTurn(8);
oneTurn(16);
oneTurn(32);
oneTurn(53);
oneTurn(64);
oneTurn(128);
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("upd-4", array.insert(array.length, CBOR.Int(-8)) == array);
assertTrue("upd-5", array.get(array.length - 1).equals(CBOR.Int(-8)));
assertTrue("upd-4", array.insert(0, CBOR.Int(-9)) == array);
assertTrue("upd-5", array.get(0).equals(CBOR.Int(-9)));
let l = array.length;
assertTrue("upd-6", array.remove(0).equals(CBOR.Int(-9)));
assertTrue("upd-7", l == array.length + 1);
assertTrue("upd-8", array.get(0).getString() == "three");
assertTrue("upd-9", array.toDiagnostic(false) == '["three",1,-8]');
function aBadOne(expression) {
try {
eval("array." + expression);
fail("Should not pass");
} catch (Error) {
}
}
aBadOne("get('string')")
aBadOne("get(array.length)");
aBadOne("get(-1)");
aBadOne("insert(array.length + 1, CBOR.Int(6))");
aBadOne("insert(array.length)");
aBadOne("remove(array.length)");
aBadOne("remove(array.length - 1, 'hi')");
aBadOne("get(0, 6)");
/*
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.fromDiagnostic(CBOR.decode(cbor).toString()).encode(), bin));
assertTrue("version", CBOR.version == "1.0.20");
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');
// 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).getInt53();
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).getInt53() == IN_RANGE);
cbor = CBOR.Int(-IN_RANGE).encode();
assertTrue("R0", CBOR.decode(cbor).getInt53() == -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);
let arraySequence = CBOR.Array();
decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
while (object = decoder.decodeWithOptions()) {
arraySequence.add(object);
}
assertFalse("Comp5", CBOR.compareArrays(arraySequence.encodeAsSequence(), cbor));
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()));
assertTrue("t3.2", tag.cotxId == "https://example.com/myobject");
assertTrue("t3.3", tag.cotxObject.equals(CBOR.Int(6)));
cbor = CBOR.Tag(0xf0123456789abcden, object).encode();
assertTrue("t14", CBOR.decode(cbor).getTagNumber()== 0xf0123456789abcden);
assertTrue("t5", CBOR.toHex(cbor) ==
"dbf0123456789abcde82781c68747470733a2f2f6578616d706c652e636f6d2f6d796f626a65637406");
[-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 instant methods
function oneGetDateTime(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);
}
function badDate(hexBor, err) {
try {
CBOR.decode(CBOR.fromHex(hexBor));
fail("must not");
} catch (error) {
if (!error.toString().includes(err)) {
throw error;
}
}
}
function truncateDateTime(iso, millis, seconds) {
let dateTime = CBOR.String(iso).getDateTime();
assertTrue("trdt1", dateTime.getTime() == millis);
assertTrue("trdt2", CBOR.createDateTime(dateTime, true, false).getDateTime().getTime() == millis);
assertTrue("trdt3", CBOR.createDateTime(dateTime, false, false).getDateTime().getTime() == seconds * 1000);
}
function truncateEpochTime(float, millis, seconds) {
let epoch = CBOR.Float(float).getEpochTime();
assertTrue("tr1", epoch.getTime() == millis);
assertTrue("tr2", CBOR.createEpochTime(epoch, true).getEpochTime().getTime() == millis);
assertTrue("tr3", CBOR.createEpochTime(epoch, false).getEpochTime().getTime() == seconds * 1000);
}
function oneGetEpochTime(hexBor, epoch, err) {
let time = Math.floor((epoch * 1000) + 0.5);
let instant = CBOR.decode(CBOR.fromHex(hexBor)).getEpochTime();
assertTrue("epoch1", instant.getTime() == time);
let cborObject = CBOR.createEpochTime(instant, time % 1000);
// console.log("E=" + cborObject.toString());
// console.log("1=" + cborObject.getEpochTime().getTime() + " 2=" + time);
assertTrue("epoch2", cborObject.getEpochTime().getTime() == time);
if (time % 1000 > 500) {
let p1 = Math.floor(epoch + 1.0) * 1000;
cborObject = CBOR.createEpochTime(instant, false);
// console.log("r1=" + cborObject.getEpochTime().getTime() + " r2=" + p1);
assertTrue("epoch3", cborObject.getEpochTime().getTime() == p1);
}
instant = CBOR.decode(CBOR.fromHex(hexBor));
try {
instant.checkForUnread();
fail("must not");
} catch (error) {
if (!error.toString().includes(err)) {
throw error;
}
}
instant.getEpochTime();
instant.checkForUnread();
}
oneGetDateTime(1740060548000, "2025-02-20T14:09:08+00:00");
oneGetDateTime(1740060548000, "2025-02-20T14:09:08Z");
oneGetDateTime(1740060548000, "2025-02-20T15:09:08+01:00");
oneGetDateTime(1740060548000, "2025-02-20T15:39:08+01:30");
oneGetDateTime(1740060548000, "2025-02-20T12:09:08-02:00");
oneGetDateTime(1740060548000, "2025-02-20T11:39:08-02:30");
oneGetDateTime(1740060548123, "2025-02-20T11:39:08.123-02:30");
oneGetDateTime(1740060548930, "2025-02-20T14:09:08.930Z");
// Next: Truncates!
oneGetDateTime(1740060548930, "2025-02-20T14:09:08.9305Z");
oneGetDateTime(-62167219200000, "0000-01-01T00:00:00Z");
oneGetDateTime(253402300799000, "9999-12-31T23:59:59Z");
badDate("c001", "got: CBOR.Int");
badDate("c06135", "Invalid ISO date string: 5");
badDate("c16135", "got: CBOR.String");
oneGetEpochTime("1A67B73784", 1740060548, "Data of type=CBOR.Int");
oneGetEpochTime("FB41D9EDCDE113645A", 1740060548.303, "Data of type=CBOR.Float with value=174");
oneGetEpochTime("c1FB41D9EDCDE113645A", 1740060548.303, "Tagged object 1 of type=CBOR.Float");
// Next: Truncates!
// oneGetEpochTime("c1fb41d9edcde1136c8b", 1740060548.3035, "Tagged object 1 of type=CBOR.Float");
// oneGetEpochTime("c1fb41d9edcde1204189", 1740060548.5045, "Tagged object 1 of type=CBOR.Float");
oneGetEpochTime("c11b0000003afff4417f", 253402300799, "Tagged object 1 of type=CBOR.Int");
oneGetEpochTime("00", 0, "Data of type=CBOR.Int");
function oneMillis(time, iso) {
let instant = new Date();
instant.setTime(time);
assertTrue("cdt1=", CBOR.createDateTime(instant, true, true).getString() == iso);
let created = CBOR.createDateTime(instant, true, false);
assertTrue("cdt2=", created.getDateTime().getTime() == time);
assertTrue("cdt3=", created.getString().length == iso.length + 5);
created = CBOR.createEpochTime(instant, true);
assertTrue("cet1=", created.getEpochTime().getTime() == time);
assertTrue("cet2=", created instanceof CBOR.Float == iso.includes("."));
}
oneMillis(1752189147000, "2025-07-10T23:12:27Z");
oneMillis(1752189147123, "2025-07-10T23:12:27.123Z");
oneMillis(1752189147120, "2025-07-10T23:12:27.12Z");
oneMillis(1752189147100, "2025-07-10T23:12:27.1Z");
truncateEpochTime(1740060548.000, 1740060548000, 1740060548);
truncateEpochTime(0.0, 0, 0);
truncateEpochTime(1740060548.0004, 1740060548000, 1740060548);
truncateEpochTime(1740060548.0005, 1740060548001, 1740060548);
truncateDateTime("2025-07-10T23:12:27Z", 1752189147000, 1752189147);
truncateDateTime("2025-07-10T23:12:27.1Z", 1752189147100, 1752189147);
truncateDateTime("2025-07-10T23:12:27.12Z", 1752189147120, 1752189147);
truncateDateTime("2025-07-10T23:12:27.123Z", 1752189147123, 1752189147);
truncateDateTime("2025-07-10T23:12:27.1233Z", 1752189147123, 1752189147);
truncateDateTime("2025-07-10T23:12:27.1235Z", 1752189147123, 1752189147);
truncateDateTime("2025-07-10T23:12:27.523Z", 1752189147523, 1752189148);
truncateDateTime("1925-07-10T23:12:27Z", -1403570853000, -1403570853);
truncateDateTime("1925-07-10T23:12:27.1Z", -1403570852900, -1403570853);
truncateDateTime("1925-07-10T23:12:27.12Z", -1403570852880, -1403570853);
truncateDateTime("1925-07-10T23:12:27.123Z", -1403570852877, -1403570853);
truncateDateTime("1925-07-10T23:12:27.499Z", -1403570852501, -1403570853);
truncateDateTime("1925-07-10T23:12:27.500Z", -1403570852500, -1403570852);
truncateDateTime("1925-07-10T23:12:27.700Z", -1403570852300, -1403570852);
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 {
// Beyond nano-seconds
CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43.6666666666Z"));
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;
}
}
[-1, 253402300800].forEach(epoch => {
try {
// Out of range for Date().
CBOR.Tag(1n, CBOR.Int(epoch));
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("Epoch out of")) {
throw error;
}
}
try {
// Out of range for Date().
let instant = new Date();
instant.setTime(epoch * 1000);
CBOR.createEpochTime(instant, true);
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("Epoch out of")) {
throw error;
}
}
});
assertTrue("zero", CBOR.String("0000-01-01T00:00:00Z").getDateTime().getTime() == -62167219200000);
let now = new Date();
/*
console.log("Now=" + now.getTime() + " iso=" + now.toISOString() +
" offset=" + now.getTimezoneOffset() + " str=" + now.toString());
*/
function oneCreateDateTime(dateOrTime, utc, millis, bad) {
let instant = new Date();
let time = typeof dateOrTime == 'number' ? Math.round(dateOrTime) : dateOrTime.getTime();
instant.setTime(time);
if (bad) {
try {
CBOR.createDateTime(instant, millis, utc);
throw Error("Should not");
} catch (error) {
if (!error.toString().includes("Date object out of range")) {
throw error;
}
}
} else {
let dateTime = CBOR.createDateTime(instant, millis, utc);
if (millis || !(time % 1000)) {
assertTrue("cdt1" + dateTime, dateTime.getDateTime().getTime() == time);
} else if (!millis && time % 1000) {
assertFalse("cdt2" + dateTime, dateTime.getDateTime().getTime() == time);
}
}
}
oneCreateDateTime(now, true, false);
oneCreateDateTime(now, true, true);
oneCreateDateTime(now, false, false);
oneCreateDateTime(now, false, true);
oneCreateDateTime(1740060548000, true, false);
oneCreateDateTime(1740060548000, true, true);
oneCreateDateTime(1740060548501, true, false);
oneCreateDateTime(1740060548501, true, true);
oneCreateDateTime(1740060548000.3, true, true);
oneCreateDateTime(1740060548000.5, true, true);
oneCreateDateTime(-62167219200000, true, true);
oneCreateDateTime(-62167219200001, true, true, true);
oneCreateDateTime(253402300799000, true, true);
oneCreateDateTime(253402300799001, true, true, true);
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();