Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+647
View File
@@ -0,0 +1,647 @@
const fixtures = [
{
cbor: 'AA==',
hex: '00',
roundtrip: true,
decoded: 0
},
{
cbor: 'AQ==',
hex: '01',
roundtrip: true,
decoded: 1
},
{
cbor: 'Cg==',
hex: '0a',
roundtrip: true,
decoded: 10
},
{
cbor: 'Fw==',
hex: '17',
roundtrip: true,
decoded: 23
},
{
cbor: 'GBg=',
hex: '1818',
roundtrip: true,
decoded: 24
},
{
cbor: 'GBk=',
hex: '1819',
roundtrip: true,
decoded: 25
},
{
cbor: 'GGQ=',
hex: '1864',
roundtrip: true,
decoded: 100
},
{
cbor: 'GQPo',
hex: '1903e8',
roundtrip: true,
decoded: 1000
},
{
cbor: 'GgAPQkA=',
hex: '1a000f4240',
roundtrip: true,
decoded: 1000000
},
{
cbor: 'GwAAAOjUpRAA',
hex: '1b000000e8d4a51000',
roundtrip: true,
decoded: 1000000000000
},
{
cbor: 'G///////////',
hex: '1bffffffffffffffff',
roundtrip: true,
decoded: BigInt('18446744073709551615')
},
{
cbor: 'wkkBAAAAAAAAAAA=',
hex: 'c249010000000000000000',
roundtrip: true,
decoded: BigInt('18446744073709551616'),
noTagDecodeError: /tag not supported \(2\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'O///////////',
hex: '3bffffffffffffffff',
roundtrip: true,
decoded: BigInt('-18446744073709551616')
},
{
cbor: 'w0kBAAAAAAAAAAA=',
hex: 'c349010000000000000000',
roundtrip: true,
decoded: BigInt('-18446744073709551617'),
noTagDecodeError: /tag not supported \(3\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'IA==',
hex: '20',
roundtrip: true,
decoded: -1
},
{
cbor: 'KQ==',
hex: '29',
roundtrip: true,
decoded: -10
},
{
cbor: 'OGM=',
hex: '3863',
roundtrip: true,
decoded: -100
},
{
cbor: 'OQPn',
hex: '3903e7',
roundtrip: true,
decoded: -1000
},
{
cbor: '+QAA',
hex: 'f90000',
roundtrip: false, // indistinguishable float
decoded: 0.0
},
{
cbor: '+YAA',
hex: 'f98000',
roundtrip: false, // indistinguishable float
decoded: -0.0
},
{
cbor: '+TwA',
hex: 'f93c00',
roundtrip: false, // indistinguishable float
decoded: 1.0
},
{
cbor: '+z/xmZmZmZma',
hex: 'fb3ff199999999999a',
roundtrip: true,
decoded: 1.1
},
{
cbor: '+T4A',
hex: 'f93e00',
roundtrip: true,
decoded: 1.5
},
{
cbor: '+Xv/',
hex: 'f97bff',
roundtrip: false, // indistinguishable float
decoded: 65504.0
},
{
cbor: '+kfDUAA=',
hex: 'fa47c35000',
roundtrip: false, // indistinguishable float
decoded: 100000.0
},
{
cbor: '+n9///8=',
hex: 'fa7f7fffff',
roundtrip: true,
decoded: 3.4028234663852886e+38
},
{
cbor: '+3435DyIAHWc',
hex: 'fb7e37e43c8800759c',
roundtrip: true,
decoded: 1.0e+300
},
{
cbor: '+QAB',
hex: 'f90001',
roundtrip: true,
decoded: 5.960464477539063e-08
},
{
cbor: '+QQA',
hex: 'f90400',
roundtrip: true,
decoded: 6.103515625e-05
},
{
cbor: '+cQA',
hex: 'f9c400',
roundtrip: false, // indistinguishable float
decoded: -4.0
},
{
cbor: '+8AQZmZmZmZm',
hex: 'fbc010666666666666',
roundtrip: true,
decoded: -4.1
},
{
cbor: '+XwA',
hex: 'f97c00',
roundtrip: true,
diagnostic: Infinity
},
{
cbor: '+X4A',
hex: 'f97e00',
roundtrip: true,
diagnostic: NaN
},
{
cbor: '+fwA',
hex: 'f9fc00',
roundtrip: true,
diagnostic: -Infinity
},
{
cbor: '+n+AAAA=',
hex: 'fa7f800000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+n/AAAA=',
hex: 'fa7fc00000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+v+AAAA=',
hex: 'faff800000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '+3/wAAAAAAAA',
hex: 'fb7ff0000000000000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+3/4AAAAAAAA',
hex: 'fb7ff8000000000000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+//wAAAAAAAA',
hex: 'fbfff0000000000000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '9A==',
hex: 'f4',
roundtrip: true,
decoded: false
},
{
cbor: '9Q==',
hex: 'f5',
roundtrip: true,
decoded: true
},
{
cbor: '9g==',
hex: 'f6',
roundtrip: true,
decoded: null
},
{
cbor: '9w==',
hex: 'f7',
roundtrip: true,
diagnostic: undefined
},
{
cbor: '8A==',
hex: 'f0',
roundtrip: true,
diagnostic: 'simple(16)',
error: /simple values are not supported/
},
{
cbor: '+Bg=',
hex: 'f818',
roundtrip: true,
diagnostic: 'simple(24)',
error: /simple values are not supported/
},
{
cbor: '+P8=',
hex: 'f8ff',
roundtrip: true,
diagnostic: 'simple(255)',
error: /simple values are not supported/
},
{
cbor: 'wHQyMDEzLTAzLTIxVDIwOjA0OjAwWg==',
hex: 'c074323031332d30332d32315432303a30343a30305a',
roundtrip: false, // tags
diagnostic: '0("2013-03-21T20:04:00Z")'
},
{
cbor: 'wRpRS2ew',
hex: 'c11a514b67b0',
roundtrip: false, // tags
diagnostic: '1(1363896240)'
},
{
cbor: 'wftB1FLZ7CAAAA==',
hex: 'c1fb41d452d9ec200000',
roundtrip: false, // tags
diagnostic: '1(1363896240.5)'
},
{
cbor: '10QBAgME',
hex: 'd74401020304',
roundtrip: false, // tags
diagnostic: "23(h'01020304')"
},
{
cbor: '2BhFZElFVEY=',
hex: 'd818456449455446',
roundtrip: false, // tags
diagnostic: "24(h'6449455446')"
},
{
cbor: '2CB2aHR0cDovL3d3dy5leGFtcGxlLmNvbQ==',
hex: 'd82076687474703a2f2f7777772e6578616d706c652e636f6d',
roundtrip: false, // tags
diagnostic: '32("http://www.example.com")'
},
{
cbor: 'QA==',
hex: '40',
roundtrip: true,
diagnostic: "h''"
},
{
cbor: 'RAECAwQ=',
hex: '4401020304',
roundtrip: true,
diagnostic: "h'01020304'"
},
{
cbor: 'YA==',
hex: '60',
roundtrip: true,
decoded: ''
},
{
cbor: 'YWE=',
hex: '6161',
roundtrip: true,
decoded: 'a'
},
{
cbor: 'ZElFVEY=',
hex: '6449455446',
roundtrip: true,
decoded: 'IETF'
},
{
cbor: 'YiJc',
hex: '62225c',
roundtrip: true,
decoded: '"\\'
},
{
cbor: 'YsO8',
hex: '62c3bc',
roundtrip: true,
decoded: 'ü'
},
{
cbor: 'Y+awtA==',
hex: '63e6b0b4',
roundtrip: true,
decoded: '水'
},
{
cbor: 'ZPCQhZE=',
hex: '64f0908591',
roundtrip: true,
decoded: '𐅑'
},
{
cbor: 'gA==',
hex: '80',
roundtrip: true,
decoded: [
]
},
{
cbor: 'gwECAw==',
hex: '83010203',
roundtrip: true,
decoded: [
1,
2,
3
]
},
{
cbor: 'gwGCAgOCBAU=',
hex: '8301820203820405',
roundtrip: true,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'mBkBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgYGBk=',
hex: '98190102030405060708090a0b0c0d0e0f101112131415161718181819',
roundtrip: true,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'oA==',
hex: 'a0',
roundtrip: true,
decoded: {
}
},
{
cbor: 'ogECAwQ=',
hex: 'a201020304',
roundtrip: true,
diagnostic: '{1: 2, 3: 4}',
error: /non-string keys not supported/
},
{
cbor: 'omFhAWFiggID',
hex: 'a26161016162820203',
roundtrip: true,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhoWFiYWM=',
hex: '826161a161626163',
roundtrip: true,
decoded: [
'a',
{
b: 'c'
}
]
},
{
cbor: 'pWFhYUFhYmFCYWNhQ2FkYURhZWFF',
hex: 'a56161614161626142616361436164614461656145',
roundtrip: true,
decoded: {
a: 'A',
b: 'B',
c: 'C',
d: 'D',
e: 'E'
}
},
{
cbor: 'X0IBAkMDBAX/',
hex: '5f42010243030405ff',
roundtrip: false,
diagnostic: "(_ h'0102', h'030405')",
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'f2VzdHJlYWRtaW5n/w==',
hex: '7f657374726561646d696e67ff',
roundtrip: false,
decoded: 'streaming',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'n/8=',
hex: '9fff',
roundtrip: false,
decoded: [
]
},
{
cbor: 'nwGCAgOfBAX//w==',
hex: '9f018202039f0405ffff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwGCAgOCBAX/',
hex: '9f01820203820405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGCAgOfBAX/',
hex: '83018202039f0405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGfAgP/ggQF',
hex: '83019f0203ff820405',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwECAwQFBgcICQoLDA0ODxAREhMUFRYXGBgYGf8=',
hex: '9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff',
roundtrip: false,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'v2FhAWFinwID//8=',
hex: 'bf61610161629f0203ffff',
roundtrip: false,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhv2FiYWP/',
hex: '826161bf61626163ff',
roundtrip: false,
decoded: [
'a',
{
b: 'c'
}
]
},
{
cbor: 'v2NGdW71Y0FtdCH/',
hex: 'bf6346756ef563416d7421ff',
roundtrip: false,
decoded: {
Fun: true,
Amt: -2
}
}
]
export { fixtures }
+18
View File
@@ -0,0 +1,18 @@
import { Token, Type } from '../lib/token.js'
export function dateDecoder (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1')
}
return new Date(obj)
}
export function dateEncoder (obj) {
if (!(obj instanceof Date)) {
throw new Error('expected Date for "Date" encoder')
}
return [
new Token(Type.tag, 0),
new Token(Type.string, obj.toISOString().replace(/\.000Z$/, 'Z'))
]
}
+402
View File
@@ -0,0 +1,402 @@
/* eslint-env mocha */
import chai from 'chai'
import { exec } from 'child_process'
import process from 'process'
import path from 'path'
import { platform } from 'os'
import { fileURLToPath } from 'url'
const { assert } = chai
const fixture1JsonString = '{"a":1,"b":[2,3],"smile":"😀"}'
const fixture1JsonPrettyString =
`{
"a": 1,
"b": [
2,
3
],
"smile": "😀"
}
`
const fixture1HexString = 'a3616101616282020365736d696c6564f09f9880'
const fixture1Bin = fromHex(fixture1HexString)
const fixture1BinString = new TextDecoder().decode(fixture1Bin)
const fixture1DiagnosticString =
`a3 # map(3)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`
const fixture2HexString = 'a4616101616282020363627566440102036165736d696c6564f09f9880'
const fixture2DiagnosticString =
`a4 # map(4)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
63 # string(3)
627566 # "buf"
44 # bytes(4)
01020361 # "\\x01\\x02\\x03a"
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`
const binPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../lib/bin.js')
function fromHex (hex) {
return new Uint8Array(hex.split('')
.map((/** @type {string} */ c, /** @type {number} */ i, /** @type {string[]} */ d) => i % 2 === 0 ? `0x${c}${d[i + 1]}` : '')
.filter(Boolean)
.map((/** @type {string} */ e) => parseInt(e, 16)))
}
async function execBin (cmd, stdin) {
return new Promise((resolve, reject) => {
const cp = exec(`"${process.execPath}" "${binPath}" ${cmd}`, (err, stdout, stderr) => {
if (err) {
err.stdout = stdout
err.stderr = stderr
return reject(err)
}
resolve({ stdout, stderr })
})
if (stdin != null) {
cp.on('spawn', () => {
cp.stdin.write(stdin)
cp.stdin.end()
})
}
})
}
describe('Bin', () => {
it('usage', async () => {
try {
await execBin('')
assert.fail('should have errored')
} catch (e) {
assert.strictEqual(e.stdout, '')
assert.strictEqual(e.stderr,
`Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`)
}
})
it('bad cmd', async () => {
try {
await execBin('blip')
assert.fail('should have errored')
} catch (e) {
assert.strictEqual(e.stdout, '')
assert.strictEqual(e.stderr,
`Unknown command: 'blip'
Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`)
}
})
it('help', async () => {
const { stdout, stderr } = await execBin('help')
assert.strictEqual(stdout, '')
assert.strictEqual(stderr,
`Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`)
})
it('bin2diag (stdin)', async () => {
const { stdout, stderr } = await execBin('bin2diag', fixture1Bin)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1DiagnosticString)
})
it('bin2hex (stdin)', async () => {
const { stdout, stderr } = await execBin('bin2hex', fixture1Bin)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${fixture1HexString}\n`)
})
it('bin2json (stdin)', async () => {
const { stdout, stderr } = await execBin('bin2json', fixture1Bin)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${fixture1JsonString}\n`)
})
it('bin2json pretty (stdin)', async () => {
const { stdout, stderr } = await execBin('bin2json --pretty', fixture1Bin)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1JsonPrettyString)
})
for (const stdin of [true, false]) {
if (platform() !== 'win32' || stdin) { // multiline CLI input is hard (impossible?) on Windows
it(`diag2bin${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`diag2bin '${fixture1DiagnosticString}'`)
: await execBin('diag2bin', fixture1DiagnosticString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1BinString)
})
it(`diag2hex${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`diag2hex '${fixture1DiagnosticString}'`)
: await execBin('diag2hex', fixture1DiagnosticString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${fixture1HexString}\n`)
})
it(`diag2json${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`diag2json '${fixture1DiagnosticString}'`)
: await execBin('diag2json', fixture1DiagnosticString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${fixture1JsonString}\n`)
})
it(`diag2json pretty${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`diag2json --pretty '${fixture1DiagnosticString}'`)
: await execBin('diag2json --pretty', fixture1DiagnosticString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1JsonPrettyString)
})
}
it(`hex2bin${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`hex2bin ${fixture1HexString}`)
: await execBin('hex2bin', fixture1HexString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1BinString)
})
it(`hex2diag${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`hex2diag ${fixture2HexString}`)
: await execBin('hex2diag', fixture2HexString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture2DiagnosticString)
})
it(`hex2json${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`hex2json ${fixture1HexString}`)
: await execBin('hex2json', fixture1HexString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${fixture1JsonString}\n`)
})
it(`hex2json pretty${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`hex2json --pretty ${fixture1HexString}`)
: await execBin('hex2json --pretty', fixture1HexString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1JsonPrettyString)
})
it(`json2bin${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"😀\\"}"')
: await execBin('json2bin', fixture1JsonString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1BinString)
})
it(`json2diag${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"😀\\"}"')
: await execBin('json2diag', fixture1JsonString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, fixture1DiagnosticString)
})
it(`json2hex${stdin ? ' (stdin)' : ''}`, async () => {
const { stdout, stderr } = !stdin
? await execBin(`json2hex "${fixture1JsonString.replace(/"/g, '\\"')}"`)
: await execBin('json2hex', fixture1JsonString)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${fixture1HexString}\n`)
})
}
// complicated nesting to test indenting algorithm
it('diag indenting', async () => {
const { stdout, stderr } = await execBin('json2diag', '{"a":[],"b":{},"c":{"a":1,"b":{"a":{"a":{}}}},"d":{"a":{"a":{"a":1},"b":2,"c":[]}},"e":[[[[{"a":{}}]]]],"f":1}')
assert.strictEqual(stderr, '')
assert.strictEqual(stdout,
`a6 # map(6)
61 # string(1)
61 # "a"
80 # array(0)
61 # string(1)
62 # "b"
a0 # map(0)
61 # string(1)
63 # "c"
a2 # map(2)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
a1 # map(1)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
64 # "d"
a1 # map(1)
61 # string(1)
61 # "a"
a3 # map(3)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
02 # uint(2)
61 # string(1)
63 # "c"
80 # array(0)
61 # string(1)
65 # "e"
81 # array(1)
81 # array(1)
81 # array(1)
81 # array(1)
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
66 # "f"
01 # uint(1)
`)
})
describe('diag length bytes', () => {
it('compact', async () => {
const { stdout, stderr } = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaa"')
assert.strictEqual(stderr, '')
assert.strictEqual(stdout,
`77 # string(23)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
`)
})
it('1-byte', async () => {
const { stdout, stderr } = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"')
assert.strictEqual(stderr, '')
assert.strictEqual(stdout,
`78 23 # string(35)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161616161616161616161 # "aaaaaaaaaaaa"
`)
})
it('2-byte', async () => {
const { stdout, stderr } = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"')
assert.strictEqual(stderr, '')
assert.strictEqual(stdout,
`79 0100 # string(256)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161 # "aaa"
`)
})
})
it('diag non-utf8 and non-printable ascii', async () => {
const input = '7864f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c11e756338bd93865e645f1adec9b9c99ef407fbd4fc6859e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82f9f18c3d03418e35'
let { stdout, stderr } = await execBin(`hex2diag ${input}`)
assert.strictEqual(stderr, '')
assert.strictEqual(stdout,
`78 64 # string(86)
f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b # "õ_øñ%\\x08¶>ò¿ì§Uzé\\x0dö1\\x1a^Ác\\x1b"
4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c # "J\\x1f¨C1\\x0bÙç\\x10ê¬å¡½×*пàIw\\x1c"
11e756338bd93865e645f1adec9b9c99ef407fbd4fc685 # "\\x11çV3\\x8bÙ8eæEñ\\xadì\\x9b\\x9c\\x99ï@\\x7f½OÆ\\x85"
9e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82 # "\\x9ey\\x04Å\\xad}ɽ\\x10¥Ì\\x16\\x97=[(ì\\x1amÔ=\\x9f\\x82"
f9f18c3d03418e35 # "ùñ\\x8c=\\x03A\\x8e5"
`)
// round-trip
;({ stdout, stderr } = await execBin('diag2hex', stdout))
assert.strictEqual(stderr, '')
assert.strictEqual(stdout, `${input}\n`)
})
})
+3
View File
@@ -0,0 +1,3 @@
// file included so ipjs will compile ../lib/bin.js
// this test file is not intended to be run or loaded
import bin from '../lib/bin.js' // eslint-disable-line
+103
View File
@@ -0,0 +1,103 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
// some from https://github.com/PJK/libcbor
const fixtures = [
{ data: '00', expected: 0, type: 'uint8' },
{ data: '02', expected: 2, type: 'uint8' },
{ data: '18ff', expected: 255, type: 'uint8' },
{ data: '1901f4', expected: 500, type: 'uint16' },
{ data: '1900ff', expected: 255, type: 'uint16', strict: false },
{ data: '19ffff', expected: 65535, type: 'uint16' },
{ data: '1a000000ff', expected: 255, type: 'uint32', strict: false },
{ data: '1a00010000', expected: 65536, type: 'uint32' },
{ data: '1a000f4240', expected: 1000000, type: 'uint32' },
{ data: '1aa5f702b3', expected: 2784428723, type: 'uint32' },
{ data: '1b00000000000000ff', expected: 255, type: 'uint64', strict: false },
{ data: '1b0016db6db6db6db7', expected: Number.MAX_SAFE_INTEGER / 1.4, type: 'uint64' },
{ data: '1b001fffffffffffff', expected: Number.MAX_SAFE_INTEGER, type: 'uint64' },
{ data: '1ba5f702b3a5f702b3', expected: BigInt('11959030306112471731'), type: 'uint64' },
{ data: '1bffffffffffffffff', expected: BigInt('18446744073709551615'), type: 'uint64' }
]
describe('uint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.expected}`, () => {
assert.ok(decode(data) === fixture.expected, `decode ${fixture.type} ${decode(data)} != ${fixture.expected}`)
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)')
} else {
assert.ok(decode(data, { strict: true }) === fixture.expected, `decode ${fixture.type}`)
}
})
}
})
it('should throw error', () => {
// minor number 28, too high for uint
assert.throws(() => decode(fromHex('1ca5f702b3a5f702b3')), Error, 'CBOR decode error: encountered invalid minor (28) for major 0')
assert.throws(() => decode(fromHex('1ba5f702b3a5f702')), Error, 'CBOR decode error: not enough data for type')
})
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${fixture.type}=${fixture.expected}`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type} !strict`)
} else {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type}`)
}
})
}
})
// mostly unnecessary, but feels good
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should roundtrip ${fixture.type}=${fixture.expected}`, () => {
assert.ok(decode(encode(fixture.expected)) === fixture.expected, `roundtrip ${fixture.type}`)
})
}
}
})
describe('toobig', () => {
it('bigger than 64-bit', () => {
// boundary condition is right on 64-bit int
assert.doesNotThrow(() => encode(BigInt('18446744073709551615') /* BigInt(2) ** BigInt(64) - BigInt(1) */))
assert.throws(() => encode(BigInt('18446744073709551616') /* BigInt(2) ** BigInt(64) */), /BigInt larger than allowable range/)
})
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode(data, { allowBigInt: false }), /safe integer range/)
} else {
assert.ok(decode(data, { allowBigInt: false }) === fixture.expected, `decode ${fixture.type}`)
}
}
})
})
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected)
it(`should encode ${small}n`, () => {
assert.strictEqual(toHex(encode(BigInt(small))), fixture.data, `encode ${small}`)
})
}
}
})
})
+96
View File
@@ -0,0 +1,96 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
// some from https://github.com/PJK/libcbor
const fixtures = [
{ data: '20', expected: -1, type: 'negint8' },
{ data: '22', expected: -3, type: 'negint8' },
{ data: '3863', expected: -100, type: 'negint8' },
{ data: '38ff', expected: -256, type: 'negint8' },
{ data: '3900ff', expected: -256, type: 'negint16', strict: false },
{ data: '3901f4', expected: -501, type: 'negint16' },
{ data: '3a000000ff', expected: -256, type: 'negint32', strict: false },
{ data: '3aa5f702b3', expected: -2784428724, type: 'negint32' },
{ data: '3b00000000000000ff', expected: -256, type: 'negint32', strict: false },
{ data: '3b0016db6db6db6db7', expected: Number.MIN_SAFE_INTEGER / 1.4 - 1, type: 'negint64' },
{ data: '3b001ffffffffffffe', expected: Number.MIN_SAFE_INTEGER, type: 'negint64' },
// kind of hard to assert on these (TODO: improve bignum handling)
{ data: '3b001fffffffffffff', expected: BigInt('-9007199254740992') /* Number.MIN_SAFE_INTEGER - 1 */, type: 'negint64' },
{ data: '3b0020000000000000', expected: BigInt('-9007199254740993') /* Number.MIN_SAFE_INTEGER - 2 */, type: 'negint64' },
{ data: '3ba5f702b3a5f702b3', expected: BigInt('-11959030306112471732'), type: 'negint64' },
{ data: '3bffffffffffffffff', expected: BigInt('-18446744073709551616'), type: 'negint64' }
]
describe('negint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.expected}`, () => {
assert.ok(decode(data) === fixture.expected, `decode ${fixture.type} (${decode(data)} != ${fixture.expected})`)
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)')
} else {
assert.strictEqual(decode(data, { strict: true }), fixture.expected, `decode ${fixture.type}`)
}
})
}
})
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${fixture.type}=${fixture.expected}`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type} !strict`)
} else {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type}`)
}
})
}
})
// mostly unnecessary, but feels good
describe('roundtrip', () => {
for (const fixture of fixtures) {
it(`should roundtrip ${fixture.type}=${fixture.expected}`, () => {
assert.ok(decode(encode(fixture.expected)) === fixture.expected, `roundtrip ${fixture.type}`)
})
}
})
describe('toobig', () => {
it('bigger than 64-bit', () => {
// boundary condition is right below 64-bit negint
assert.doesNotThrow(() => encode(BigInt('-18446744073709551616') /* BigInt(-1) * BigInt(2) ** BigInt(64) */))
assert.throws(() => encode(BigInt('-18446744073709551617') /* BigInt(-1) * BigInt(2) ** BigInt(64) - BigInt(1) */), /BigInt larger than allowable range/)
})
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode(data, { allowBigInt: false }), /safe integer range/)
} else {
assert.ok(decode(data, { allowBigInt: false }) === fixture.expected, `decode ${fixture.type}`)
}
}
})
})
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected)
it(`should encode ${small}n`, () => {
assert.strictEqual(toHex(encode(BigInt(small))), fixture.data, `encode ${small}`)
})
}
}
})
})
+198
View File
@@ -0,0 +1,198 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { useBuffer, fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
// some from https://github.com/PJK/libcbor
const fixtures = [
{ data: '40', expected: '', type: 'bytes' },
{ data: '41a1', expected: 'a1', type: 'bytes' },
{ data: '5801a1', expected: 'a1', type: 'bytes', strict: false },
{
data: '58ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 8-bit length'
},
{
data: '5900ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 16-bit length',
strict: false
},
{
data: '5a000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 32-bit length',
strict: false
},
{
data: '5b00000000000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 64-bit length',
strict: false
}
]
// fill up byte arrays we can validate in strict mode, the minimal size for each
// excluding 64-bit because 4G is just too big
;(() => {
function rnd (length) {
return new Uint8Array(Array.from({ length }, () => Math.floor(Math.random() * 255)))
}
const expected16 = rnd(256)
fixtures.push({
data: new Uint8Array([...fromHex('590100'), ...expected16]),
expected: expected16,
type: 'bytes',
label: 'long bytes, 16-bit length strict-compat'
})
const expected32 = rnd(65536)
fixtures.push({
data: new Uint8Array([...fromHex('5a00010000'), ...expected32]),
expected: expected32,
type: 'bytes',
label: 'long bytes, 32-bit length strict-compat'
})
})()
describe('bytes', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.label || fixture.expected}`, () => {
let actual = decode(data)
assert.strictEqual(
toHex(actual),
toHex(fromHex(fixture.expected)),
`decode ${fixture.type}`)
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)')
} else {
actual = decode(data, { strict: true })
assert.strictEqual(
toHex(actual),
toHex(fromHex(fixture.expected)),
`decode ${fixture.type} strict`)
}
})
it('should fail to decode very large length', () => {
assert.throws(
() => decode(fromHex('5ba5f702b3a5f702b3000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe')),
/CBOR decode error: 64-bit integer bytes lengths not supported/)
})
}
})
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large bytes ${fixture.type}=${fixture.label || fixture.expected}`, () => {})
continue
}
const data = fromHex(fixture.expected)
const expectedHex = toHex(fixture.data)
it(`should encode ${fixture.type}=${fixture.label || fixture.expected}`, () => {
if (fixture.unsafe) {
assert.throws(() => encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/)
} else if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(data)), expectedHex, `encode ${fixture.type} !strict`)
} else {
assert.strictEqual(toHex(encode(data)), expectedHex, `encode ${fixture.type}`)
}
})
}
})
describe('typedarrays', () => {
const cases = [
{
obj: Uint8Array.from([1, 2, 3]),
hex: '43010203'
},
{
obj: Uint8ClampedArray.from([1, 2, 3]),
hex: '43010203'
},
{
obj: Uint16Array.from([1, 2, 3]),
hex: '46010002000300'
},
{
obj: Uint32Array.from([1, 2, 3]),
hex: '4c010000000200000003000000'
},
{
obj: Int8Array.from([1, 2, -3]),
hex: '430102fd'
},
{
obj: Int16Array.from([1, 2, -3]),
hex: '4601000200fdff'
},
{
obj: Int32Array.from([1, 2, -3]),
hex: '4c0100000002000000fdffffff'
},
{
obj: Float32Array.from([1, 2, -3]),
hex: '4c0000803f00000040000040c0'
},
{
obj: Float64Array.from([1, 2, -3]),
hex: '5818000000000000f03f000000000000004000000000000008c0'
},
{
obj: BigUint64Array.from([BigInt(1), BigInt(2), BigInt(3)]),
hex: '5818010000000000000002000000000000000300000000000000'
},
{
obj: BigInt64Array.from([BigInt(1), BigInt(2), BigInt(-3)]),
hex: '581801000000000000000200000000000000fdffffffffffffff'
},
{
obj: new DataView(Uint8Array.from([1, 2, 3]).buffer),
hex: '43010203'
},
{
obj: Uint8Array.from([1, 2, 3]).buffer,
hex: '43010203'
}
]
for (const testCase of cases) {
it(testCase.obj.constructor.name, () => {
assert.equal(toHex(encode(testCase.obj)), testCase.hex)
const decoded = decode(fromHex(testCase.hex))
assert.instanceOf(decoded, Uint8Array)
assert.equal(toHex(decoded), toHex(testCase.obj))
})
}
})
if (useBuffer) {
describe('buffer', () => {
it('can encode Node.js Buffers', () => {
const obj = global.Buffer.from([1, 2, 3])
assert.equal(toHex(encode(obj)), '43010203')
const decoded = decode(fromHex('43010203'))
assert.instanceOf(decoded, Uint8Array)
assert.equal(toHex(decoded), toHex(obj))
})
})
}
})
+136
View File
@@ -0,0 +1,136 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
// some from https://github.com/PJK/libcbor
const fixtures = [
{ data: '60', expected: '', type: 'string' },
{ data: '6161', expected: 'a', type: 'string' },
{ data: '780161', expected: 'a', type: 'string', strict: false },
{
data: '6c48656c6c6f20776f726c6421',
expected: 'Hello world!',
type: 'string'
},
{
data: '6fc48c6175657320c39f76c49b746521',
expected: 'Čaues ßvěte!',
type: 'string'
},
{
data: '78964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 8-bit length'
},
{
data: '7900964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 16-bit length',
strict: false
},
{
data: '7a000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 32-bit length',
strict: false
},
{
data: '7b00000000000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 64-bit length',
strict: false
}
]
// fill up byte arrays converted to strings so we can validate in strict mode,
// the minimal size for each excluding 64-bit because 4G is just too big
;(() => {
function rnd (length) {
const sa = []
let l = 0
while (l < length) {
// some unicode character, unless we're near the end and want to fill up exactly so
// we need to pad with ascii
const ascii = (length - l) < 3
const base = ascii ? 32 : 0x1f000
const max = ascii ? 126 : 0x1ff00
const cc = Math.floor(Math.random() * (max - base)) + base
const s = String.fromCharCode(cc)
l += (new TextEncoder().encode(s)).length
sa.push(s)
}
return sa.join('')
}
const expected16 = rnd(256)
fixtures.push({
data: new Uint8Array([...fromHex('790100'), ...(new TextEncoder().encode(expected16))]),
expected: expected16,
type: 'string',
label: 'long string, 16-bit length strict-compat'
})
const expected32 = rnd(65536)
fixtures.push({
data: new Uint8Array([...fromHex('7a00010000'), ...(new TextEncoder().encode(expected32))]),
expected: expected32,
type: 'string',
label: 'long string, 32-bit length strict-compat'
})
})()
describe('string', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.label || fixture.expected}`, () => {
let actual = decode(data)
assert.strictEqual(actual, fixture.expected, `decode ${fixture.type}`)
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)')
} else {
actual = decode(data, { strict: true })
assert.strictEqual(actual, fixture.expected, `decode ${fixture.type} strict`)
}
})
it('should fail to decode very large length', () => {
assert.throws(
() => decode(fromHex('7ba5f702b3a5f702b34c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e')),
/CBOR decode error: 64-bit integer string lengths not supported/)
})
}
})
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large string ${fixture.type}=${fixture.label || fixture.expected}`, () => {})
continue
}
const data = fixture.expected
const expectedHex = toHex(fixture.data)
it(`should encode ${fixture.type}=${fixture.label || fixture.expected}`, () => {
if (fixture.unsafe) {
assert.throws(() => encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/)
} else if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(data)), expectedHex, `encode ${fixture.type} !strict`)
} else {
assert.strictEqual(toHex(encode(data)), expectedHex, `encode ${fixture.type}`)
}
})
}
})
})
+93
View File
@@ -0,0 +1,93 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
const fixtures = [
{ data: '80', expected: [], type: 'array empty' },
{ data: '8102', expected: [2], type: 'array 1 compact uint' },
{ data: '8118ff', expected: [255], type: 'array 1 uint8' },
{ data: '811901f4', expected: [500], type: 'array 1 uint16' },
{ data: '811a00010000', expected: [65536], type: 'array 1 uint32' },
{ data: '811b00000000000000ff', expected: [255], type: 'array 1 uint64', strict: false },
{ data: '811b0016db6db6db6db7', expected: [Number.MAX_SAFE_INTEGER / 1.4], type: 'array 1 uint64' },
{ data: '811b001fffffffffffff', expected: [Number.MAX_SAFE_INTEGER], type: 'array 1 uint64' },
{ data: '8403040506', expected: [3, 4, 5, 6], type: 'array 4 ints' },
{
data: '8c1b0016db6db6db6db71a000100001901f40200202238ff3aa5f702b33b0016db6db6db6db74261316fc48c6175657320c39f76c49b746521',
expected: [Number.MAX_SAFE_INTEGER / 1.4, 65536, 500, 2, 0, -1, -3, -256, -2784428724, Number.MIN_SAFE_INTEGER / 1.4 - 1, new TextEncoder().encode('a1'), 'Čaues ßvěte!'],
type: 'array mixed terminals',
label: '[]'
},
{
data: '8265617272617982626f66820582666e657374656482666172726179736121',
expected: ['array', ['of', [5, ['nested', ['arrays', '!']]]]],
type: 'array nested'
},
// testing lengths encoded as too-large ints
{ data: '980403040506', expected: [3, 4, 5, 6], type: 'array 4 ints, length8', strict: false },
{ data: '99000403040506', expected: [3, 4, 5, 6], type: 'array 4 ints, length16', strict: false },
{ data: '9a0000000403040506', expected: [3, 4, 5, 6], type: 'array 4 ints, length32', strict: false },
{ data: '9b000000000000000403040506', expected: [3, 4, 5, 6], type: 'array 4 ints, length64', strict: false }
]
describe('array', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.label || fixture.expected}`, () => {
assert.deepStrictEqual(decode(data), fixture.expected, `decode ${fixture.type}`)
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)')
} else {
assert.deepStrictEqual(decode(data, { strict: true }), fixture.expected, `decode ${fixture.type}`)
}
})
it('should fail to decode very large length', () => {
assert.throws(
() => decode(fromHex('9ba5f702b3a5f7020403040506')),
/CBOR decode error: 64-bit integer array lengths not supported/)
})
}
})
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${fixture.type}=${fixture.label || fixture.expected}`, () => {
if (fixture.unsafe) {
assert.throws(encode.bind(null, fixture.expected), Error, /^CBOR encode error: number too large to encode \(\d+\)$/)
} else if (fixture.strict === false) {
assert.notDeepEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type} !strict`)
} else {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type}`)
}
})
}
})
// mostly unnecessary, but feels good
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${fixture.type}=${fixture.label || fixture.expected}`, () => {
assert.deepStrictEqual(decode(encode(fixture.expected)), fixture.expected, `roundtrip ${fixture.type}`)
})
}
}
})
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode(fromHex('9f616f6174ff')), ['o', 't'])
})
it('can switch off indefinite length support', () => {
assert.throws(() => decode(fromHex('9f616f6174ff'), { allowIndefinite: false }), /indefinite/)
})
})
})
+284
View File
@@ -0,0 +1,284 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
// TODO: reject duplicate keys from encoded form
const fixtures = [
{ data: 'a0', expected: {}, type: 'map empty' },
{ data: 'a0', expected: new Map(), type: 'map empty (useMaps)', useMaps: true },
{ data: 'a1616101', expected: { a: 1 }, type: 'map 1 pair' },
{ data: 'a161316161', expected: { 1: 'a' }, type: 'map 1 pair (rev)' },
{
data: 'a1016161',
expected: toMap([[1, 'a']]),
type: 'map 1 pair (int key as Map w/ useMaps)',
useMaps: true
},
{
data: 'a243010203633132334302030463323334',
expected: toMap([[Uint8Array.from([1, 2, 3]), '123'], [Uint8Array.from([2, 3, 4]), '234']]),
type: 'map 2 pair (bytes keys Map w/ useMaps)',
useMaps: true
},
{
data: 'a1666f626a656374a16477697468a26134666e6573746564676f626a65637473a161216121',
expected: { object: { with: { 4: 'nested', objects: { '!': '!' } } } },
type: 'map nested'
},
{ // same as previous but as a Map and an int key
data: 'a1666f626a656374a16477697468a204666e6573746564676f626a65637473a161216121',
expected: toMap([['object', toMap([['with', toMap([[4, 'nested'], ['objects', toMap([['!', '!']])]])]])]]),
type: 'map nested w/ useMaps',
useMaps: true
},
{
data: 'ae636f6e651b0016db6db6db6db763736978206374656e3b0016db6db6db6db76374776f1a0001000064666976650064666f757202646e696e653aa5f702b365656967687438ff65736576656e226574687265651901f466656c6576656e426131667477656c76656fc48c6175657320c39f76c49b74652168666f75727465656ea4616664666f7572616f016174026274680368746869727465656e840203046466697665',
encode: {
one: Number.MAX_SAFE_INTEGER / 1.4,
two: 65536,
three: 500,
four: 2,
five: 0,
six: -1,
seven: -3,
eight: -256,
nine: -2784428724,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
thirteen: [2, 3, 4, 'five'],
fourteen: { o: 1, t: 2, th: 3, f: 'four' }
},
expected: {
one: Number.MAX_SAFE_INTEGER / 1.4,
six: -1,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
two: 65536,
five: 0,
four: 2,
nine: -2784428724,
eight: -256,
seven: -3,
three: 500,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
fourteen: { f: 'four', o: 1, t: 2, th: 3 },
thirteen: [2, 3, 4, 'five']
},
type: 'map with complex entries',
label: '{}'
},
{
data: 'ad01636f6e65026374776f1901f46c666976652068756e647265641902586b7369782068756e647265641a00010000636269671b0016db6db6db6db76662696767657220696d696e7573206f6e6521696d696e75732074776f38ff781f6d696e75732074776f2068756e6472656420616e64206669667479207369783901f4781a6d696e757820666976652068756e6472656420616e64206f6e653901f5781a6d696e757820666976652068756e6472656420616e642074776f3aa5f702b367626967206e65673b0016db6db6db6db76a626967676572206e6567',
encode: toMap([
[2, 'two'],
[1, 'one'],
[-2, 'minus two'],
[-1, 'minus one'],
[600, 'six hundred'],
[500, 'five hundred'],
[-256, 'minus two hundred and fifty six'],
[-502, 'minux five hundred and two'],
[-501, 'minux five hundred and one'],
[65536, 'big'],
[-2784428724, 'big neg'],
[6433713753386423, 'bigger'],
[-6433713753386424, 'bigger neg']
]),
expected: toMap([
[1, 'one'],
[2, 'two'],
[500, 'five hundred'],
[600, 'six hundred'],
[65536, 'big'],
[6433713753386423, 'bigger'],
[-1, 'minus one'],
[-2, 'minus two'],
[-256, 'minus two hundred and fifty six'],
[-501, 'minux five hundred and one'],
[-502, 'minux five hundred and two'],
[-2784428724, 'big neg'],
[-6433713753386424, 'bigger neg']
]),
type: 'map with ints and negints',
useMaps: true
},
{
data: 'a44104636f6e65430102026374776f430102036574687265654301020464666f7572',
encode: toMap([
[Uint8Array.from([1, 2, 3]), 'three'],
[Uint8Array.from([4]), 'one'],
[Uint8Array.from([1, 2, 4]), 'four'],
[Uint8Array.from([1, 2, 2]), 'two']
]),
expected: toMap([
[Uint8Array.from([4]), 'one'],
[Uint8Array.from([1, 2, 2]), 'two'],
[Uint8Array.from([1, 2, 3]), 'three'],
[Uint8Array.from([1, 2, 4]), 'four']
]),
type: 'map with bytes keys',
useMaps: true
},
// testing lengths encoded as too-large ints
{ data: 'b801616101', expected: { a: 1 }, type: 'map 1 pair, length8', strict: false },
{ data: 'b90001616101', expected: { a: 1 }, type: 'map 1 pair, length16', strict: false },
{ data: 'ba00000001616101', expected: { a: 1 }, type: 'map 1 pair, length32', strict: false },
{ data: 'bb0000000000000001616101', expected: { a: 1 }, type: 'map 1 pair, length64', strict: false }
]
function toMap (arr) {
const m = new Map()
for (const [key, value] of arr) {
m.set(key, value)
}
return m
}
function entries (map) {
function nest (a) {
for (const e of a) {
e[0] = entries(e[0])
e[1] = entries(e[1])
}
return a
}
if (Object.getPrototypeOf(map) === Map.prototype) {
return nest([...map.entries()])
}
if (typeof map === 'object') {
return nest([...Object.entries(map)])
}
return map
}
describe('map', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.label || JSON.stringify(fixture.expected)}`, () => {
let options = fixture.useMaps ? { useMaps: true } : undefined
const decoded = decode(data, options)
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(decoded), Map.prototype, 'is Map')
} else {
assert.isObject(decoded, 'is object')
}
assert.deepStrictEqual(entries(decoded), entries(fixture.expected), `decode ${fixture.type}`)
options = Object.assign({ strict: true }, options)
if (fixture.strict === false) {
assert.throws(() => decode(data, options), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)')
} else {
assert.deepStrictEqual(
entries(decode(data, options)),
entries(fixture.expected),
`decode ${fixture.type}`)
}
})
it('should fail to decode very large length', () => {
assert.throws(
() => decode(fromHex('bba5f702b3a5f70201616101')),
/CBOR decode error: 64-bit integer map lengths not supported/)
})
}
it('errors', () => {
assert.throws(() => decode(fromHex('a1016161')), /non-string keys not supported \(got number\)/)
})
})
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${fixture.type}=${fixture.label || JSON.stringify(fixture.expected)}`, () => {
const toEncode = fixture.encode || fixture.expected
if (fixture.unsafe) {
assert.throws(encode.bind(null, toEncode), Error, /^CBOR encode error: number too large to encode \(\d+\)$/)
} else if (fixture.strict === false || fixture.roundtrip === false) {
assert.notDeepEqual(toHex(encode(toEncode)), fixture.data, `encode ${fixture.type} !strict`)
} else {
assert.strictEqual(toHex(encode(toEncode)), fixture.data, `encode ${fixture.type}`)
}
})
}
})
// mostly unnecessary, but feels good
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false && fixture.roundtrip !== false) {
it(`should roundtrip ${fixture.type}=${fixture.label || JSON.stringify(fixture.expected)}`, () => {
const toEncode = fixture.encode || fixture.expected
const options = fixture.useMaps ? { useMaps: true } : undefined
const rt = decode(encode(toEncode), options)
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(rt), Map.prototype, 'is Map')
} else {
assert.isObject(rt, 'is object')
}
assert.deepStrictEqual(entries(rt), entries(fixture.expected), `roundtrip ${fixture.type}`)
})
}
}
})
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode(fromHex('bf616f01617402ff')), { o: 1, t: 2 })
})
it('can switch off indefinite length support', () => {
assert.throws(() => decode(fromHex('bf616f01617402ff'), { allowIndefinite: false }), /indefinite/)
})
})
describe('sorting', () => {
it('sorts int map keys', () => {
assert.strictEqual(toHex(encode(new Map([[1, 1], [2, 2]]))), 'a201010202')
assert.strictEqual(toHex(encode(new Map([[2, 1], [1, 2]]))), 'a201020201')
})
it('sorts negint map keys', () => {
assert.strictEqual(toHex(encode(new Map([[-1, 1], [-2, 2]]))), 'a220012102')
assert.strictEqual(toHex(encode(new Map([[-2, 1], [-1, 2]]))), 'a220022101')
})
it('sorts bytes map keys', () => {
assert.strictEqual(toHex(encode(new Map([[Uint8Array.from([1, 2]), 1], [Uint8Array.from([2, 1]), 2]]))), 'a24201020142020102')
assert.strictEqual(toHex(encode(new Map([[Uint8Array.from([2, 1]), 1], [Uint8Array.from([1, 2]), 2]]))), 'a24201020242020101')
// shortest first
assert.strictEqual(toHex(encode(new Map([[Uint8Array.from([1, 2]), 1], [Uint8Array.from([2, 1]), 2], [Uint8Array.from([200]), 3]]))), 'a341c8034201020142020102')
})
it('sorts bytes map keys', () => {
assert.strictEqual(toHex(encode(new Map([[Uint8Array.from([1, 2]), 1], [Uint8Array.from([2, 1]), 2]]))), 'a24201020142020102')
assert.strictEqual(toHex(encode(new Map([[Uint8Array.from([2, 1]), 1], [Uint8Array.from([1, 2]), 2]]))), 'a24201020242020101')
// shortest first
assert.strictEqual(toHex(encode(new Map([[Uint8Array.from([1, 2]), 1], [Uint8Array.from([2, 1]), 2], [Uint8Array.from([200]), 3]]))), 'a341c8034201020142020102')
})
it('sorts array map keys (length only)', () => {
assert.strictEqual(toHex(encode(new Map([[[1], 1], [[1, 1], 2]]))), 'a281010182010102')
assert.strictEqual(toHex(encode(new Map([[[1, 1], 1], [[1], 2]]))), 'a281010282010101')
})
it('sorts map map keys (length only)', () => {
assert.strictEqual(toHex(encode(new Map([[{ a: 1 }, 1], [{ a: 1, b: 1 }, 2]]))), 'a2a161610101a261610161620102')
assert.strictEqual(toHex(encode(new Map([[{ a: 1, b: 1 }, 1], [{ a: 1 }, 2]]))), 'a2a161610102a261610161620101')
})
// TODO: tag keys .. but why would you do this!?
})
})
+85
View File
@@ -0,0 +1,85 @@
/* eslint-env mocha */
import chai from 'chai'
import { Token, Type } from '../lib/token.js'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
import { dateDecoder, dateEncoder } from './common.js'
const { assert } = chai
function Uint16ArrayDecoder (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 23')
}
const u8a = fromHex(obj)
return new Uint16Array(u8a.buffer, u8a.byteOffset, u8a.length / 2)
}
function Uint16ArrayEncoder (obj) {
if (!(obj instanceof Uint16Array)) {
throw new Error('expected Uint16Array for "Uint16Array" encoder')
}
return [
new Token(Type.tag, 23),
new Token(Type.string, toHex(obj))
]
}
describe('tag', () => {
it('date', () => {
assert.throws(() => encode({ d: new Date() }), /unsupported type: Date/)
assert.equal(
toHex(encode(new Date('2013-03-21T20:04:00Z'), { typeEncoders: { Date: dateEncoder } })),
'c074323031332d30332d32315432303a30343a30305a' // from appendix_a
)
const decodedDate = decode(fromHex('c074323031332d30332d32315432303a30343a30305a'), { tags: { 0: dateDecoder } })
assert.instanceOf(decodedDate, Date)
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString())
})
it('Uint16Array as hex/23 (overide existing type)', () => {
assert.equal(
toHex(encode(Uint16Array.from([1, 2, 3]), { typeEncoders: { Uint16Array: Uint16ArrayEncoder } })),
'd76c303130303032303030333030' // tag(23) + string('010002000300')
)
const decoded = decode(fromHex('d76c303130303032303030333030'), { tags: { 23: Uint16ArrayDecoder } })
assert.instanceOf(decoded, Uint16Array)
assert.equal(toHex(decoded), toHex(Uint16Array.from([1, 2, 3])))
})
it('tag int too large', () => {
const verify = (hex, strict) => {
if (!strict) {
assert.throws(
() => decode(fromHex(hex), { tags: { 8: dateDecoder }, strict: true }),
/integer encoded in more bytes than necessary/)
}
const decodedDate = decode(fromHex(hex), { tags: { 8: dateDecoder }, strict })
assert.instanceOf(decodedDate, Date)
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString())
}
// compact
verify('c874323031332d30332d32315432303a30343a30305a', true)
// int8
verify('d80874323031332d30332d32315432303a30343a30305a', false)
// int16
verify('d9000874323031332d30332d32315432303a30343a30305a', false)
// int32
verify('da0000000874323031332d30332d32315432303a30343a30305a', false)
// int64
verify('db000000000000000874323031332d30332d32315432303a30343a30305a', false)
})
/*
describe('taglib', () => {
it('bigint', () => {
const v = BigInt(2) ** BigInt(80)
})
})
*/
})
+131
View File
@@ -0,0 +1,131 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import { fromHex, toHex } from '../lib/byte-utils.js'
const { assert } = chai
const fixtures = [
{ data: '8601f5f4f6f720', expected: [1, true, false, null, undefined, -1], type: 'array of float specials' },
{ data: 'f93800', expected: 0.5, type: 'float16' },
{ data: 'f9b800', expected: -0.5, type: 'float16' },
{ data: 'fa33c00000', expected: 8.940696716308594e-08, type: 'float32' },
{ data: 'fab3c00000', expected: -8.940696716308594e-08, type: 'float32' },
{ data: 'fb3ff199999999999a', expected: 1.1, type: 'float64' },
{ data: 'fbbff199999999999a', expected: -1.1, type: 'float64' },
{ data: 'fb3ff1c71c71c71c72', expected: 1.11111111111111111111111111111, type: 'float64' }, // eslint-disable-line
{ data: 'fb0000000000000002', expected: 1e-323, type: 'float64' },
{ data: 'fb8000000000000002', expected: -1e-323, type: 'float64' },
{ data: 'fb3fefffffffffffff', expected: 0.9999999999999999, type: 'float64' },
{ data: 'fbbfefffffffffffff', expected: -0.9999999999999999, type: 'float64' },
{ data: 'f97c00', expected: Infinity, type: 'Infinity' }, // special CBOR token for -Infinity
{ data: 'fb7ff0000000000000', expected: Infinity, type: 'Infinity', strict: false }, // an IEEE 754 representation of Infinity
{ data: 'f9fc00', expected: -Infinity, type: '-Infinity' }, // special CBOR token for -Infinity
{ data: 'fbfff0000000000000', expected: -Infinity, type: '-Infinity', strict: false }, // an IEEE 754 representation of Infinity
{ data: 'f97e00', expected: NaN, type: 'NaN' }, // special CBOR token for NaN
{ data: 'f97ff8', expected: NaN, type: 'NaN', strict: false }, // one of the many IEEE 754 representations of NaN
{ data: 'fa7ff80000', expected: NaN, type: 'NaN', strict: false },
{ data: 'fb7ff8000000000000', expected: NaN, type: 'NaN', strict: false },
{ data: 'fb7ff8cafedeadbeef', expected: NaN, type: 'NaN', strict: false }, // yep, that's NaN too
{ data: 'fb40f4241a31a5a515', expected: 82497.63712086187, type: 'float64' }
]
describe('float', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data)
it(`should decode ${fixture.type}=${fixture.expected}`, () => {
assert.deepStrictEqual(decode(data), fixture.expected, `decode ${fixture.type}`)
assert.deepStrictEqual(decode(data, { strict: true }), fixture.expected, `decode ${fixture.type}`)
})
}
})
it('error', () => {
// minor number 28, too high for uint
assert.throws(() => decode(fromHex('f80000')), Error, 'simple values are not supported')
assert.throws(() => decode(fromHex('f900')), Error, 'not enough data for float16')
assert.throws(() => decode(fromHex('fa0000')), Error, 'not enough data for float32')
assert.throws(() => decode(fromHex('fb00000000')), Error, 'not enough data for float64')
})
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should encode ${fixture.type}=${fixture.expected}`, () => {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${fixture.type}`)
})
}
}
})
describe('encode float64', () => {
for (const fixture of fixtures) {
if (fixture.type.startsWith('float')) {
it(`should encode ${fixture.type}=${fixture.expected}`, () => {
const encoded = encode(fixture.expected, { float64: true })
assert.strictEqual(encoded.length, 9) // always encode as 9 bytes, regardless of size
assert.strictEqual(encoded[0], 0xfb)
assert.strictEqual(decode(encoded), fixture.expected, `encode float64 ${fixture.type}`)
})
}
}
})
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${fixture.type}=${fixture.expected}`, () => {
assert.deepStrictEqual(decode(encode(fixture.expected)), fixture.expected, `roundtrip ${fixture.type}`)
})
}
}
})
describe('specials', () => {
// This is a bit of a hack, the CBOR is invalid because it's a standard fixed-length array
// followed by a BREAK, which should normally error ("too many terminals"), but we want to
// exercise the allowIndefinite switch in the major-7 decode and it should error before it
// even gets to looking at terminals and whether the tokens make sense.
it('indefinite length switch fails on BREAK', () => {
// sanity check, BREAK doesn't belong there
assert.throws(() => decode(Uint8Array.from([131, 1, 2, 0xff])), /unexpected break to lengthed array/)
// throw earlier because we're disallowing BREAK entirely
assert.throws(() => decode(Uint8Array.from([131, 1, 2, 0xff]), { allowIndefinite: false }), /indefinite/)
})
it('can switch off undefined support', () => {
assert.deepStrictEqual(decode(fromHex('f7')), undefined)
assert.throws(() => decode(fromHex('f7'), { allowUndefined: false }), /undefined/)
assert.deepStrictEqual(decode(fromHex('830102f7')), [1, 2, undefined])
assert.throws(() => decode(fromHex('830102f7'), { allowUndefined: false }), /undefined/)
})
it('can coerce undefined to null', () => {
assert.deepStrictEqual(decode(fromHex('f7'), { coerceUndefinedToNull: false }), undefined)
assert.deepStrictEqual(decode(fromHex('f7'), { coerceUndefinedToNull: true }), null)
assert.deepStrictEqual(decode(fromHex('830102f7'), { coerceUndefinedToNull: false }), [1, 2, undefined])
assert.deepStrictEqual(decode(fromHex('830102f7'), { coerceUndefinedToNull: true }), [1, 2, null])
})
it('can switch off Infinity support', () => {
assert.deepStrictEqual(decode(fromHex('830102f97c00')), [1, 2, Infinity])
assert.deepStrictEqual(decode(fromHex('830102f9fc00')), [1, 2, -Infinity])
assert.throws(() => decode(fromHex('830102f97c00'), { allowInfinity: false }), /Infinity/)
assert.throws(() => decode(fromHex('830102f9fc00'), { allowInfinity: false }), /Infinity/)
for (const fixture of fixtures.filter((f) => f.type.endsWith('Infinity'))) {
assert.throws(() => decode(fromHex(fixture.data), { allowInfinity: false }), /Infinity/)
}
})
it('can switch off NaN support', () => {
assert.deepStrictEqual(decode(fromHex('830102f97e00')), [1, 2, NaN])
assert.throws(() => decode(fromHex('830102f97e00'), { allowNaN: false }), /NaN/)
for (const fixture of fixtures.filter((f) => f.type === 'NaN')) {
assert.throws(() => decode(fromHex(fixture.data), { allowNaN: false }), /NaN/)
}
})
})
})
+37
View File
@@ -0,0 +1,37 @@
/* eslint-env mocha */
import chai from 'chai'
import { Bl } from '../lib/bl.js'
const { assert } = chai
describe('Internal bytes list', () => {
describe('push', () => {
it('push bits', () => {
const bl = new Bl(10)
const expected = []
for (let i = 0; i < 25; i++) {
bl.push([i + 1])
expected.push(i + 1)
}
assert.deepEqual([...bl.toBytes()], expected)
})
for (let i = 4; i < 21; i++) {
it(`push Bl(${i})`, () => {
const bl = new Bl(i)
const expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 110, 120, 11, 12, 130, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
for (let i = 0; i < 5; i++) {
bl.push([i + 1])
}
bl.push(Uint8Array.from([6, 7, 8, 9, 10]))
bl.push([100])
bl.push(Uint8Array.from([110, 120]))
bl.push(Uint8Array.from([11, 12]))
bl.push([130])
bl.push(Uint8Array.from([13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]))
assert.deepEqual([...bl.toBytes()], expected)
})
}
})
})
+107
View File
@@ -0,0 +1,107 @@
/* eslint-env mocha,es2020 */
import chai from 'chai'
import { decode, encode } from '../cborg.js'
import * as taglib from 'cborg/taglib'
import { fromHex, toHex } from '../lib/byte-utils.js'
// fixtures from https://github.com/cbor/test-vectors
import { fixtures } from './appendix_a.js'
const { assert } = chai
const tags = []
const typeEncoders = {}
tags[0] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1')
}
return `0("${new Date(obj).toISOString().replace(/\.000Z$/, 'Z')}")`
}
tags[1] = function (obj) {
if (typeof obj !== 'number') {
throw new Error('expected number for tag 1')
}
return `1(${obj})`
}
tags[2] = taglib.bigIntDecoder
typeEncoders.bigint = taglib.bigIntEncoder
tags[3] = taglib.bigNegIntDecoder
tags[23] = function (obj) {
// expected conversion to base16
if (!(obj instanceof Uint8Array)) {
throw new Error('expected byte array for tag 23')
}
return `23(h'${toHex(obj)}')`
}
tags[24] = function (obj) { // embedded cbor, oh my
return tags[23](obj).replace(/^23/, '24')
}
tags[32] = function (obj) { // url
if (typeof obj !== 'string') {
throw new Error('expected string for tag 32')
}
;(() => new URL(obj))() // will throw if not a url
return `32("${obj}")`
}
describe('cbor/test-vectors', () => {
let i = 0
for (const fixture of fixtures) {
const u8a = fromHex(fixture.hex)
let expected = fixture.decoded !== undefined ? fixture.decoded : fixture.diagnostic
if (typeof expected === 'string' && expected.startsWith('h\'')) {
expected = fromHex(expected.replace(/(^h)'|('$)/g, ''))
}
it(`test vector #${i}: ${inspect(expected).replace(/\n\s*/g, '')}`, () => {
if (fixture.error) {
assert.throws(() => decode(u8a, { tags }), fixture.error)
} else {
if (fixture.noTagDecodeError) {
assert.throws(() => decode(u8a), fixture.noTagDecodeError)
}
let actual = decode(u8a, { tags })
if (typeof actual === 'bigint') {
actual = inspect(actual)
}
if (typeof expected === 'bigint') {
expected = inspect(expected)
}
assert.deepEqual(actual, expected)
if (fixture.roundtrip) {
if (fixture.noTagEncodeError) {
assert.throws(() => encode(decode(u8a, { tags })), fixture.noTagEncodeError)
}
const reencoded = encode(decode(u8a, { tags }), { typeEncoders })
assert.equal(toHex(reencoded), fixture.hex)
}
}
})
i++
}
it.skip('encode w/ tags', () => {
})
})
function inspect (o) {
if (typeof o === 'string') {
return `'${o}'`
}
if (o instanceof Uint8Array) {
return `Uint8Array<${o.join(',')}>`
}
if (o == null || typeof o !== 'object') {
return String(o)
}
return JSON.stringify(o)
}
+65
View File
@@ -0,0 +1,65 @@
/* eslint-env mocha */
import chai from 'chai'
import { decode } from '../cborg.js'
import { fromHex } from '../lib/byte-utils.js'
const { assert } = chai
describe('decode errors', () => {
it('not Uint8Array', () => {
for (const arg of [true, false, null, undefined, 'string', { obj: 'ect' }, {}, ['array'], [], [1, 2, 3], 0, 100, 1.1, -1, Symbol.for('nope')]) {
assert.throws(() => decode(arg), /CBOR decode error.*must be a Uint8Array/)
}
})
it('no data', () => {
assert.throws(() => decode(new Uint8Array('')), /CBOR decode error.*content/)
})
it('break only', () => {
assert.throws(() => decode(new Uint8Array([255])), /CBOR decode error.*break/)
})
it('not enough map entries (value)', () => {
// last value missing
assert.throws(() => decode(fromHex('a2616f016174')), /map.*not enough entries.*value/)
})
it('not enough map entries (key)', () => {
// last key & value missing
assert.throws(() => decode(fromHex('a2616f01')), /map.*not enough entries.*key/)
})
it('break in lengthed map', () => {
// 0xff (break) in the middle
assert.throws(() => decode(fromHex('a2616f01ff740f')), /unexpected break to lengthed map/)
})
it('not enough array entries', () => {
// last value missing
assert.throws(() => decode(fromHex('82616f')), /array.*not enough entries/)
})
it('break in lengthed array', () => {
// last value missing
assert.throws(() => decode(fromHex('82ff')), /unexpected break to lengthed array/)
})
it('no such decoder', () => {
// last value missing
assert.throws(() => decode(fromHex('82ff')), /unexpected break to lengthed array/)
})
it('too many terminals', () => {
// two '1's
assert.throws(() => decode(fromHex('0101')), /too many terminals/)
})
it('rejectDuplicateMapKeys enabled on duplicate keys', () => {
assert.deepStrictEqual(decode(fromHex('a3636261720363666f6f0163666f6f02')), { foo: 2, bar: 3 })
assert.throws(() => decode(fromHex('a3636261720363666f6f0163666f6f02'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/)
assert.throws(() => decode(fromHex('a3636261720363666f6f0163666f6f02'), { useMaps: true, rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/)
})
})
+42
View File
@@ -0,0 +1,42 @@
/* eslint-env mocha */
import { garbage } from 'ipld-garbage'
import { decode, encode } from '../cborg.js'
import chai from 'chai'
const { assert } = chai
describe('Fuzz round-trip', () => {
it('random objects', function () {
this.timeout(5000)
for (let i = 0; i < 1000; i++) {
const obj = garbage(300, { weights: { CID: 0 } })
const byts = encode(obj)
const decoded = decode(byts)
assert.deepEqual(decoded, obj)
}
})
it('circular references error', () => {
let obj = {}
obj.obj = obj
assert.throws(() => encode(obj), /circular references/)
obj = { blip: [1, 2, { blop: {} }] }
obj.blip[2].blop.boop = obj
assert.throws(() => encode(obj), /circular references/)
obj = { blip: [1, 2, { blop: {} }] }
obj.blip[2].blop.boop = obj.blip
assert.throws(() => encode(obj), /circular references/)
// not circular but a naive check might decide it is
obj = { blip: {}, bloop: {} }
obj.bloop = obj.blip
assert.doesNotThrow(() => encode(obj))
const arr = []
arr[0] = arr
assert.throws(() => encode(arr), /circular references/)
})
})
+191
View File
@@ -0,0 +1,191 @@
/* eslint-env mocha,es2020 */
import { assert } from 'chai'
import { decode, encode } from 'cborg/json'
const toBytes = (str) => new TextEncoder().encode(str)
function verifyRoundTrip (obj, sorting) {
const encoded = new TextDecoder().decode(encode(obj, sorting === false ? { mapSorter: null } : undefined))
const json = JSON.stringify(obj)
assert.strictEqual(encoded, json)
const decoded = decode(toBytes(JSON.stringify(obj)))
assert.deepStrictEqual(decoded, obj)
}
function verifyEncodedForm (testCase) {
// round trip the original object
const obj = JSON.parse(testCase)
const encoded = encode(obj)
assert.strictEqual(new TextDecoder().decode(encoded), JSON.stringify(obj))
const decoded = decode(encoded)
assert.deepStrictEqual(decoded, obj)
// now look at the original form
const decoded2 = decode(toBytes(testCase))
assert.deepStrictEqual(decoded2, obj)
}
describe('json basics', () => {
it('can round-trip basic literals', () => {
const testCases = [
'null',
'true',
'false',
'0',
'9007199254740991',
'-9007199254740991',
JSON.stringify(Number.MAX_VALUE),
JSON.stringify(Number.MIN_VALUE)
]
for (const testCase of testCases) {
verifyEncodedForm(testCase)
}
assert.strictEqual(decode(toBytes('1E1')), 10)
assert.strictEqual(decode(toBytes('0.1e1')), 1)
assert.strictEqual(decode(toBytes('1e-1')), 0.1)
assert.strictEqual(decode(toBytes('1e+00')), 1)
assert.strictEqual(decode(toBytes('10.0')), 10)
assert.deepStrictEqual(decode(toBytes('[-10.0,1.0,0.0,100.0]')), [-10, 1, 0, 100])
verifyRoundTrip(true)
verifyRoundTrip(false)
verifyRoundTrip(null)
verifyRoundTrip(100)
verifyRoundTrip(-100)
verifyRoundTrip(1.11)
verifyRoundTrip(-100.11111)
verifyRoundTrip(1.11e10)
verifyRoundTrip(1.0011111e-18)
})
it('handles large integers as BigInt', () => {
const verify = (inp, str) => {
if (str === undefined) {
str = String(inp)
}
assert.strictEqual(decode(toBytes(str), { allowBigInt: true }), inp)
assert.strictEqual(decode(toBytes(str)), parseFloat(str)) // no BigInt for you
}
verify(Number.MAX_SAFE_INTEGER)
verify(-Number.MAX_SAFE_INTEGER)
verify(BigInt('9007199254740992')) // Number.MAX_SAFE_INTEGER+1
verify(BigInt('9007199254740993'))
verify(BigInt('11959030306112471731'))
verify(BigInt('18446744073709551615')) // max uint64
verify(BigInt('9223372036854775807')) // max int64
verify(BigInt('-9007199254740992'))
verify(BigInt('-9007199254740993'))
verify(BigInt('-9223372036854776000')) // min int64
verify(BigInt('-11959030306112471732'))
verify(BigInt('-18446744073709551616')) // min -uint64
// these are "floats", distinct from "ints" which wouldn't have the `.` in them
verify(-9007199254740992, '-9007199254740992.0')
verify(-9223372036854776000, '-9223372036854776000.0')
verify(-18446744073709551616, '-18446744073709551616.0')
})
it('can round-trip string literals', () => {
const testCases = [
JSON.stringify(''),
JSON.stringify(' '),
JSON.stringify('"'),
JSON.stringify('\\'),
JSON.stringify('\b\f\n\r\t'),
JSON.stringify('\u0022'),
JSON.stringify('&#34; %22 0x22 034 &#x22;'),
'"😀"'
]
for (const testCase of testCases) {
verifyEncodedForm(testCase)
}
assert.strictEqual(decode(toBytes('"/ & \\/"')), '/ & /')
verifyRoundTrip('this is a string')
verifyRoundTrip('this 𝄞 is a ☺☺ ♣ string ̐ ̀\n\r') // accents like ̀ and ̐ use two-byte utf8
verifyRoundTrip('')
verifyRoundTrip('foo\\bar\nbaz\tbop\rbing"bip\'bang')
})
it('can round-trip array literals', () => {
const testCases = [
'[]',
'[null]',
'[true, false]',
'[ \n 0,1, 2\n , 3,\n4] \n ',
'[-10.0, 1.0, 0.0, 100.0]',
'[["2 deep"]]'
]
for (const testCase of testCases) {
verifyEncodedForm(testCase)
}
verifyRoundTrip([1, 2, 3, 'string', true, 4])
verifyRoundTrip([1, 2, 3, 'string', true, ['and', 'a', 'nested', 'array', true], 4])
})
it('can round-trip object literals', () => {
const testCases = [
'{}',
'\n {\n "\\b"\n :\n""\n }\n ',
'{"":""}',
'{"1":{"2":0,"3":"deep"}}'
]
for (const testCase of testCases) {
verifyEncodedForm(testCase)
}
})
it('will sort map keys', () => {
const unsorted = { one: 1, two: 2, three: 3.1, str: 'string', bool: true, four: 4 }
verifyRoundTrip(unsorted, false)
assert.strictEqual(
new TextDecoder().decode(encode(unsorted)),
'{"bool":true,"four":4,"one":1,"str":"string","three":3.1,"two":2}')
})
it('can handle novel cases', () => {
assert.strictEqual(decode(toBytes('"this \\uD834\\uDD1E is a \\u263a\\u263a string"')), 'this 𝄞 is a ☺☺ string')
verifyRoundTrip({ one: 1, two: 2, three: 3.1, str: 'string', arr: ['and', 'a', 'nested', [], 'array', [true, 1], false], bool: true, obj: { nested: 'object', a: [], o: {} }, four: 4 }, false)
verifyRoundTrip([false, [{ '#nFzU': {}, '\\w>': -0.9441451951197325, "\t'": "'JB+2Wg\tw\"IrM*#e^L/d&4rrzUuwq(1mH6aVRredB&Bfs]S\"KqK(Tz1Q\"URBAfw", '\n@FrfM': 'M[D]q&' }, "J4>'Xdc+u2$%", 4227406737130333]], false)
verifyRoundTrip([0.12995619865708727, -4973404279772543, { drG2: [true], ';#K^Qf>V': null, '`2=': 'ecc<e/$+-.;U>Gr5RdZDJ\n5+:{=QHNN.tVVN~dX$FWFwu`6>"&=tW!*1*^☺)JFM1p|}&X.B|${*\\f@!w2☺+' }], false)
assert.strictEqual(`${decode(encode(9007199254740991))}`, '9007199254740991')
assert.strictEqual(`${decode(encode(9007199254740992))}`, '9007199254740992')
assert.strictEqual(`${decode(encode(900719925474099100n))}`, '900719925474099100')
})
it('should throw on bad types', () => {
assert.throws(() => encode(new Uint8Array([1, 2])), /CBOR encode error: unsupported type: Uint8Array/)
assert.throws(() => encode({ boop: new Uint8Array([1, 2]) }), /CBOR encode error: unsupported type: Uint8Array/)
assert.throws(() => encode(undefined), /CBOR encode error: unsupported type: undefined/)
assert.throws(() => encode(new Map([[1, 2], [2, 3]])), /CBOR encode error: non-string map keys are not supported/)
assert.throws(() => encode(new Map([[['foo', 'bar'], 2], [['bar', 'foo'], 3]])), /CBOR encode error: complex map keys are not supported/)
})
it('should throw on bad decode failure modes', () => {
assert.throws(() => decode(toBytes('{"a":1 & "b":2}')), 'CBOR decode error: unexpected character at position 7, was expecting object delimiter but found \'&\'')
assert.throws(() => decode(toBytes('{"a":1,"b"!2}')), 'CBOR decode error: unexpected character at position 10, was expecting key/value delimiter \':\' but found \'!\'')
assert.throws(() => decode(toBytes('[1,2&3]')), 'CBOR decode error: unexpected character at position 4, was expecting array delimiter but found \'&\'')
assert.throws(() => decode(toBytes('{"a":!}')), 'CBOR decode error: unexpected character at position 5')
assert.throws(() => decode(toBytes('"abc')), 'CBOR decode error: unexpected end of string at position 4')
assert.throws(() => decode(toBytes('"ab\\xc"')), 'CBOR decode error: unexpected string escape character at position 5')
assert.throws(() => decode(toBytes('"ab\x1Ec"')), 'CBOR decode error: invalid control character at position 3')
assert.throws(() => decode(toBytes('"ab\\')), 'CBOR decode error: unexpected string termination at position 4')
// begining of a quoted string with 3-byte unicode sequence with the last one truncated
assert.throws(() => decode(toBytes('"☺').subarray(0, 3)), 'CBOR decode error: unexpected unicode sequence at position 1')
assert.throws(() => decode(toBytes('"\\uxyza"')), 'CBOR decode error: unexpected unicode escape character at position 3')
assert.throws(() => decode(toBytes('"\\u11"')), 'CBOR decode error: unexpected end of unicode escape sequence at position 3')
assert.throws(() => decode(toBytes('-boop')), 'CBOR decode error: unexpected token at position 1')
assert.throws(() => decode(toBytes('{"v":nope}')), 'CBOR decode error: unexpected token at position 7, expected to find \'null\'')
assert.throws(() => decode(toBytes('[n]')), 'CBOR decode error: unexpected end of input at position 1')
assert.throws(() => decode(toBytes('{"v":truu}')), 'CBOR decode error: unexpected token at position 9, expected to find \'true\'')
assert.throws(() => decode(toBytes('[tr]')), 'CBOR decode error: unexpected end of input at position 1')
assert.throws(() => decode(toBytes('{"v":flase}')), 'CBOR decode error: unexpected token at position 7, expected to find \'false\'')
assert.throws(() => decode(toBytes('[fa]')), 'CBOR decode error: unexpected end of input at position 1')
assert.throws(() => decode(toBytes('-0..1')), 'CBOR decode error: unexpected token at position 3')
})
it('should throw when rejectDuplicateMapKeys enabled on duplicate keys', () => {
assert.deepStrictEqual(decode(toBytes('{"foo":1,"foo":2}')), { foo: 2 })
assert.throws(() => decode(toBytes('{"foo":1,"foo":2}'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/)
})
})
+65
View File
@@ -0,0 +1,65 @@
/* eslint-env mocha */
import chai from 'chai'
import { garbage } from 'ipld-garbage'
import { uintBoundaries } from '../lib/0uint.js'
import { encode } from '../cborg.js'
import { encodedLength } from '../lib/length.js'
import { dateEncoder } from './common.js'
const { assert } = chai
function verifyLength (object, options) {
const len = encodedLength(object, options)
const encoded = encode(object, options)
const actual = encoded.length
assert.strictEqual(actual, len, JSON.stringify(object))
}
describe('encodedLength', () => {
it('int boundaries', () => {
for (let ii = 0; ii < 4; ii++) {
verifyLength(uintBoundaries[ii])
verifyLength(uintBoundaries[ii] - 1)
verifyLength(uintBoundaries[ii] + 1)
verifyLength(-1 * uintBoundaries[ii])
verifyLength(-1 * uintBoundaries[ii] - 1)
verifyLength(-1 * uintBoundaries[ii] + 1)
}
})
it('tags', () => {
verifyLength({ date: new Date('2013-03-21T20:04:00Z') }, { typeEncoders: { Date: dateEncoder } })
})
it('floats', () => {
verifyLength(0.5)
verifyLength(0.5, { float64: true })
verifyLength(8.940696716308594e-08)
verifyLength(8.940696716308594e-08, { float64: true })
})
it('small garbage', function () {
this.timeout(10000)
for (let ii = 0; ii < 1000; ii++) {
const gbg = garbage(1 << 6, { weights: { CID: 0 } })
verifyLength(gbg)
}
})
it('medium garbage', function () {
this.timeout(10000)
for (let ii = 0; ii < 100; ii++) {
const gbg = garbage(1 << 16, { weights: { CID: 0 } })
verifyLength(gbg)
}
})
it('large garbage', function () {
this.timeout(10000)
for (let ii = 0; ii < 10; ii++) {
const gbg = garbage(1 << 20, { weights: { CID: 0 } })
verifyLength(gbg)
}
})
})