UNPKG

@deepkit/bson

Version:
770 lines (656 loc) 34.1 kB
import { expect, test } from '@jest/globals'; import bson, { Binary } from 'bson'; import { deserializeBSON, getBSONDeserializer } from '../src/bson-deserializer'; import { BinaryBigInt, copyAndSetParent, MongoId, nodeBufferToArrayBuffer, PrimaryKey, Reference, ReflectionKind, SignedBinaryBigInt, TypeObjectLiteral, typeOf, uuid, UUID } from '@deepkit/type'; import { getClassName } from '@deepkit/core'; import { serializeWithoutOptimiser } from '../src/bson-serializer'; const { deserialize, serialize } = bson; test('basic number', () => { const obj = { v: 123 }; const bson = serialize(obj); const schema = typeOf<{ v: number }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: true }))).toEqual({ v: 1 }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: false }))).toEqual({ v: 0 }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: -1234 }))).toEqual({ v: -1234 }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to number`); }); test('basic bigint', () => { const obj = { v: 123n }; const bson = serialize({ v: 123 }); const schema = typeOf<{ v: bigint }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: true }))).toEqual({ v: 1n }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: false }))).toEqual({ v: 0n }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to bigint`); }); test('basic null', () => { const schema = typeOf<{ v: null }>(); expect(getBSONDeserializer(undefined, schema)(serialize({ v: null }))).toEqual({ v: null }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: undefined }))).toEqual({ v: null }); expect(getBSONDeserializer(undefined, schema)(serialize({}))).toEqual({ v: null }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: 123 }))).toThrow(`Cannot convert bson type INT to null`); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to null`); expect(deserializeBSON<{ v: null }>(serialize({ v: null }))).toEqual({ v: null }); expect(deserializeBSON<{ v?: null }>(serialize({ v: null }))).toEqual({ v: null }); expect(deserializeBSON<{ v?: null }>(serialize({ v: undefined }))).toEqual({ v: undefined }); }); test('basic undefined', () => { const schema = typeOf<{ v: undefined }>(); expect(getBSONDeserializer(undefined, schema)(serialize({ v: null }))).toEqual({ v: undefined }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: undefined }))).toEqual({ v: undefined }); expect(getBSONDeserializer(undefined, schema)(serialize({}))).toEqual({ v: undefined }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: 123 }))).toThrow(`Cannot convert bson type INT to undefined`); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to undefined`); }); test('basic literal', () => { expect(deserializeBSON<{ v: 'abc' }>(serialize({ v: null }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: 'abc' }>(serialize({ v: undefined }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: 'abc' }>(serialize({}))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: 'abc' }>(serialize({ v: 1234 }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: 123 }>(serialize({ v: 'abc' }))).toEqual({ v: 123 }); expect(deserializeBSON<{ v: true }>(serialize({ v: 'abc' }))).toEqual({ v: true }); }); test('basic optional', () => { expect(deserializeBSON<{ v?: string }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v?: string }>(serialize({ v: null }))).toEqual({ v: undefined }); expect(deserializeBSON<{ v?: string }>(serialize({ v: undefined }))).toEqual({ v: undefined }); }); test('basic date', () => { const date = new Date; expect(deserializeBSON<{ v: Date }>(serialize({ v: date }))).toEqual({ v: date }); expect(deserializeBSON<{ v: Date }>(serialize({ v: date.toJSON() }))).toEqual({ v: date }); expect(deserializeBSON<{ v: Date }>(serialize({ v: date.valueOf() }))).toEqual({ v: date }); }); test('basic class with constructor', () => { class User { id: number = 0; constructor(public username: string) { } } { const user = deserializeBSON<User>(serialize({ username: 'Peter' })); expect(user).toBeInstanceOf(User); expect(user.username).toBe('Peter'); expect(user.id).toBe(0); } { const user = deserializeBSON<User>(serialize({ id: 3, username: 'Peter' })); expect(user).toBeInstanceOf(User); expect(user.username).toBe('Peter'); expect(user.id).toBe(3); } }); test('basic class no constructor', () => { class User { id: number = 0; username!: string; } { const user = deserializeBSON<User>(serialize({ username: 'Peter' })); expect(user).toBeInstanceOf(User); expect(user.username).toBe('Peter'); expect(user.id).toBe(0); } { const user = deserializeBSON<User>(serialize({ id: 3, username: 'Peter' })); expect(user).toBeInstanceOf(User); expect(user.username).toBe('Peter'); expect(user.id).toBe(3); } }); test('basic optional property with initializer', () => { const defaultValue = new Date; class User { v: Date = defaultValue; } expect(deserializeBSON<User>(serialize({ v: new Date(1) }))).toEqual({ v: new Date(1) }); expect(deserializeBSON<User>(serialize({ v: undefined }))).toEqual({ v: defaultValue }); expect(deserializeBSON<User>(serialize({ v: null }))).toEqual({ v: defaultValue }); expect(deserializeBSON<User>(serialize({}))).toEqual({ v: defaultValue }); }); test('basic binary bigint', () => { const buffer = Buffer.from([100]); const obj = { v: 100n }; const bson = serialize({ v: new Binary(buffer, Binary.SUBTYPE_DEFAULT) }); const schema = typeOf<{ v: BinaryBigInt }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toEqual({ v: 123n }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: true }))).toEqual({ v: 1n }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: false }))).toEqual({ v: 0n }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to BinaryBigInt`); }); test('basic signed binary bigint', () => { const buffer = Buffer.from([0, 100]); const obj = { v: 100n }; const bson = serialize({ v: new Binary(buffer, Binary.SUBTYPE_DEFAULT) }); const bsonNegative = serialize({ v: new Binary(Buffer.from([255, 100]), Binary.SUBTYPE_DEFAULT) }); const schema = typeOf<{ v: SignedBinaryBigInt }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(bsonNegative)).toEqual({ v: -100n }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toEqual({ v: 123n }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: true }))).toEqual({ v: 1n }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: false }))).toEqual({ v: 0n }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to SignedBinaryBigInt`); }); test('basic string', () => { const obj = { v: 'abc' }; const bson = serialize(obj); const schema = typeOf<{ v: string }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: 123 }))).toEqual({ v: '123' }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: {} }))).toThrow(`Cannot convert bson type OBJECT to string`); }); test('basic boolean', () => { const obj = { v: true }; const bson = serialize(obj); const schema = typeOf<{ v: boolean }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(getBSONDeserializer(undefined, schema)(serialize({ v: 123 }))).toEqual({ v: true }); expect(getBSONDeserializer(undefined, schema)(serialize({ v: 0 }))).toEqual({ v: false }); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toThrow(`Cannot convert bson type STRING to boolean`); }); test('basic array buffer', () => { const buffer = Buffer.allocUnsafe(16); const obj = { v: nodeBufferToArrayBuffer(buffer) }; const bson = serialize({ v: new Binary(buffer, Binary.SUBTYPE_DEFAULT) }); const schema = typeOf<{ v: ArrayBuffer }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toThrow(`Cannot convert bson type STRING to ArrayBuffer`); }); test('basic typed array', () => { const buffer = Buffer.allocUnsafe(16); const obj = { v: new Uint8Array(buffer) }; const bson = serialize({ v: new Binary(buffer, Binary.SUBTYPE_DEFAULT) }); const schema = typeOf<{ v: Uint8Array }>(); expect(getBSONDeserializer(undefined, schema)(bson)).toEqual(obj); expect(() => getBSONDeserializer(undefined, schema)(serialize({ v: '123' }))).toThrow(`Cannot convert bson type STRING to Uint8Array`); }); test('basic union', () => { type t = { v: string | number }; expect(deserializeBSON<t>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(deserializeBSON<t>(serialize({ v: 123 }))).toEqual({ v: 123 }); }); test('basic union two objects', () => { type t = { a: string } | { b: number }; expect(deserializeBSON<t>(serialize({ a: 'abc' }))).toEqual({ a: 'abc' }); expect(deserializeBSON<t>(serialize({ b: 123 }))).toEqual({ b: 123 }); }); test('basic union with typed array', () => { const buffer = Buffer.allocUnsafe(16); expect(deserializeBSON<{ v: string | Uint8Array }>(serialize({ v: new Binary(buffer, Binary.SUBTYPE_DEFAULT) }))).toEqual({ v: new Uint8Array(buffer) }); expect(deserializeBSON<{ v: string | Uint8Array }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(() => deserializeBSON<{ v: string | Uint8Array }>(serialize({ v: {} }))).toThrow('Cannot convert bson type OBJECT to string | Uint8Array'); }); test('basic union with arraybuffer', () => { const buffer = Buffer.allocUnsafe(16); expect(deserializeBSON<{ v: string | ArrayBuffer }>(serialize({ v: new Binary(buffer, Binary.SUBTYPE_DEFAULT) }))).toEqual({ v: nodeBufferToArrayBuffer(buffer) }); expect(deserializeBSON<{ v: string | ArrayBuffer }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(() => deserializeBSON<{ v: string | ArrayBuffer }>(serialize({ v: {} }))).toThrow('Cannot convert bson type OBJECT to string | ArrayBuffer'); }); test('basic union ', () => { expect(deserializeBSON<{ v: string | number }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: string | number }>(serialize({ v: 123 }))).toEqual({ v: 123 }); expect(() => deserializeBSON<{ v: string | number }>(serialize({ v: undefined }))).toThrow('Cannot convert undefined value to string | number'); expect(() => deserializeBSON<{ v: string | number }>(serialize({}))).toThrow('Cannot convert undefined value to string | number'); expect(deserializeBSON<{ v?: string | number }>(serialize({ v: undefined }))).toEqual({ v: undefined }); expect(deserializeBSON<{ v?: string | number }>(serialize({ v: null }))).toEqual({ v: undefined }); expect(deserializeBSON<{ v?: string | number }>(serialize({}))).toEqual({ v: undefined }); }); test('basic union with null', () => { expect(deserializeBSON<{ v: string | null }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: string | null }>(serialize({ v: null }))).toEqual({ v: null }); expect(deserializeBSON<{ v?: string | null }>(serialize({ v: null }))).toEqual({ v: null }); expect(deserializeBSON<{ v?: string | null }>(serialize({ v: undefined }))).toEqual({ v: undefined }); }); test('basic union with literals', () => { expect(deserializeBSON<{ v: 'a' | 'b' }>(serialize({ v: 'a' }))).toEqual({ v: 'a' }); expect(deserializeBSON<{ v: 'a' | string }>(serialize({ v: 'a' }))).toEqual({ v: 'a' }); expect(deserializeBSON<{ v: 'a' | string }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: 'a' | 2 }>(serialize({ v: 'a' }))).toEqual({ v: 'a' }); expect(deserializeBSON<{ v: 'a' | 2 }>(serialize({ v: 'a' }))).toEqual({ v: 'a' }); expect(deserializeBSON<{ v: 'a' | 2, num: number }>(serialize({ v: 2, num: 5 }))).toEqual({ v: 2, num: 5 }); expect(deserializeBSON<{ v: true | number }>(serialize({ v: 2 }))).toEqual({ v: 2 }); expect(deserializeBSON<{ v: true | number }>(serialize({ v: true }))).toEqual({ v: true }); expect(deserializeBSON<{ v: true | number }>(serialize({ v: false }))).toEqual({ v: 0 }); }); test('basic union with template literals', () => { expect(deserializeBSON<{ v: `a${number}` | number }>(serialize({ v: 'a123' }))).toEqual({ v: 'a123' }); expect(deserializeBSON<{ v: `a${number}` | number }>(serialize({ v: 123 }))).toEqual({ v: 123 }); expect(deserializeBSON<{ v?: `a${number}` | number }>(serialize({ v: undefined }))).toEqual({ v: undefined }); }); test('basic template literal', () => { expect(deserializeBSON<{ v: `a${number}` }>(serialize({ v: 'a123' }))).toEqual({ v: 'a123' }); expect(deserializeBSON<{ v: `a${number}` }>(serialize({ v: 'a1' }))).toEqual({ v: 'a1' }); expect(() => deserializeBSON<{ v: `a${number}` }>(serialize({ v: 'a' }))).toThrow('Cannot convert a to `a${number}`'); expect(() => deserializeBSON<{ v: `a${number}` }>(serialize({ v: 'abc' }))).toThrow('Cannot convert abc to `a${number}`'); expect(() => deserializeBSON<{ v: `a${number}` }>(serialize({ v: false }))).toThrow('Cannot convert bson type BOOLEAN to `a${number}`'); expect(() => deserializeBSON<{ v: `a${number}` }>(serialize({ v: 234 }))).toThrow('Cannot convert bson type INT to `a${number}`'); }); test('basic uuid', () => { const myUuid = uuid(); expect(deserializeBSON<{ v: UUID }>(serialize({ v: myUuid }))).toEqual({ v: myUuid }); expect(() => deserializeBSON<{ v: UUID }>(serialize({ v: 'asd' }))).toThrow('Cannot convert asd to UUID'); expect(() => deserializeBSON<{ v: UUID }>(serialize({ v: 0 }))).toThrow('Cannot convert 0 to UUID'); }); test('basic mongoId', () => { const myObjectId = '507f1f77bcf86cd799439011'; expect(deserializeBSON<{ v: MongoId }>(serialize({ v: myObjectId }))).toEqual({ v: myObjectId }); expect(() => deserializeBSON<{ v: MongoId }>(serialize({ v: 'asd' }))).toThrow('Cannot convert asd to MongoId.'); expect(() => deserializeBSON<{ v: MongoId }>(serialize({ v: 0 }))).toThrow('Cannot convert 0 to MongoId.'); }); test('basic union with string | uuid', () => { const myUuid = uuid(); expect(deserializeBSON<{ v: string | UUID }>(serialize({ v: 'abc' }))).toEqual({ v: 'abc' }); expect(deserializeBSON<{ v: string | UUID }>(serialize({ v: myUuid }))).toEqual({ v: myUuid }); expect(deserializeBSON<{ v: string | UUID }>(serialize({ v: 23 }))).toEqual({ v: '23' }); }); test('basic union with number | uuid', () => { const myUuid = uuid(); expect(deserializeBSON<{ v: number | UUID }>(serialize({ v: myUuid }))).toEqual({ v: myUuid }); expect(deserializeBSON<{ v: number | UUID }>(serialize({ v: 23 }))).toEqual({ v: 23 }); expect(() => deserializeBSON<{ v: number | UUID }>(serialize({ v: 'asdad' }))).toThrow('Cannot convert bson type STRING to number'); }); test('basic union with null | uuid', () => { const myUuid = uuid(); expect(deserializeBSON<{ v: null | UUID }>(serialize({ v: myUuid }))).toEqual({ v: myUuid }); expect(deserializeBSON<{ v: null | UUID }>(serialize({ v: null }))).toEqual({ v: null }); expect(() => deserializeBSON<{ v: null | UUID }>(serialize({ v: 'asdad' }))).toThrow('Cannot convert bson type STRING to null | UUID'); }); test('basic union with uuid', () => { const myUuid = uuid(); expect(deserializeBSON<{ v: number | UUID }>(serialize({ v: 23 }))).toEqual({ v: 23 }); expect(deserializeBSON<{ v: number | UUID }>(serialize({ v: myUuid }))).toEqual({ v: myUuid }); expect(() => deserializeBSON<{ v: UUID | undefined }>(serialize({ v: 'asdad' }))).toThrow('Cannot convert asdad to UUID'); expect(() => deserializeBSON<{ v: number | UUID }>(serialize({ v: 'asdad' }))).toThrow('Cannot convert bson type STRING to number'); }); test('basic union with Date', () => { const value = new Date; expect(deserializeBSON<{ v: number | Date }>(serialize({ v: 23 }))).toEqual({ v: 23 }); expect(deserializeBSON<{ v: number | Date }>(serialize({ v: value }))).toEqual({ v: value }); expect(deserializeBSON<{ v: number | Date }>(serialize({ v: true }))).toEqual({ v: 1 }); expect(() => deserializeBSON<{ v: number | Date }>(serialize({}))).toThrow('Cannot convert undefined value to number | Date'); }); test('basic regexp', () => { const myRegexp = /abc/gmi; expect(deserializeBSON<{ v: RegExp }>(serialize({ v: myRegexp }))).toEqual({ v: myRegexp }); expect(() => deserializeBSON<{ v: RegExp }>(serialize({ v: 'abc' }))).toThrow('Cannot convert bson type STRING to RegExp'); expect(() => deserializeBSON<{ v: RegExp }>(serialize({ v: 23 }))).toThrow('Cannot convert bson type INT to RegExp'); expect(() => deserializeBSON<{ v: RegExp }>(serialize({}))).toThrow('Cannot convert undefined value to RegExp'); }); test('basic union with regexp', () => { const myRegexp = /abc/gmi; expect(deserializeBSON<{ v: number | RegExp }>(serialize({ v: myRegexp }))).toEqual({ v: myRegexp }); expect(deserializeBSON<{ v: number | RegExp }>(serialize({ v: 23 }))).toEqual({ v: 23 }); expect(() => deserializeBSON<{ v: number | RegExp }>(serialize({ v: {} }))).toThrow('Cannot convert bson type OBJECT to number | RegExp'); expect(() => deserializeBSON<{ v: number | RegExp }>(serialize({}))).toThrow('Cannot convert undefined value to number | RegExp'); }); test('basic array', () => { const value = ['a', 'b', 'c']; expect(deserializeBSON<{ v: string[] }>(serialize({ v: value }))).toEqual({ v: value }); expect(deserializeBSON<{ v: string[] }>(serialize({ v: [1, 'b'] }))).toEqual({ v: ['1', 'b'] }); expect(() => deserializeBSON<{ v: string[] }>(serialize({ v: 123 }))).toThrow('Cannot convert bson type INT to Array<string>'); expect(() => deserializeBSON<{ v: string[] }>(serialize({ v: [{}] }))).toThrow('Cannot convert bson type OBJECT to string'); }); test('basic array union', () => { const value = ['a', 'b', false, 'c', true]; expect(deserializeBSON<{ v: (string | boolean)[] }>(serialize({ v: value }))).toEqual({ v: value }); expect(() => deserializeBSON<{ v: (string | boolean)[] }>(serialize({ v: 123 }))).toThrow('Cannot convert bson type INT to Array<string | boolean>'); expect(() => deserializeBSON<{ v: (string | boolean)[] }>(serialize({ v: ['a', {}] }))).toThrow('Cannot convert bson type OBJECT to string | boolean'); }); test('basic two array union', () => { const value = ['a', 'b', false, 'c', true]; type MyType = (string | boolean)[] | number[]; expect(deserializeBSON<{ v: MyType }>(serialize({ v: value }))).toEqual({ v: value }); expect(deserializeBSON<{ v: MyType }>(serialize({ v: [1, 2] }))).toEqual({ v: [1, 2] }); expect(() => deserializeBSON<{ v: MyType }>(serialize({ v: 123 }))).toThrow('Cannot convert bson type INT to MyType'); expect(() => deserializeBSON<{ v: MyType }>(serialize({ v: ['a', {}] }))).toThrow('Cannot convert bson type ARRAY to MyType'); }); test('basic loosely array union', () => { const value = ['a', 'b', false, 'c', true]; type MyType = (string | boolean)[] | number; expect(deserializeBSON<{ v: MyType }>(serialize({ v: value }))).toEqual({ v: value }); //when resolving an complicated union, we do not use loosely type guards expect(() => deserializeBSON<{ v: MyType }>(serialize({ v: [1, 2] }))).toThrow('Cannot convert bson type ARRAY to MyType'); expect(deserializeBSON<{ v: MyType }>(serialize({ v: 123 }))).toEqual({ v: 123 }); expect(() => deserializeBSON<{ v: MyType }>(serialize({ v: ['a', {}] }))).toThrow('Cannot convert bson type ARRAY to MyType'); }); test('basic class array union', () => { class A { type!: 'a'; b?: number; } class B { type!: 'b'; } class C { c!: string; } type MyType = (A)[] | (B)[] | (C)[]; { const items = deserializeBSON<{ v: MyType }>(serialize({ v: [{ type: 'a' }] })); expect(items.v[0]).toBeInstanceOf(A); expect((items.v[0] as A).type).toBe('a'); } { const items = deserializeBSON<{ v: MyType }>(serialize({ v: [{ type: 'b' }] })); expect(items.v[0]).toBeInstanceOf(B); expect((items.v[0] as B).type).toBe('b'); } { const items = deserializeBSON<{ v: MyType }>(serialize({ v: [{ c: 'yes' }] })); expect(items.v[0]).toBeInstanceOf(C); expect((items.v[0] as C).c).toBe('yes'); } { expect(() => deserializeBSON<{ v: MyType }>(serialize({ v: [{ nope: 'no' }] }))).toThrow(`Cannot convert bson type ARRAY to MyType`); } }); test('constructor parameters', () => { class A { id: number = 0; constructor(public username: string) { } } expect(deserializeBSON<{ v: A }>(serialize({ v: new A('Peter') }))).toEqual({ v: { id: 0, username: 'Peter' } }); }); test('reference', () => { class A { id: number & PrimaryKey = 0; constructor(public username: string) { } } { const item = deserializeBSON<{ v: A & Reference }>(serialize({ v: 23 })); expect(item).toEqual({ v: { id: 23 } }); expect(item.v).toBeInstanceOf(A); expect(getClassName(item.v)).toBe('AReference'); } { const item = deserializeBSON<{ v: A & Reference }>(serialize({ v: { id: 34, username: 'Peter' } })); expect(item).toEqual({ v: { id: 34, username: 'Peter' } }); expect(item.v).toBeInstanceOf(A); expect(getClassName(item.v)).toBe('A'); } }); test('reference in union', () => { class A { id: number & PrimaryKey = 0; constructor(public username: string) { } } type t = { v: (A & Reference) | string[] }; { const item = deserializeBSON<t>(serialize({ v: 23 })); expect(item).toEqual({ v: { id: 23 } }); expect(item.v).toBeInstanceOf(A); expect(getClassName(item.v)).toBe('AReference'); } { const item = deserializeBSON<t>(serialize({ v: { id: 34, username: 'Peter' } })); expect(item).toEqual({ v: { id: 34, username: 'Peter' } }); expect(item.v).toBeInstanceOf(A); expect(getClassName(item.v)).toBe('A'); } }); test('tuple', () => { { type t = { v: [string, number] }; expect(deserializeBSON<t>(serialize({ v: ['abc', 34] }))).toEqual({ v: ['abc', 34] }); expect(deserializeBSON<t>(serialize({ v: ['abc', 34, 55] }))).toEqual({ v: ['abc', 34] }); expect(deserializeBSON<t>(serialize({ v: ['abc', '44'] }))).toEqual({ v: ['abc', 44] }); } { type t = { v: [number] }; expect(deserializeBSON<t>(serialize({ v: [34] }))).toEqual({ v: [34] }); expect(deserializeBSON<t>(serialize({ v: ['44'] }))).toEqual({ v: [44] }); } { type t = { v: [...number[]] }; expect(deserializeBSON<t>(serialize({ v: [34] }))).toEqual({ v: [34] }); expect(deserializeBSON<t>(serialize({ v: ['44'] }))).toEqual({ v: [44] }); expect(deserializeBSON<t>(serialize({ v: [34, 55] }))).toEqual({ v: [34, 55] }); expect(deserializeBSON<t>(serialize({ v: ['44', 55] }))).toEqual({ v: [44, 55] }); } { type t = { v: [string, ...number[]] }; expect(deserializeBSON<t>(serialize({ v: [34] }))).toEqual({ v: ['34'] }); expect(deserializeBSON<t>(serialize({ v: ['44'] }))).toEqual({ v: ['44'] }); expect(deserializeBSON<t>(serialize({ v: [34, 55] }))).toEqual({ v: ['34', 55] }); expect(deserializeBSON<t>(serialize({ v: ['44', 55, 66] }))).toEqual({ v: ['44', 55, 66] }); } { type t = { v: [...number[], string] }; expect(deserializeBSON<t>(serialize({ v: [34] }))).toEqual({ v: ['34'] }); expect(deserializeBSON<t>(serialize({ v: ['44'] }))).toEqual({ v: ['44'] }); expect(deserializeBSON<t>(serialize({ v: [34, '55'] }))).toEqual({ v: [34, '55'] }); expect(deserializeBSON<t>(serialize({ v: ['44', 55, '66'] }))).toEqual({ v: [44, 55, '66'] }); } { type t = { v: [...number[], string, boolean] }; expect(deserializeBSON<t>(serialize({ v: [true] }))).toEqual({ v: [true] }); expect(deserializeBSON<t>(serialize({ v: [34, true] }))).toEqual({ v: ['34', true] }); expect(deserializeBSON<t>(serialize({ v: ['44', 55, '66', true] }))).toEqual({ v: [44, 55, '66', true] }); } { type t = { v: [string, ...number[], boolean] }; expect(deserializeBSON<t>(serialize({ v: ['abc', true] }))).toEqual({ v: ['abc', true] }); expect(deserializeBSON<t>(serialize({ v: ['abc', 12, true] }))).toEqual({ v: ['abc', 12, true] }); expect(deserializeBSON<t>(serialize({ v: ['abc', 12, 23, true] }))).toEqual({ v: ['abc', 12, 23, true] }); } }); test('tuple on union', () => { interface C { d: true; } { type t = { v: [string, number] | [C] }; expect(deserializeBSON<t>(serialize({ v: ['abc', 34] }))).toEqual({ v: ['abc', 34] }); expect(deserializeBSON<t>(serialize({ v: [{ d: true }] }))).toEqual({ v: [{ d: true }] }); expect(deserializeBSON<t>(serialize({ v: ['abc', 34, 55] }))).toEqual({ v: ['abc', 34] }); expect(() => deserializeBSON<t>(serialize({ v: ['abc', '44'] }))).toThrow('Cannot convert bson type ARRAY to [string, number] | [C]'); //union type guard are strict } { type t = { v: [number] | [C] }; expect(deserializeBSON<t>(serialize({ v: [34] }))).toEqual({ v: [34] }); expect(() => deserializeBSON<t>(serialize({ v: ['44'] }))).toThrow('Cannot convert bson type ARRAY to [number]'); //union type guard are strict } { type t = { v: [...number[]] | [C] }; expect(deserializeBSON<t>(serialize({ v: [34] }))).toEqual({ v: [34] }); expect(() => deserializeBSON<t>(serialize({ v: ['44'] }))).toThrow('Cannot convert bson type ARRAY to [...number[]] | [C]'); expect(deserializeBSON<t>(serialize({ v: [34, 55] }))).toEqual({ v: [34, 55] }); expect(() => deserializeBSON<t>(serialize({ v: ['44', 55] }))).toThrow('Cannot convert bson type ARRAY to [...number[]] | [C]'); } { type t = { v: [string, ...number[]] | [C] }; expect(deserializeBSON<t>(serialize({ v: ['44'] }))).toEqual({ v: ['44'] }); expect(() => deserializeBSON<t>(serialize({ v: [34] }))).toThrow(); expect(() => deserializeBSON<t>(serialize({ v: [34, 55] }))).toThrow(); expect(deserializeBSON<t>(serialize({ v: ['44', 55, 66] }))).toEqual({ v: ['44', 55, 66] }); } { type t = { v: [...number[], string] | [C] }; expect(deserializeBSON<t>(serialize({ v: ['44'] }))).toEqual({ v: ['44'] }); expect(() => deserializeBSON<t>(serialize({ v: [34] }))).toThrow(); expect(deserializeBSON<t>(serialize({ v: [34, '55'] }))).toEqual({ v: [34, '55'] }); expect(() => deserializeBSON<t>(serialize({ v: ['44', 55, '66'] }))).toThrow(); } { type t = { v: [...number[], string, boolean] | [C] }; expect(deserializeBSON<t>(serialize({ v: [true] }))).toEqual({ v: [true] }); expect(() => deserializeBSON<t>(serialize({ v: [34, true] }))).toThrow(); expect(deserializeBSON<t>(serialize({ v: [44, 55, '66', true] }))).toEqual({ v: [44, 55, '66', true] }); } { type t = { v: [string, ...number[], boolean] | [C] }; expect(deserializeBSON<t>(serialize({ v: ['abc', true] }))).toEqual({ v: ['abc', true] }); expect(deserializeBSON<t>(serialize({ v: ['abc', 12, true] }))).toEqual({ v: ['abc', 12, true] }); expect(deserializeBSON<t>(serialize({ v: ['abc', 12, 23, true] }))).toEqual({ v: ['abc', 12, 23, true] }); } }); test('set', () => { { type t = { v: Set<string> }; expect(deserializeBSON<t>(serialize({ v: ['abc', 34] }))).toEqual({ v: new Set(['abc', '34']) }); } }); test('set in union', () => { interface C { d: true; } { type t = { v: Set<string> | [C] }; expect(deserializeBSON<t>(serialize({ v: ['abc', '34'] }))).toEqual({ v: new Set(['abc', '34']) }); expect(deserializeBSON<t>(serialize({ v: [{ d: true }] }))).toEqual({ v: [{ d: true }] }); } }); test('map', () => { { type t = { v: Map<string, number> }; expect(deserializeBSON<t>(serialize({ v: [['a', 23], ['b', 34]] }))).toEqual({ v: new Map<any, any>([['a', 23], ['b', 34]]) }); } }); test('map union', () => { interface C { d: true; } { type t = { v: Map<string, number> | [C] }; expect(deserializeBSON<t>(serialize({ v: [['a', 23], ['b', 34]] }))).toEqual({ v: new Map<any, any>([['a', 23], ['b', 34]]) }); expect(deserializeBSON<t>(serialize({ v: [{ d: true }] }))).toEqual({ v: [{ d: true }] }); } }); test('index signature', () => { interface A { [name: string]: number; } expect(deserializeBSON<A>(serialize({ a: 1 }))).toEqual({ a: 1 }); expect(deserializeBSON<A>(serialize({ a: 1, b: 2 }))).toEqual({ a: 1, b: 2 }); }); test('index signature in union', () => { interface A { [name: string]: number; } type t = A | { another: true }; expect(deserializeBSON<t>(serialize({ a: 1 }))).toEqual({ a: 1 }); expect(deserializeBSON<t>(serialize({ another: true }))).toEqual({ another: true }); expect(deserializeBSON<t>(serialize({ a: 1, b: 2 }))).toEqual({ a: 1, b: 2 }); }); test('index signature template literal', () => { interface A { [name: `a${number}`]: number; } expect(deserializeBSON<A>(serialize({ a23: 1 }))).toEqual({ a23: 1 }); expect(deserializeBSON<A>(serialize({ add: 1 }))).toEqual({}); expect(deserializeBSON<A>(serialize({ a23: 1, a3: 3 }))).toEqual({ a23: 1, a3: 3 }); }); test('index signature multiple', () => { interface A { [name: number]: boolean; [name2: `a${number}`]: number; } expect(deserializeBSON<A>(serialize({ a23: 1 }))).toEqual({ a23: 1 }); expect(deserializeBSON<A>(serialize({ 12: true }))).toEqual({ 12: true }); expect(deserializeBSON<A>(serialize({ a23: 2, 12: true }))).toEqual({ a23: 2, 12: true }); }); test('index signature + object literal', () => { interface A { [name: number]: boolean; [name2: `a${number}`]: number; } type t = A & { [P in 'abc']: string }; expect(deserializeBSON<t>(serialize({ abc: 'yes' }))).toEqual({ abc: 'yes' }); expect(() => deserializeBSON<t>(serialize({ a23: 1 }))).toThrow('Cannot convert undefined value to string'); expect(deserializeBSON<t>(serialize({ abc: 'yes', 12: true }))).toEqual({ abc: 'yes', 12: true }); expect(deserializeBSON<t>(serialize({ abc: 'yes', 12: true, a23: 23 }))).toEqual({ abc: 'yes', 12: true, a23: 23 }); }); test('any', () => { const data = { lastErrorObject: { n: 1, updatedExisting: true }, value: { _id: '61df83e58a5e3ba77f8f1c0f', id: 'bdcfb3a0-034a-4f07-8aff-b78e2822a5a8' }, ok: 1, '$clusterTime': { clusterTime: 7052500565351202817n, signature: { hash: Buffer.alloc(16), keyId: 0n } }, operationTime: 7052500565351202817n }; const type: TypeObjectLiteral = copyAndSetParent({ kind: ReflectionKind.objectLiteral, types: [ { kind: ReflectionKind.propertySignature, name: 'value', type: { kind: ReflectionKind.any } } ] }); const bson = serializeWithoutOptimiser(data); const deserializer = getBSONDeserializer(undefined, type); const back = deserializer(bson); console.log('back', back); }); test('circular', () => { class Model { id: number = 0; child?: Model; } const bson = serializeWithoutOptimiser({ items: [{ id: 0, child: { id: 2 } }] } as Response); interface Response { items: Model[]; } const fn = getBSONDeserializer<Response>(); const back = fn(bson); expect(back).toEqual({ items: [{ id: 0, child: { id: 2 } }] }); }); test('additional are ignored', () => { const data = { setVersion: 1, ismaster: true, }; const bson = serializeWithoutOptimiser(data); interface IsMasterResponse { ismaster: boolean; } const fn = getBSONDeserializer<IsMasterResponse>(); const back = fn(bson); expect(back).toEqual({ismaster: true}); });