@cloudpss/ubjson
Version:
588 lines (515 loc) • 19.8 kB
text/typescript
/**
* Tests from https://bitbucket.org/shelacek/ubjson
*/
import { encode, decode } from '../dist/index.js';
import { getEncoder } from '../dist/encoder.js';
import { toArray } from './.utils.ts';
// @ts-expect-error Access private property
const poolInit = getEncoder().pool as Uint8Array<ArrayBuffer>;
test('encode function', () => {
expect(() =>
encode(function () {
//noop
}),
).toThrow();
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode bigint', () => {
expect(toArray(encode(1n))).toEqual(toArray(encode(1)));
expect(toArray(encode(0x1234_5678_90ab_cdefn))).toEqual(
toArray('L', 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef),
);
expect(() => encode(0x8234_5678_90ab_cdefn)).toThrow(/BigInt value out of range:/);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode symbol', () => {
expect(() => encode(Symbol('sym'))).toThrow();
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode undefined', () => {
expect(toArray(encode(undefined))).toEqual(toArray('N'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode null', () => {
expect(toArray(encode(null))).toEqual(toArray('Z'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode true', () => {
expect(toArray(encode(true))).toEqual(toArray('T'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode false', () => {
expect(toArray(encode(false))).toEqual(toArray('F'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode int8', () => {
expect(toArray(encode(-1))).toEqual(toArray('i', 255));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode uint8', () => {
expect(toArray(encode(200))).toEqual(toArray('U', 200));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode int16', () => {
expect(toArray(encode(0x1234))).toEqual(toArray('I', 0x12, 0x34));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode int32', () => {
expect(toArray(encode(0x1234_5678))).toEqual(toArray('l', 0x12, 0x34, 0x56, 0x78));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode float32', () => {
expect(toArray(encode(1.003_906_25))).toEqual(toArray('d', 0x3f, 0x80, 0x80, 0x00));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode float32 (too large integer)', () => {
expect(toArray(encode(2_147_483_648))).toEqual(toArray('d', 0x4f, 0x00, 0x00, 0x00));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode float64', () => {
expect(toArray(encode(100_000.003_906_25))).toEqual(toArray('D', 0x40, 0xf8, 0x6a, 0x00, 0x10, 0x00, 0x00, 0x00));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode char', () => {
expect(toArray(encode('a'))).toEqual(toArray('C', 'a'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode char 127', () => {
expect(toArray(encode('\u007F'))).toEqual(toArray('C', '\u007F'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode char 257', () => {
expect(toArray(encode('\u0123'))).toEqual(toArray('S', 'i', 2, 196, 163));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode char emoji', () => {
expect(toArray(encode('💖'))).toEqual(toArray('S', 'i', 4, 240, 159, 146, 150));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode string', () => {
expect(toArray(encode('ubjson'))).toEqual(toArray('S', 'i', 6, ...'ubjson'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode string (len = 60)', () => {
// 不生成 u8
const str = 'ubjson'.repeat(10);
expect(toArray(encode(str))).toEqual(toArray('S', 'i', str.length, ...str));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode string (len = 120)', () => {
// 不生成 u8
const str = 'ubjson'.repeat(20);
expect(toArray(encode(str))).toEqual(toArray('S', 'i', str.length, ...str));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode string (len = 180)', () => {
// 不生成 u8
const str = 'ubjson'.repeat(30);
expect(toArray(encode(str))).toEqual(toArray('S', 'I', 0, str.length, ...str));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode string (no encodeInto)', () => {
// eslint-disable-next-line @typescript-eslint/unbound-method
const { encodeInto } = TextEncoder.prototype;
// @ts-expect-error 移除 encodeInto 以测试兼容性
TextEncoder.prototype.encodeInto = undefined;
expect(toArray(encode('ubjson'))).toEqual(toArray('S', 'i', 6, ...'ubjson'));
TextEncoder.prototype.encodeInto = encodeInto;
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array', () => {
expect(toArray(encode([1, 2, 3, -1]))).toEqual(toArray('[', 'U', 1, 'U', 2, 'U', 3, 'i', 255, ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (empty)', () => {
expect(toArray(encode([]))).toEqual(toArray('[', ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (undefined)', () => {
expect(toArray(encode([undefined]))).toEqual(toArray('[', 'Z', ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (function)', () => {
expect(toArray(encode([() => 1]))).toEqual(toArray('[', 'Z', ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (spares)', () => {
const array = Array.from({ length: 3 });
array[1] = true;
expect(toArray(encode(array))).toEqual(toArray('[', 'Z', 'T', 'Z', ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (mixed)', () => {
expect(toArray(encode([1, 'a', true]))).toEqual(toArray('[', 'U', 1, 'C', 'a', 'T', ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (int8)', () => {
expect(toArray(encode([-1, 2, 3]))).toEqual(toArray('[', 'i', 255, 'U', 2, 'U', 3, ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (int16)', () => {
expect(toArray(encode([255, -1]))).toEqual(toArray('[', 'U', 0xff, 'i', 0xff, ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (only null values)', () => {
expect(toArray(encode([null, null, null]))).toEqual(toArray('[', 'Z', 'Z', 'Z', ']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode N-D array', () => {
expect(
decode(
encode([
[1, 2, 3],
[4, 5, 6],
]),
),
).toEqual([
[1, 2, 3],
[4, 5, 6],
]);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array of objects', () => {
expect(
decode(
encode([
{ a: 1, b: 2, c: 3 },
{ d: 4, e: 5, f: 6 },
]),
),
).toEqual([
{ a: 1, b: 2, c: 3 },
{ d: 4, e: 5, f: 6 },
]);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array of objects of arrays', () => {
expect(
decode(
encode([
{ a: [1, 2], b: [3, 4] },
{ c: [5, 6], d: [7, 8] },
]),
),
).toEqual([
{ a: [1, 2], b: [3, 4] },
{ c: [5, 6], d: [7, 8] },
]);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (int8 typed array)', () => {
expect(toArray(encode(Int8Array.from([18, -2])))).toEqual(toArray('[', '$', 'i', '#', 'i', 2, 0x12, 0xfe));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (uint8 typed array)', () => {
expect(toArray(encode(Uint8Array.from([18, 254])))).toEqual(toArray('[', '$', 'U', '#', 'i', 2, 0x12, 0xfe));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (buffer array)', () => {
expect(toArray(encode(Buffer.from([18, -2])))).toEqual(toArray('[', '$', 'U', '#', 'i', 2, 0x12, 0xfe));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (int16 typed array)', () => {
expect(toArray(encode(Int16Array.from([4660, -292])))).toEqual(
toArray('[', '$', 'I', '#', 'i', 2, 0x12, 0x34, 0xfe, 0xdc),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (int32 typed array)', () => {
expect(toArray(encode(Int32Array.from([305_419_896, -19_088_744])))).toEqual(
toArray('[', '$', 'l', '#', 'i', 2, 0x12, 0x34, 0x56, 0x78, 0xfe, 0xdc, 0xba, 0x98),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (float32 typed array)', () => {
expect(toArray(encode(Float32Array.from([0.25, 0.125])))).toEqual(
toArray('[', '$', 'd', '#', 'i', 2, 0x3e, 0x80, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (float64 typed array)', () => {
expect(toArray(encode(Float64Array.from([0.25, 0.125])))).toEqual(
toArray(
'[',
'$',
'D',
'#',
'i',
2,
0x3f,
0xd0,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x3f,
0xc0,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (uint8clamped typed array)', () => {
expect(() => encode(new Uint8ClampedArray())).toThrow();
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (uint16 typed array)', () => {
expect(() => encode(new Uint16Array())).toThrow();
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (uint32 typed array)', () => {
expect(() => encode(new Uint32Array())).toThrow();
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (uint64 typed array)', () => {
expect(() => encode(new BigUint64Array())).toThrow();
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode array (int64 typed array)', () => {
expect(toArray(encode(new BigInt64Array([1n, 0x1234_5678_1234_5678n])))).toEqual(
toArray(
'[',
'$',
'L',
'#',
'i',
2,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
0x12,
0x34,
0x56,
0x78,
0x12,
0x34,
0x56,
0x78,
),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object', () => {
expect(toArray(encode({ a: 1, b: 2, c: 3 }))).toEqual(
toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (keep order)', () => {
expect(toArray(encode({ b: 2, a: 1, c: 3 }, { sortObjectKeys: true }))).toEqual(
toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (empty)', () => {
expect(toArray(encode({}))).toEqual(toArray('{', '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (empty key)', () => {
expect(toArray(encode({ '': '' }))).toEqual(toArray('{', 'i', 0, 'S', 'i', 0, '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (mixed)', () => {
expect(toArray(encode({ a: 1, b: 'a', c: true }))).toEqual(
toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'C', 'a', 'i', 1, 'c', 'T', '}'),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (only null values)', () => {
expect(toArray(encode({ a: null, b: null, c: null }))).toEqual(
toArray('{', 'i', 1, 'a', 'Z', 'i', 1, 'b', 'Z', 'i', 1, 'c', 'Z', '}'),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (skip prototype)', () => {
const obj = Object.create({ a: 2, x: 'xx' }) as Record<string, unknown>;
obj['a'] = 1;
obj['b'] = 'a';
obj['c'] = true;
expect(toArray(encode(obj))).toEqual(
toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'C', 'a', 'i', 1, 'c', 'T', '}'),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (skip symbol)', () => {
const obj = { [Symbol()]: true, a: 1 };
expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (skip non-enumerable)', () => {
const obj = {};
Object.defineProperty(obj, 'a', { value: 1, configurable: true, writable: true });
expect(toArray(encode(obj))).toEqual(toArray('{', '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (include getter)', () => {
const obj = {};
Object.defineProperty(obj, 'a', { get: () => 1, enumerable: true });
expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (skip undefined)', () => {
const obj = { a: undefined };
expect(toArray(encode(obj))).toEqual(toArray('{', '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (skip function)', () => {
const obj = { a: 1, b: () => 1 };
expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode object (include null)', () => {
const obj = { a: null };
expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'Z', '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge typed array (16K)', () => {
const obj = new Uint8Array(16 * 1024);
expect(toArray(encode(obj).slice(0, 8))).toEqual(toArray('[', '$', 'U', '#', 'I', 0x40, 0x00, 0));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge typed array (~128M)', () => {
const obj = new Uint8Array(128 * 1024 * 1024 - 10);
obj[0] = 0x58;
expect(toArray(encode(obj).slice(0, 10))).toEqual(toArray('[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf6, 0x58));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge typed array (128M)', () => {
const obj = new Uint8Array(128 * 1024 * 1024);
obj[0] = 0x58;
expect(toArray(encode(obj).slice(0, 10))).toEqual(toArray('[', '$', 'U', '#', 'l', 0x8, 0x00, 0x00, 0x00, 0x58));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge typed array (1G) [error]', () => {
const obj = new Uint8Array(1 * 1024 * 1024 * 1024);
expect(() => encode(obj)).toThrow(/Buffer has exceed max size/);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge data (~128M)', () => {
const obj = [new Uint8Array(128 * 1024 * 1024 - 20)];
obj[0][0] = 0x12;
obj[0][1] = 0x34;
expect(toArray(encode(obj).slice(0, 12))).toEqual(
toArray('[', '[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xec, 0x12, 0x34),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge data (128M)', () => {
const obj = [new Uint8Array(128 * 1024 * 1024)];
obj[0][0] = 0x12;
obj[0][1] = 0x34;
expect(toArray(encode(obj).slice(0, 12))).toEqual(
toArray('[', '[', '$', 'U', '#', 'l', 0x8, 0x00, 0x00, 0x00, 0x12, 0x34),
);
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode huge data (256M)', () => {
const obj = [new Uint8Array(128 * 1024 * 1024 - 9), new Uint8Array(128 * 1024 * 1024 - 9)];
obj[0][0] = 0x12;
obj[0][1] = 0x34;
obj[1][0] = 0x56;
obj[1][1] = 0x78;
const encoded = encode(obj);
expect(encoded.length).toBe(128 * 1024 * 1024 * 2 + 2);
expect(toArray(encoded.slice(0, 12))).toEqual(
toArray('[', '[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x12, 0x34),
);
expect(toArray(encoded.slice(128 * 1024 * 1024 + 1, 128 * 1024 * 1024 + 1 + 11))).toEqual(
toArray('[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x56, 0x78),
);
expect(toArray(encoded.slice(-1))).toEqual(toArray(']'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode custom toJSON', () => {
const obj = {
toJSON: () => ({ a: 1 }),
};
expect(toArray(encode(obj))).toEqual(toArray('{', 'i', 1, 'a', 'U', 1, '}'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});
test('encode Date', () => {
const obj = new Date(0);
expect(toArray(encode(obj))).toEqual(toArray('S', 'i', 24, ...'1970-01-01T00:00:00.000Z'));
// @ts-expect-error Access private property
expect(getEncoder().pool).toBe(poolInit);
});