UNPKG

@deepkit/bson

Version:
236 lines (197 loc) 9.11 kB
import { describe, expect, test } from '@jest/globals'; import 'reflect-metadata'; import { entity, FieldDecoratorResult, t, Types } from '@deepkit/type'; import bson from 'bson'; import { createBSONSizer, getBSONSerializer, JS_INT_MAX, JS_INT_MIN } from '../src/bson-serialize'; import { getBSONDecoder } from '../src/bson-jit-parser'; const { calculateObjectSize, serialize, deserialize } = bson; enum MyEnum { first, second, third, } enum MyEnum2 { first = 'first', second = 'second', third = 'third', } class DecoratedValue { constructor( @t.array(t.string).decorated public c: string[] = [] ) { } } class DecoratedValue2 { @t.array(t.string).decorated public items: string[] = []; } @entity.name('myModel') class MyModel { @t.literal('a').discriminant public type: string = 'a'; @t items: DecoratedValue = new DecoratedValue; constructor(@t public name: string) { } } class SimpleModel { @t items: DecoratedValue = new DecoratedValue; constructor(@t public name: string) { } } const ab = new ArrayBuffer(2); const uint8Array = new Uint8Array(ab); uint8Array[0] = 11; uint8Array[1] = 22; test('compare ab', () => { const other = new Uint8Array([11, 22]).buffer; expect(ab).toEqual(other); }); const decoratedValue2 = new DecoratedValue2(); decoratedValue2.items = ['a', 'b', 'c']; const actualUndefined = Symbol('undefined'); const types: [type: FieldDecoratorResult<any>, value: any, expected?: any, dontComarepToMongo?: true][] = [ [t.string, 'Hello Peter'], [t.number, 1], [t.number, 0], [t.number, -1], [t.number, -90071992547409920], [t.number, 90071992547409920], [t.number, 134.444444445], [t.number, -134.444444445], [t.number, 212313134.444444445], [t.number, -1212313134.444444445], [t.bigint, -1348574839n, undefined, true], [t.bigint, -156n, undefined, true], [t.bigint, -0n, undefined, true], [t.bigint, 0n, undefined, true], [t.bigint, 555n, undefined, true], [t.bigint, 9223372036854775810n, undefined, true], //forces binary format [t.bigint, -9223372036854775810n, undefined, true], //forces binary format [t.any, -1348574839n, undefined, true], [t.any, -156n, undefined, true], [t.any, -0n, undefined, true], [t.any, 0n, undefined, true], [t.any, 555n, undefined, true], [t.any, 9223372036854775810n, undefined, true], //forces binary format [t.any, -9223372036854775810n, undefined, true], //forces binary format [t.bigint, undefined, undefined, true], [t.bigint, 'asd', actualUndefined, true], [t.boolean, false], [t.boolean, true], [t.boolean, true], [t.uuid, 'bef8de96-41fe-442f-b70c-c3a150f8c96c'], [t.uuid, 'bef8de92-41fe-442f-b70c-c3a150f8c961'], [t.mongoId, '507f191e810c19729de860ea'], [t.date, new Date('1987-10-12T00:00:00.000Z')], [t.date, new Date('2920-08-09T19:02:28.397Z')], [t.date, new Date('2020-08-09T19:02:28.397Z')], [t.date, new Date('1900-10-12T00:00:00.000Z')], [t.date, new Date('1000-10-12T00:00:00.000Z')], [t.enum(MyEnum), MyEnum.first], [t.enum(MyEnum), MyEnum.second], [t.enum(MyEnum), MyEnum.third], [t.enum(MyEnum2), MyEnum2.first], [t.enum(MyEnum2), MyEnum2.second], [t.enum(MyEnum2), MyEnum2.third], [t.type({ name: t.string }), { name: 'Peter' }], [t.union(t.string, MyModel), 'asd'], [t.union(t.string, MyModel), { name: 'foo' }, new MyModel('foo'), true], [t.union(t.string, SimpleModel), 'asd'], [t.union(t.string, SimpleModel), { name: 'foo' }, new SimpleModel('foo')], [t.union(t.mongoId, SimpleModel), '507f191e810c19729de860ea', undefined, true], [t.union(t.mongoId, SimpleModel), { name: 'foo' }, new SimpleModel('foo')], [t.union(t.string, t.array(t.string)), 'asd'], [t.union(t.string, t.array(t.string)), ['a', 'b']], [t.union(t.string, t.uuid), 'asd'], [t.union(t.string, t.uuid), '3c25985e-4e25-45db-9e8a-a487cc78929a'], [t.union(t.string, t.mongoId), 'asd'], [t.union(t.string, t.mongoId), '507f191e810c19729de860ea', undefined, true], [t.union(t.string, t.uuid, t.date), '3c25985e-4e25-45db-9e8a-a487cc78929a'], [t.union(t.string, t.uuid, t.date), 'asd'], [t.union(t.string, t.map(t.string)), 'asd'], [t.union(t.string, t.map(t.string)), { first: 'asd', second: 'asd' }], [t.union(t.string, t.partial(MyModel)), 'asd'], [t.union(t.string, t.partial(MyModel)), { name: 'asd' }], [t.union(t.string, t.type(ArrayBuffer)), 'asd'], [t.union(t.string, t.type(ArrayBuffer)), ab, undefined, true], [t.union(t.string, t.type(Uint8Array)), uint8Array, undefined, true], [t.type(DecoratedValue), new DecoratedValue(['a', 'b']), undefined, true], [t.type(DecoratedValue2), decoratedValue2, undefined, true], [t.union(t.string, MyModel), { type: 'a', name: 'Peter', items: new DecoratedValue(['a', 'b']), }, undefined, true], [t.array(t.string), ['Peter', 'b']], [t.array(t.number.optional), [1, undefined, 2], undefined, true], //bson-js converts the undefined into null [t.array(t.string.optional), ['Peter', undefined, 'Bar'], undefined, true], //bson-js converts the undefined into null [t.array(t.string.nullable), ['Peter', null, 'Bar']], [t.array(t.union(t.string, t.number)), ['Peter', 23, 'Bar']], [t.array(t.union(t.boolean, t.number)), [false, 23, true]], [t.map(t.string), { name: 'Peter' }], [t.any, { name: 'Peter', ready: false }], [t.type(ArrayBuffer), ab], [t.type(Uint8Array), new Uint8Array([22, 44, 55, 66])], [t.type(Int16Array), new Int16Array([22, 44, 55, 66])], [t.union(t.type({ type: t.literal('m'), name: t.string })), { type: 'm', name: 'Peter' }], [t.partial({ name: t.string }), {}], [t.partial({ name: t.string }), { name: 'Peter' }], [t.any, new RegExp('/abc/', 'g')], [t.any, new RegExp(/abc/, 'g')], [t.any, /abc/g], [t.any, /abc/i], [t.any, /abc/m], [t.any, /abc/gim], ]; describe('integration', () => { for (let i = 0; i < types.length; i++) { const type = types[i]; const [field, value, expected, dontCompareToBSONJS] = type; const property = (field as FieldDecoratorResult<any>).buildPropertySchema('test_' + i); test(`types round-trip #${i} ${property.toString()}: ${value}`, () => { const s = t.schema({ field: field }); const sOptional = t.schema({ field: field.optional }); const sNullable = t.schema({ field: field.nullable }); const obj = { field: value }; const expectedFieldValue = expected === actualUndefined ? undefined : expected ?? value; const expectedObj = { field: expectedFieldValue }; expect(sOptional.getProperty('field').isOptional).toBe(true); expect(sNullable.getProperty('field').isNullable).toBe(true); const serializer = getBSONSerializer(s); const bsonDeepkit = serializer(obj); // console.log(`types round-trip #${i} ${property.toString()}: ${value} back`, obj, deserialize(Buffer.from(bsonDeepkit))); const decoder = getBSONDecoder(s); const decoded = decoder(bsonDeepkit); expect(decoded).toEqual(expectedObj); expect(decoder(serializer({}))).toEqual({}); //optional // expect(getBSONDecoder(sOptional)(getBSONSerializer(sOptional)({}))).toEqual({}); const optionalTrip = getBSONDecoder(sOptional)(getBSONSerializer(sOptional)({ field: undefined })); expect(optionalTrip).toEqual({ field: undefined }); expect('field' in optionalTrip).toEqual(true); //null expect(getBSONDecoder(sNullable)(getBSONSerializer(sNullable)({ field: undefined }))).toEqual({ field: null }); expect('field' in getBSONDecoder(sNullable)(getBSONSerializer(sNullable)({ field: undefined }))).toEqual(true); const nullTrip = getBSONDecoder(sNullable)(getBSONSerializer(sNullable)({ field: null })); expect(nullTrip).toEqual({ field: null }); const type = field.buildPropertySchema().type; const blacklist: Types[] = [ 'uuid', 'objectId', 'arrayBuffer', 'Uint8Array', 'Int16Array' ]; if (blacklist.includes(type)) return; if (dontCompareToBSONJS) return; expect(createBSONSizer(s)(obj)).toEqual(calculateObjectSize(obj)); //official BSON serializer has a bug not serializing LONG correctly if (field.buildPropertySchema().type === 'number' && (value > JS_INT_MAX || value < JS_INT_MIN)) return; const bsonOfficial = serialize(obj); expect(obj).toEqual(deserialize(serialize(obj))); expect(bsonDeepkit).toEqual(bsonOfficial); }); } });