Files
Matthieu Sieben 7310b9704d Fix behavior of "default" and assignment to LexValue (#4562)
* Fix inability to assign (object containing) open union results to `LexMap` type

* properly support and type default values

* strong cohesion

* changeset

* tidy

* Add validation error as cause when handling invalid records

* tidy

* Update `cborg` dependency, fixing encoding of strings with invalid surrogate pairs, and ignoring `undefined` object properties

* tidy

* Simplify encoding logic of `number`

* Improvements around usage of `noUndefinedVals`

* Memoize array schemas (without options)

* Memoize empty params schemas

* tidy

* tidy
2026-01-20 13:20:56 +01:00

124 lines
3.4 KiB
TypeScript

import { assert, describe, expect, it } from 'vitest'
import { LexValue, isLexMap, parseCid } from '@atproto/lex-data'
import { decode, decodeAll, encode } from '../src/index.js'
describe('encode', () => {
it('encodes data to CBOR format', () => {
expect(encode({ hello: 'world' })).toEqual(
Uint8Array.from([
0xa1, 0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x65, 0x77, 0x6f, 0x72, 0x6c,
0x64,
]),
)
})
it('throws when encoding floats', () => {
expect(() => encode({ value: 3.14 })).toThrow()
})
it('Supports encoding "undefined" values', () => {
expect(encode({ value: undefined })).toStrictEqual(encode({}))
expect(encode({ a: 1, value: undefined })).toStrictEqual(encode({ a: 1 }))
expect(encode({ foo: { bar: undefined } })).toStrictEqual(
encode({ foo: {} }),
)
})
it('throws when encoding Maps with non-string keys', () => {
expect(() =>
// @ts-expect-error
encode({
foo: new Map<any, any>([
[42, 'value'],
['key', 'value2'],
]),
}),
).toThrow()
})
})
describe('decode', () => {
it('decodes CBOR data to original format', () => {
const bytes = Uint8Array.from([
0xa1, 0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x65, 0x77, 0x6f, 0x72, 0x6c,
0x64,
])
expect(decode(bytes)).toEqual({ hello: 'world' })
})
})
describe('identity', () => {
for (const vector of [
null,
parseCid('bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'),
[
parseCid('bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'),
parseCid('bafyreigoxt64qghytzkr6ik7qvtzc7lyytiq5xbbrokbxjows2wp7vmo6q'),
parseCid('bafyreiaizynclnqiolq7byfpjjtgqzn4sfrsgn7z2hhf6bo4utdwkin7ke'),
parseCid('bafyreifd4w4tcr5tluxz7osjtnofffvtsmgdqcfrfi6evjde4pl27lrjpy'),
],
new Uint8Array(Buffer.from('hello world')),
true,
false,
0,
42,
-1,
'',
'hello world',
[],
[1, 2, 3],
{},
{ a: 1, b: 'two', c: true },
{
nested: {
array: { value: [1, 2, 3] },
object: { key: 'value' },
cid: parseCid(
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
),
bytes: new Uint8Array(Buffer.from('byte array')),
},
},
] as LexValue[]) {
it(JSON.stringify(vector), () => {
const cbor = encode(vector)
const decoded = decode(cbor)
expect(decoded).toEqual(vector)
expect(encode(decoded)).toEqual(cbor)
})
}
})
describe('ipld decode multi', () => {
it('decodes concatenated dag-cbor messages', async () => {
const one = {
a: 123,
b: parseCid(
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
),
}
const two = {
c: new Uint8Array([1, 2, 3]),
d: parseCid(
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
),
}
const encoded = Buffer.concat([encode(one), encode(two)])
const decoded = Array.from(decodeAll(encoded))
expect(decoded.length).toBe(2)
expect(decoded[0]).toEqual(one)
expect(decoded[1]).toEqual(two)
})
it('parses safe ints as number', async () => {
const one = {
test: Number.MAX_SAFE_INTEGER,
}
const encoded = encode(one)
const { length, 0: first } = Array.from(decodeAll(encoded))
expect(length).toBe(1)
assert(isLexMap(first))
expect(Number.isInteger(first.test)).toBe(true)
})
})