import { expect , test } from '@jest/globals' ; import 'reflect-metadata' ; import { createBSONSizer , getBSONSerializer , getBSONSizer , getValueSize , hexToByte , uuidStringToByte } from '../src/bson-serialize' ; import { ExtractClassType , f , getClassSchema , jsonSerializer , nodeBufferToArrayBuffer , t } from '@deepkit/type' ; import bson from 'bson' ; import { getBSONDecoder } from '../src/bson-jit-parser' ; import { randomBytes } from 'crypto' ; import { parseObject , ParserV2 } from '../src/bson-parser' ; import { ObjectId } from '../src/model' ; import { BSON_BINARY_SUBTYPE_BIGINT , BSONType } from '../src/utils' ; const { Binary , calculateObjectSize , deserialize , Long , ObjectId : OfficialObjectId , serialize } = bson ; test ( 'hexToByte' , ( ) => { expect ( hexToByte ( '00' ) ) . toBe ( 0 ) ; expect ( hexToByte ( '01' ) ) . toBe ( 1 ) ; expect ( hexToByte ( '0f' ) ) . toBe ( 15 ) ; expect ( hexToByte ( '10' ) ) . toBe ( 16 ) ; expect ( hexToByte ( 'ff' ) ) . toBe ( 255 ) ; expect ( hexToByte ( 'f0' ) ) . toBe ( 240 ) ; expect ( hexToByte ( '50' ) ) . toBe ( 80 ) ; expect ( hexToByte ( '7f' ) ) . toBe ( 127 ) ; expect ( hexToByte ( 'f00f' , 1 ) ) . toBe ( 15 ) ; expect ( hexToByte ( 'f0ff' , 1 ) ) . toBe ( 255 ) ; expect ( hexToByte ( 'f00001' , 2 ) ) . toBe ( 1 ) ; expect ( hexToByte ( 'f8' ) ) . toBe ( 16 * 15 + 8 ) ; expect ( hexToByte ( '41' ) ) . toBe ( 16 * 4 + 1 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 1 ) ) . toBe ( 16 * 15 + 8 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 4 ) ) . toBe ( 16 * 4 + 1 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 6 ) ) . toBe ( 16 * 4 + 4 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 7 ) ) . toBe ( 16 * 2 + 15 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 8 ) ) . toBe ( 16 * 11 + 7 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 10 ) ) . toBe ( 16 * 12 + 3 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 11 ) ) . toBe ( 16 * 10 + 1 ) ; expect ( uuidStringToByte ( 'bef8de96-41fe-442f-b70c-c3a150f8c96c' , 15 ) ) . toBe ( 16 * 6 + 12 ) ; } ) ; test ( 'basic string' , ( ) => { const object = { name : 'Peter' } ; const expectedSize = 4 + 1 + 'name\0' . length + ( 4 + 'Peter' . length + 1 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { name : t . string , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'basic number int' , ( ) => { const object = { position : 24 } ; const expectedSize = 4 + 1 + 'position\0' . length + ( 4 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { position : t . number , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'basic long' , ( ) => { const object = { position : 3364367088039355000n } ; const expectedSize = 4 + 1 + 'position\0' . length + ( 4 + 4 ) + 1 ; const schema = t . schema ( { position : t . number , } ) ; const serializer = getBSONSerializer ( schema ) ; const deserializer = getBSONDecoder ( schema ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( serializer ( object ) . byteLength ) . toBe ( expectedSize ) ; const reParsed = getBSONDecoder ( schema ) ( serializer ( object ) ) ; expect ( reParsed . position ) . toBe ( 3364367088039355000n ) ; expect ( serializer ( { position : 123456n } ) ) . toEqual ( serialize ( { position : Long . fromNumber ( 123456 ) } ) ) ; expect ( serializer ( { position : - 123456n } ) ) . toEqual ( serialize ( { position : Long . fromNumber ( - 123456 ) } ) ) ; expect ( serializer ( { position : 3364367088039355000n } ) ) . toEqual ( serialize ( { position : Long . fromBigInt ( 3364367088039355000n ) } ) ) ; expect ( serializer ( { position : - 3364367088039355000n } ) ) . toEqual ( serialize ( { position : Long . fromBigInt ( - 3364367088039355000n ) } ) ) ; expect ( deserializer ( serializer ( { position : 3364367088039355000n } ) ) ) . toEqual ( { position : 3364367088039355000n } ) ; expect ( deserializer ( serializer ( { position : - 3364367088039355000n } ) ) ) . toEqual ( { position : - 3364367088039355000n } ) ; } ) ; test ( 'basic bigint' , ( ) => { const object = { position : 3364367088039355000n } ; const expectedSize = 4 + 1 + 'position\0' . length + ( 4 + 1 + 9 ) + 1 ; const schema = t . schema ( { position : t . bigint , } ) ; const serializer = getBSONSerializer ( schema ) ; const deserializer = getBSONDecoder ( schema ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( serializer ( object ) . byteLength ) . toBe ( expectedSize ) ; const reParsed = deserializer ( serializer ( object ) ) ; expect ( reParsed . position ) . toBe ( 3364367088039355000n ) ; expect ( deserializer ( serializer ( { position : 123456n } ) ) ) . toEqual ( { position : 123456n } ) ; expect ( deserializer ( serializer ( { position : - 123456n } ) ) ) . toEqual ( { position : - 123456n } ) ; expect ( deserializer ( serializer ( { position : 3364367088039355000n } ) ) ) . toEqual ( { position : 3364367088039355000n } ) ; expect ( deserializer ( serializer ( { position : - 3364367088039355000n } ) ) ) . toEqual ( { position : - 3364367088039355000n } ) ; expect ( deserializer ( serializer ( { position : 9223372036854775807n } ) ) ) . toEqual ( { position : 9223372036854775807n } ) ; expect ( deserializer ( serializer ( { position : - 9223372036854775807n } ) ) ) . toEqual ( { position : - 9223372036854775807n } ) ; { const bson = serializer ( { position : 9223372036854775810n } ) ; expect ( bson ) . toEqual ( Buffer . from ( [ 29 , 0 , 0 , 0 , BSONType . BINARY , 112 , 111 , 115 , 105 , 116 , 105 , 111 , 110 , 0 , 9 , 0 , 0 , 0 , BSON_BINARY_SUBTYPE_BIGINT , 1 , 128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 0 , ] ) ) ; } { const bson = serializer ( { position : - 9223372036854775810n } ) ; expect ( bson ) . toEqual ( Buffer . from ( [ 29 , 0 , 0 , 0 , BSONType . BINARY , 112 , 111 , 115 , 105 , 116 , 105 , 111 , 110 , 0 , 9 , 0 , 0 , 0 , BSON_BINARY_SUBTYPE_BIGINT , 255 , 128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 0 , ] ) ) ; } } ) ; test ( 'basic any bigint' , ( ) => { const object = { position : 3364367088039355000n } ; const expectedSize = 4 + 1 + 'position\0' . length + ( 4 + 1 + 9 ) + 1 ; const schema = t . schema ( { position : t . any , } ) ; const serializer = getBSONSerializer ( schema ) ; const deserializer = getBSONDecoder ( schema ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( serializer ( object ) . byteLength ) . toBe ( expectedSize ) ; const reParsed = getBSONDecoder ( schema ) ( serializer ( object ) ) ; expect ( reParsed . position ) . toBe ( 3364367088039355000n ) ; expect ( deserializer ( serializer ( { position : 123456n } ) ) ) . toEqual ( { position : 123456n } ) ; expect ( deserializer ( serializer ( { position : - 123456n } ) ) ) . toEqual ( { position : - 123456n } ) ; expect ( deserializer ( serializer ( { position : 3364367088039355000n } ) ) ) . toEqual ( { position : 3364367088039355000n } ) ; expect ( deserializer ( serializer ( { position : - 3364367088039355000n } ) ) ) . toEqual ( { position : - 3364367088039355000n } ) ; expect ( deserializer ( serializer ( { position : 9223372036854775807n } ) ) ) . toEqual ( { position : 9223372036854775807n } ) ; expect ( deserializer ( serializer ( { position : - 9223372036854775807n } ) ) ) . toEqual ( { position : - 9223372036854775807n } ) ; { const bson = serializer ( { position : 9223372036854775810n } ) ; expect ( bson ) . toEqual ( Buffer . from ( [ 29 , 0 , 0 , 0 , BSONType . BINARY , 112 , 111 , 115 , 105 , 116 , 105 , 111 , 110 , 0 , 9 , 0 , 0 , 0 , BSON_BINARY_SUBTYPE_BIGINT , 1 , 128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 0 , ] ) ) ; } { const bson = serializer ( { position : - 9223372036854775810n } ) ; expect ( bson ) . toEqual ( Buffer . from ( [ 29 , 0 , 0 , 0 , BSONType . BINARY , 112 , 111 , 115 , 105 , 116 , 105 , 111 , 110 , 0 , 9 , 0 , 0 , 0 , BSON_BINARY_SUBTYPE_BIGINT , 255 , 128 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 0 , ] ) ) ; } } ) ; test ( 'basic long bigint' , ( ) => { const bla = [ { n : 1 , m : '1' } , { n : 1 << 16 , m : 'max uint 16' } , { n : ( 1 << 16 ) + 100 , m : 'max uint 16 + 100' } , { n : 4294967296 , m : 'max uint 32' } , { n : 4294967296 - 100 , m : 'max uint 32 - 100' } , { n : 4294967296 - 1 , m : 'max uint 32 - 1' } , { n : 4294967296 + 100 , m : 'max uint 32 + 100' } , { n : 4294967296 + 1 , m : 'max uint 32 + 1' } , { n : 4294967296 * 10 + 1 , m : 'max uint 32 * 10 + 1' } , ] ; for ( const b of bla ) { const long = Long . fromNumber ( b . n ) ; console . log ( b . n , long . toNumber ( ) , long , b . m ) ; } } ) ; test ( 'basic number double' , ( ) => { const object = { position : 149943944399 } ; const expectedSize = 4 + 1 + 'position\0' . length + ( 8 ) + 1 ; const expectedSizeNull = 4 + 1 + 'position\0' . length + ( 0 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; expect ( calculateObjectSize ( { position : null } ) ) . toBe ( expectedSizeNull ) ; expect ( calculateObjectSize ( { position : undefined } ) ) . toBe ( 5 ) ; const schema = t . schema ( { position : t . number , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; expect ( getBSONSerializer ( schema ) ( { position : null } ) . byteLength ) . toBe ( expectedSizeNull ) ; expect ( getBSONSerializer ( schema ) ( { position : undefined } ) . byteLength ) . toBe ( expectedSizeNull ) ; expect ( getBSONSerializer ( schema ) ( { } ) . byteLength ) . toBe ( 5 ) ; expect ( getBSONSerializer ( schema ) ( { position : null } ) . byteLength ) . toEqual ( expectedSizeNull ) ; expect ( getBSONSerializer ( schema ) ( { position : undefined } ) . byteLength ) . toEqual ( expectedSizeNull ) ; expect ( getBSONSerializer ( schema ) ( { } ) . byteLength ) . toEqual ( 5 ) ; expect ( getBSONSerializer ( schema ) ( { position : null } ) ) . toEqual ( serialize ( { position : null } ) ) ; expect ( getBSONSerializer ( schema ) ( { } ) ) . toEqual ( serialize ( { } ) ) ; expect ( getBSONSerializer ( schema ) ( { } ) ) . toEqual ( serialize ( { position : undefined } ) ) ; } ) ; test ( 'basic boolean' , ( ) => { const object = { valid : true } ; const expectedSize = 4 + 1 + 'valid\0' . length + ( 1 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { valid : t . boolean , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'basic date' , ( ) => { const object = { created : new Date } ; const expectedSize = 4 + 1 + 'created\0' . length + ( 8 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { created : t . date , } ) ; const serializer = getBSONSerializer ( schema ) ; const deserializer = getBSONDecoder ( schema ) ; expect ( serializer ( { created : new Date ( '2900-10-12T00:00:00.000Z' ) } ) ) . toEqual ( serialize ( { created : new Date ( '2900-10-12T00:00:00.000Z' ) } ) ) ; expect ( serializer ( { created : new Date ( '1900-10-12T00:00:00.000Z' ) } ) ) . toEqual ( serialize ( { created : new Date ( '1900-10-12T00:00:00.000Z' ) } ) ) ; expect ( serializer ( { created : new Date ( '1000-10-12T00:00:00.000Z' ) } ) ) . toEqual ( serialize ( { created : new Date ( '1000-10-12T00:00:00.000Z' ) } ) ) ; expect ( deserializer ( serializer ( { created : new Date ( '2900-10-12T00:00:00.000Z' ) } ) ) ) . toEqual ( { created : new Date ( '2900-10-12T00:00:00.000Z' ) } ) ; expect ( deserializer ( serializer ( { created : new Date ( '1900-10-12T00:00:00.000Z' ) } ) ) ) . toEqual ( { created : new Date ( '1900-10-12T00:00:00.000Z' ) } ) ; expect ( deserializer ( serializer ( { created : new Date ( '1000-10-12T00:00:00.000Z' ) } ) ) ) . toEqual ( { created : new Date ( '1000-10-12T00:00:00.000Z' ) } ) ; } ) ; test ( 'basic binary' , ( ) => { const object = { binary : new Uint16Array ( 32 ) } ; const expectedSize = 4 + 1 + 'binary\0' . length + ( 4 + 1 + 32 * 2 ) + 1 ; expect ( new Uint16Array ( 32 ) . byteLength ) . toBe ( 32 * 2 ) ; const schema = t . schema ( { binary : t . type ( Uint16Array ) , } ) ; expect ( schema . getProperty ( 'binary' ) . type ) . toBe ( 'Uint16Array' ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( object ) ) ) . toEqual ( object ) ; } ) ; test ( 'basic arrayBuffer' , ( ) => { const arrayBuffer = new ArrayBuffer ( 5 ) ; const view = new Uint8Array ( arrayBuffer ) ; view [ 0 ] = 22 ; view [ 1 ] = 44 ; view [ 2 ] = 55 ; view [ 3 ] = 66 ; view [ 4 ] = 77 ; const object = { binary : arrayBuffer } ; const expectedSize = 4 + 1 + 'binary\0' . length + ( 4 + 1 + 5 ) + 1 ; const schema = t . schema ( { binary : t . type ( ArrayBuffer ) , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( object ) ) ) . toEqual ( object ) ; } ) ; test ( 'basic Buffer' , ( ) => { const object = { binary : new Uint8Array ( 32 ) } ; const expectedSize = 4 + 1 + 'binary\0' . length + ( 4 + 1 + 32 ) + 1 ; const schema = t . schema ( { binary : t . type ( Uint8Array ) , } ) ; expect ( schema . getProperty ( 'binary' ) . type ) . toBe ( 'Uint8Array' ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( object ) ) ) . toEqual ( object ) ; Buffer . alloc ( 2 ) ; Buffer . alloc ( 200 ) ; Buffer . alloc ( 20000 ) ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( { binary : Buffer . alloc ( 44 ) } ) ) ) . toEqual ( { binary : new Uint8Array ( 44 ) } ) ; } ) ; test ( 'basic uuid' , ( ) => { const uuidRandomBinary = new Binary ( Buffer . allocUnsafe ( 16 ) , Binary . SUBTYPE_UUID ) ; const object = { uuid : '75ed2328-89f2-4b89-9c49-1498891d616d' } ; const expectedSize = 4 + 1 + 'uuid\0' . length + ( 4 + 1 + 16 ) + 1 ; expect ( calculateObjectSize ( { uuid : uuidRandomBinary } ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { uuid : t . uuid , } ) ; expect ( getBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; const uuidPlain = Buffer . from ( [ 0x75 , 0xed , 0x23 , 0x28 , 0x89 , 0xf2 , 0x4b , 0x89 , 0x9c , 0x49 , 0x14 , 0x98 , 0x89 , 0x1d , 0x61 , 0x6d ] ) ; const uuidBinary = new Binary ( uuidPlain , 4 ) ; const objectBinary = { uuid : uuidBinary } ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( objectBinary ) ) ; const bson = serialize ( objectBinary ) ; const parsed = parseObject ( new ParserV2 ( bson ) ) ; expect ( parsed . uuid ) . toBe ( '75ed2328-89f2-4b89-9c49-1498891d616d' ) ; } ) ; test ( 'basic objectId' , ( ) => { const object = { _id : '507f191e810c19729de860ea' } ; const expectedSize = 4 + 1 + '_id\0' . length + ( 12 ) + 1 ; const nativeBson = { _id : new OfficialObjectId ( '507f191e810c19729de860ea' ) } ; expect ( calculateObjectSize ( nativeBson ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { _id : t . mongoId , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( nativeBson ) ) ; } ) ; test ( 'basic nested' , ( ) => { const object = { name : { anotherOne : 'Peter2' } } ; const expectedSize = 4 + 1 + 'name\0' . length + ( 4 + 1 + 'anotherOne\0' . length + ( 4 + 'Peter2' . length + 1 ) + 1 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { name : { anotherOne : t . string , } , } ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'basic map' , ( ) => { const object = { name : { anotherOne : 'Peter2' } } ; const expectedSize = 4 + 1 + 'name\0' . length + ( 4 + 1 + 'anotherOne\0' . length + ( 4 + 'Peter2' . length + 1 ) + 1 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { name : t . map ( t . string ) } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'basic array' , ( ) => { const object = { name : [ 'Peter3' ] } ; const expectedSize = 4 + 1 + 'name\0' . length + ( 4 + 1 + '0\0' . length + ( 4 + 'Peter3' . length + 1 ) + 1 ) + 1 ; expect ( calculateObjectSize ( object ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { name : t . array ( t . string ) , } ) ; expect ( getBSONSerializer ( schema ) ( object ) . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'number' , ( ) => { const object = { name : 'Peter4' , tags : [ 'a' , 'b' , 'c' ] , priority : 15 , position : 149943944399 , valid : true , created : new Date ( ) } ; const schema = t . schema ( { name : t . string , tags : t . array ( t . string ) , priority : t . number , position : t . number , valid : t . boolean , created : t . date , } ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( calculateObjectSize ( object ) ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'all supported types' , ( ) => { const object = { name : 'Peter4' , tags : [ 'a' , 'b' , 'c' ] , priority : 15 , position : 149943944399 , valid : true , created : new Date ( ) } ; const schema = t . schema ( { name : t . string , tags : t . array ( t . string ) , priority : t . number , position : t . number , valid : t . boolean , created : t . date , } ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( calculateObjectSize ( object ) ) ; expect ( getBSONSerializer ( schema ) ( object ) ) . toEqual ( serialize ( object ) ) ; } ) ; test ( 'string utf8' , ( ) => { const schema = t . schema ( { name : t . string , any : t . any , } ) ; const serialize = getBSONSerializer ( schema ) ; const parse = getBSONDecoder ( schema ) ; expect ( parse ( serialize ( { name : 'Peter' } ) ) ) . toEqual ( { name : 'Peter' } ) ; expect ( parse ( serialize ( { name : 'Peter✌️' } ) ) ) . toEqual ( { name : 'Peter✌️' } ) ; expect ( parse ( serialize ( { name : '✌️' } ) ) ) . toEqual ( { name : '✌️' } ) ; expect ( parse ( serialize ( { name : '🌉' } ) ) ) . toEqual ( { name : '🌉' } ) ; expect ( parse ( serialize ( { name : 'πøˆ️' } ) ) ) . toEqual ( { name : 'πøˆ️' } ) ; expect ( parse ( serialize ( { name : 'Ѓ' } ) ) ) . toEqual ( { name : 'Ѓ' } ) ; expect ( parse ( serialize ( { name : '㒨' } ) ) ) . toEqual ( { name : '㒨' } ) ; expect ( parse ( serialize ( { name : '﨣' } ) ) ) . toEqual ( { name : '﨣' } ) ; expect ( parse ( serialize ( { any : { base : true } } ) ) ) . toEqual ( { any : { base : true } } ) ; expect ( parse ( serialize ( { any : { '✌️' : true } } ) ) ) . toEqual ( { any : { '✌️' : true } } ) ; expect ( parse ( serialize ( { any : { 'Ѓ' : true } } ) ) ) . toEqual ( { any : { 'Ѓ' : true } } ) ; expect ( parse ( serialize ( { any : { 㒨 : true } } ) ) ) . toEqual ( { any : { 㒨 : true } } ) ; expect ( parse ( serialize ( { any : { 﨣 : true } } ) ) ) . toEqual ( { any : { 﨣 : true } } ) ; } ) ; test ( 'optional field' , ( ) => { const findSchema = t . schema ( { find : t . string , batchSize : t . number , limit : t . number . optional , skip : t . number . optional , } ) ; const findSerializer = getBSONSerializer ( findSchema ) ; const bson = findSerializer ( { find : 'user' , batchSize : 1 , limit : 1 , } ) ; const bsonOfficial = serialize ( { find : 'user' , batchSize : 1 , limit : 1 , } ) ; expect ( bson ) . toEqual ( bsonOfficial ) ; } ) ; test ( 'complex' , ( ) => { const findSchema = t . schema ( { find : t . string , batchSize : t . number , limit : t . number . optional , filter : t . any , projection : t . any , sort : t . any , skip : t . number . optional , } ) ; const findSerializer = getBSONSerializer ( findSchema ) ; const bson = findSerializer ( { find : 'user' , batchSize : 1 , limit : 1 , } ) ; const bsonOfficial = serialize ( { find : 'user' , batchSize : 1 , limit : 1 , } ) ; expect ( bson ) . toEqual ( bsonOfficial ) ; } ) ; test ( 'any objectId' , ( ) => { const schema = t . schema ( { _id : t . any , q : t . any , } ) ; { const doc = { _id : new ObjectId ( '507f191e810c19729de860ea' ) } ; const officialDoc = { _id : new OfficialObjectId ( '507f191e810c19729de860ea' ) } ; const bson = getBSONSerializer ( schema ) ( doc ) ; const bsonOfficial = serialize ( officialDoc ) ; expect ( bson ) . toEqual ( bsonOfficial ) ; const parsed = deserialize ( Buffer . from ( bson ) ) ; expect ( parsed . _id ) . toBeInstanceOf ( OfficialObjectId ) ; expect ( parsed . _id . toHexString ( ) ) . toBe ( '507f191e810c19729de860ea' ) ; const parsed2 = getBSONDecoder ( schema ) ( bson ) ; expect ( parsed2 . _id ) . toBe ( '507f191e810c19729de860ea' ) ; } { const doc = { q : { id : new ObjectId ( '507f191e810c19729de860ea' ) } } ; const officialDoc = { q : { id : new OfficialObjectId ( '507f191e810c19729de860ea' ) } } ; const bson = getBSONSerializer ( schema ) ( doc ) ; const bsonOfficial = serialize ( officialDoc ) ; expect ( bson ) . toEqual ( bsonOfficial ) ; const parsed = deserialize ( Buffer . from ( bson ) ) ; expect ( parsed . q . id ) . toBeInstanceOf ( OfficialObjectId ) ; expect ( parsed . q . id . toHexString ( ) ) . toBe ( '507f191e810c19729de860ea' ) ; const parsed2 = getBSONDecoder ( schema ) ( bson ) ; expect ( parsed2 . q . id ) . toBe ( '507f191e810c19729de860ea' ) ; } } ) ; test ( 'objectId string' , ( ) => { const schema = t . schema ( { id : t . mongoId , } ) ; { const doc = { id : '507f191e810c19729de860ea' } ; const bson = getBSONSerializer ( schema ) ( doc ) ; const bsonOfficial = serialize ( { id : new OfficialObjectId ( '507f191e810c19729de860ea' ) } ) ; expect ( bson ) . toEqual ( bsonOfficial ) ; const parsed = deserialize ( Buffer . from ( bson ) ) ; expect ( parsed . id ) . toBeInstanceOf ( OfficialObjectId ) ; expect ( parsed . id . toHexString ( ) ) . toBe ( '507f191e810c19729de860ea' ) ; const parsed2 = getBSONDecoder ( schema ) ( bson ) ; expect ( parsed2 . id ) . toBe ( '507f191e810c19729de860ea' ) ; } } ) ; test ( 'model 1, missing `public`' , ( ) => { class User { @ f ready ? : boolean ; @ f . array ( f . string ) tags : string [ ] = [ ] ; @ f priority : number = 0 ; constructor ( @ f . primary id , @ f public name ) { } } const schema = getClassSchema ( User ) ; expect ( schema . getMethodProperties ( 'constructor' ) . length ) . toBe ( 2 ) ; expect ( schema . getPropertiesMap ( ) . size ) . toBe ( 5 ) ; { const user = new User ( 1 , 'Peter ' + 1 ) ; user . ready = true ; user . priority = 5 ; user . tags = [ 'a' , 'b' , 'c' ] ; const bson = getBSONSerializer ( User ) ( user ) ; const size = getBSONSizer ( User ) ( user ) ; expect ( size ) . toBe ( calculateObjectSize ( user ) ) ; const s = getBSONDecoder ( User ) ; const o = s ( bson ) ; expect ( o ) . toEqual ( deserialize ( Buffer . from ( bson ) ) ) ; } { const user = { ready : true , priority : 5 , tags : [ 'a' , 'b' , 'c' ] , id : null , name : 'Peter 1' , } ; const bson = getBSONSerializer ( User ) ( user ) ; const s = getBSONDecoder ( User ) ; const o = s ( bson ) ; expect ( o ) . not . toEqual ( deserialize ( Buffer . from ( bson ) ) ) ; } } ) ; test ( 'decorated' , ( ) => { class DecoratedValue { constructor ( @ t . array ( t . string ) . decorated public items = [ ] ) { } } const object = { v : new DecoratedValue ( [ 'Peter3' ] ) } ; const expectedSize = 4 + 1 + 'v\0' . length + ( 4 + 1 + '0\0' . length + ( 4 + 'Peter3' . length + 1 ) + 1 ) + 1 ; expect ( calculateObjectSize ( { v : [ 'Peter3' ] } ) ) . toBe ( expectedSize ) ; const schema = t . schema ( { v : t . type ( DecoratedValue ) , } ) ; const bson = getBSONSerializer ( schema ) ( object ) ; const officialDeserialize = deserialize ( Buffer . from ( bson ) ) ; console . log ( 'officialDeserialize' , officialDeserialize ) ; expect ( officialDeserialize . v ) . toEqual ( [ 'Peter3' ] ) ; expect ( bson . byteLength ) . toBe ( expectedSize ) ; expect ( createBSONSizer ( schema ) ( object ) ) . toBe ( expectedSize ) ; expect ( bson ) . toEqual ( serialize ( { v : [ 'Peter3' ] } ) ) ; const back = getBSONDecoder ( schema ) ( bson ) ; expect ( back . v ) . toBeInstanceOf ( DecoratedValue ) ; expect ( back . v . items ) . toEqual ( [ 'Peter3' ] ) ; expect ( back ) . toEqual ( object ) ; } ) ; test ( 'reference' , ( ) => { class User { @ t . primary id : number = 1 ; @ t . array ( User ) . backReference ( ) managedUsers : User [ ] = [ ] ; @ t name ! : string ; @ t . optional . reference ( ) manager ? : User ; } { const object = new User ( ) ; object . name = 'Peter' ; ( object as any ) . manager = null ; const bson = getBSONSerializer ( User ) ( object ) ; const json = deserialize ( bson ) ; expect ( 'manager' in json ) . toBe ( true ) ; expect ( json . manager ) . toBe ( null ) ; const trip = getBSONDecoder ( User ) ( bson ) ; expect ( trip . manager ) . toBe ( undefined ) ; expect ( 'manager' in trip ) . toBe ( false ) ; } const updateSchema = t . schema ( { update : t . string , $db : t . string , updates : t . array ( { q : t . any , u : t . any , multi : t . boolean , } ) } ) ; { const object = { update : 'Nix' , $db : 'admin' , updates : [ { q : { id : 213 } , u : { manager : null } } ] } ; expect ( getBSONSizer ( updateSchema ) ( object ) ) . toBe ( calculateObjectSize ( object ) ) ; const bson = getBSONSerializer ( updateSchema ) ( object ) ; expect ( getBSONDecoder ( updateSchema ) ( bson ) ) . toEqual ( deserialize ( Buffer . from ( bson ) ) ) ; } } ) ; test ( 'bson length' , ( ) => { const nonce = randomBytes ( 24 ) ; class SaslStartCommand extends t . class ( { saslStart : t . literal ( 1 ) , $db : t . string , mechanism : t . string , payload : t . type ( Uint8Array ) , autoAuthorize : t . literal ( 1 ) , options : { skipEmptyExchange : t . literal ( true ) } } ) { } const message = { saslStart : 1 , '$db' : 'admin' , mechanism : 'SCRAM-SHA-1' , payload : Buffer . concat ( [ Buffer . from ( 'n,,' , 'utf8' ) , Buffer . from ( `n=Peter,r=${ nonce . toString ( 'base64' ) }` , 'utf8' ) ] ) , autoAuthorize : 1 , options : { skipEmptyExchange : true } } ; expect ( message . payload . byteLength ) . toBe ( 13 + nonce . toString ( 'base64' ) . length ) ; const size = getBSONSizer ( SaslStartCommand ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( message ) ) ; const bson = getBSONSerializer ( SaslStartCommand ) ( message ) ; expect ( bson ) . toEqual ( serialize ( message ) ) ; } ) ; test ( 'arrayBuffer' , ( ) => { const schema = t . schema ( { name : t . string , secondId : t . mongoId , preview : t . type ( ArrayBuffer ) , } ) ; const message = jsonSerializer . for ( schema ) . deserialize ( { name : 'myName' , secondId : '5bf4a1ccce060e0b38864c9e' , preview : nodeBufferToArrayBuffer ( Buffer . from ( 'Baar' , 'utf8' ) ) } ) ; expect ( Buffer . from ( message . preview ) . toString ( 'utf8' ) ) . toBe ( 'Baar' ) ; const mongoMessage = { name : message . name , secondId : new OfficialObjectId ( message . secondId ) , preview : new Binary ( Buffer . from ( message . preview ) ) , } ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( mongoMessage ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; expect ( bson ) . toEqual ( serialize ( mongoMessage ) ) ; const back = getBSONDecoder ( schema ) ( bson ) ; expect ( Buffer . from ( back . preview ) . toString ( 'utf8' ) ) . toBe ( 'Baar' ) ; expect ( back . preview ) . toEqual ( message . preview ) ; } ) ; test ( 'typed array' , ( ) => { const schema = t . schema ( { name : t . string , secondId : t . mongoId , preview : t . type ( Uint16Array ) , } ) ; const message = jsonSerializer . for ( schema ) . deserialize ( { name : 'myName' , secondId : '5bf4a1ccce060e0b38864c9e' , preview : new Uint16Array ( nodeBufferToArrayBuffer ( Buffer . from ( 'LAA3AEIATQBYAA==' , 'base64' ) ) ) , } ) ; expect ( message . preview ) . toBeInstanceOf ( Uint16Array ) ; expect ( message . preview . byteLength ) . toBe ( 10 ) ; const mongoMessage = { name : message . name , secondId : new OfficialObjectId ( message . secondId ) , preview : new Binary ( Buffer . from ( new Uint8Array ( message . preview . buffer , message . preview . byteOffset , message . preview . byteLength ) ) ) , } ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( mongoMessage ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; expect ( bson ) . toEqual ( serialize ( mongoMessage ) ) ; const back = getBSONDecoder ( schema ) ( bson ) ; expect ( back . preview ) . toEqual ( message . preview ) ; } ) ; test ( 'typed any and undefined' , ( ) => { const schema = t . schema ( { data : t . any , } ) ; const message = jsonSerializer . for ( schema ) . deserialize ( { data : { $set : { } , $inc : undefined , } , } ) ; expect ( getValueSize ( { $inc : [ undefined ] } ) ) . toBe ( calculateObjectSize ( { $inc : [ undefined ] } ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; const back = getBSONDecoder ( schema ) ( bson ) ; expect ( back . data . $set ) . toEqual ( { } ) ; expect ( back . data . $inc ) . toEqual ( undefined ) ; expect ( '$inc' in back . data ) . toEqual ( true ) ; } ) ; test ( 'test map map' , ( ) => { const schema = t . schema ( { data : t . map ( t . map ( t . string ) ) , } ) ; const message = jsonSerializer . for ( schema ) . deserialize ( { data : { foo : { bar : 'abc' } } , } ) ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( message ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; expect ( bson ) . toEqual ( serialize ( message ) ) ; expect ( getBSONDecoder ( schema ) ( bson ) ) . toEqual ( message ) ; } ) ; test ( 'test array array' , ( ) => { const schema = t . schema ( { data : t . array ( t . array ( t . string ) ) , } ) ; const message = jsonSerializer . for ( schema ) . deserialize ( { data : [ [ 'abc' ] ] , } ) ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( message ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; expect ( bson ) . toEqual ( serialize ( message ) ) ; expect ( getBSONDecoder ( schema ) ( bson ) ) . toEqual ( message ) ; } ) ; test ( 'test array optional' , ( ) => { const schema = t . schema ( { data : t . array ( t . date . optional ) , } ) ; { const message = { data : [ new Date , undefined ] } ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( message ) ) ; } } ) ; test ( 'test map optional 1' , ( ) => { const schema = t . schema ( { data : t . map ( t . date . optional ) , } ) ; { const message = { data : { first : new Date , second : undefined } } ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( message ) ) ) . toEqual ( message ) ; } } ) ; test ( 'test map optional 2' , ( ) => { const schema = t . schema ( { data : t . map ( t . date . optional ) , } ) ; { const message = { data : { first : new Date , second : undefined } } ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( message ) ) ) . toEqual ( message ) ; } } ) ; test ( 'test union optional' , ( ) => { const schema = t . schema ( { data : t . union ( 'foo' , 'bar' ) . optional } ) ; { const message = { data : 'foo' } ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( message ) ) ; const serializer = getBSONSerializer ( schema ) ; const bson = serializer ( message ) ; expect ( bson ) . toEqual ( serialize ( message ) ) ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( message ) ) ) . toEqual ( message ) ; } { const message = { data : undefined } ; const trip = getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( message ) ) ; expect ( 'data' in trip ) . toBe ( true ) ; expect ( trip . data ) . toEqual ( undefined ) ; } { const message = { data : 'bar' } ; const size = getBSONSizer ( schema ) ( message ) ; expect ( size ) . toBe ( calculateObjectSize ( message ) ) ; expect ( getBSONDecoder ( schema ) ( getBSONSerializer ( schema ) ( message ) ) ) . toEqual ( message ) ; expect ( getBSONSerializer ( schema ) ( message ) ) . toEqual ( serialize ( message ) ) ; } } ) ; test ( 'test object' , ( ) => { class MyModel { excluded = true ; @ t type : string = '' ; constructor ( @ t public name ) { } } const schema = t . schema ( { data : t . type ( MyModel ) , } ) ; { const item = new MyModel ( 'bar' ) ; item . type = 'foo' ; const message = { data : item } ; expect ( getBSONSizer ( schema ) ( message ) ) . not . toBe ( calculateObjectSize ( message ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; const backOfficial = deserialize ( Buffer . from ( bson ) ) ; expect ( backOfficial . data . excluded ) . toBe ( undefined ) ; expect ( bson ) . not . toEqual ( serialize ( message ) ) ; const back = getBSONDecoder ( schema ) ( bson ) ; expect ( back . data ) . toBeInstanceOf ( MyModel ) ; expect ( back . data . name ) . toBe ( 'bar' ) ; expect ( back . data . type ) . toBe ( 'foo' ) ; expect ( back ) . toEqual ( message ) ; } } ) ; test ( 'test union deep object' , ( ) => { class MyModel { excluded = true ; @ t . literal ( 'model' ) d : 'model' = 'model' ; @ t type : string = '' ; constructor ( @ t public name ) { } } const schema = t . schema ( { data : t . union ( t . string , MyModel ) , } ) ; { const message = { data : 'peter' } ; expect ( getBSONSizer ( schema ) ( message ) ) . toBe ( calculateObjectSize ( message ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; expect ( bson ) . toEqual ( serialize ( message ) ) ; expect ( getBSONDecoder ( schema ) ( bson ) ) . toEqual ( message ) ; } { const item = new MyModel ( 'bar' ) ; item . type = 'foo' ; const message = { data : item } ; expect ( getBSONSizer ( schema ) ( message ) ) . not . toBe ( calculateObjectSize ( message ) ) ; const bson = getBSONSerializer ( schema ) ( message ) ; const backOfficial = deserialize ( Buffer . from ( bson ) ) ; expect ( backOfficial . data . excluded ) . toBe ( undefined ) ; expect ( bson ) . not . toEqual ( serialize ( message ) ) ; const back = getBSONDecoder ( schema ) ( bson ) ; expect ( back . data ) . toBeInstanceOf ( MyModel ) ; expect ( ( back . data as MyModel ) . name ) . toBe ( 'bar' ) ; expect ( ( back . data as MyModel ) . type ) . toBe ( 'foo' ) ; expect ( back ) . toEqual ( message ) ; } } ) ;