import type { DevtoolsConnectOptions } from '@mongosh/service-provider-node-driver'; import { expect } from 'chai'; import { UUID, Long } from 'bson'; import { serializeError, deserializeError, serializeEvaluationResult, deserializeEvaluationResult, serializeConnectOptions, deserializeConnectOptions, } from './serializer'; import { dummyOptions } from './index.spec'; describe('serializer', function () { describe('serializeError', function () { it('serializer Error to plain object', function () { const serialized = serializeError(new TypeError('Uh-oh')); expect(serialized).to.have.own.property('name', 'TypeError'); expect(serialized).to.have.own.property('message', 'Uh-oh'); expect(serialized).to.have.own.property('stack'); }); }); describe('deserializeError', function () { it('creates an instance of an error from plain object', function () { const err = deserializeError({ name: 'CustomError', message: 'Error!' }); expect(err).to.be.instanceof(Error); expect(err).to.have.own.property('name', 'CustomError'); expect(err).to.have.own.property('message', 'Error!'); }); }); describe('serializeEvaluationResult', function () { it('should return primitive values as-is', function () { const serialized = serializeEvaluationResult({ type: 'primitive', printable: 123, }); expect(serialized).to.have.property('type', 'primitive'); expect(serialized).to.have.property('printable', 123); }); it('should serialize error values', function () { const serialized = serializeEvaluationResult({ type: 'error', printable: new SyntaxError('Ooops!'), }); expect(serialized).to.have.property('type', 'SerializedErrorResult'); expect(serialized).to.have.property('printable').not.instanceof(Error); expect(serialized).to.have.nested.property( 'printable.name', 'SyntaxError' ); expect(serialized).to.have.nested.property('printable.message', 'Ooops!'); }); it('should return inspect result for non shell-api result types (type === null)', function () { const serialized = serializeEvaluationResult({ type: null, printable: function abc() {}, }); expect(serialized).to.have.property('type', 'InspectResult'); expect(serialized).to.have.property('printable', '[Function: abc]'); }); it('should serialize shell-api result type', function () { const serialized = serializeEvaluationResult({ type: 'TotallyRealShellApiType', printable: { foo: 'bar' }, }); expect(serialized).to.have.property('type', 'SerializedShellApiResult'); expect(serialized).to.have.nested.property( 'printable.origType', 'TotallyRealShellApiType' ); expect(serialized) .to.have.nested.property('printable.serializedValue') .deep.equal({ foo: 'bar', }); }); }); describe('deserializeEvaluationResult', function () { it('should deserialize SerializedErrorResult', function () { const deserialized = deserializeEvaluationResult({ type: 'SerializedErrorResult', printable: { name: 'TypeError', message: 'Uh-oh' }, }); expect(deserialized).to.have.property('printable').be.instanceof(Error); expect(deserialized).to.have.nested.property( 'printable.name', 'TypeError' ); expect(deserialized).to.have.nested.property( 'printable.message', 'Uh-oh' ); }); it('should deserialize SerializedShellApiResult', function () { const deserialized = deserializeEvaluationResult({ type: 'SerializedShellApiResult', printable: { origType: 'ShellApiResult', serializedValue: { foo: 'bar' }, }, }); expect(deserialized).to.have.property('type', 'ShellApiResult'); expect(deserialized) .to.have.property('printable') .deep.equal({ foo: 'bar' }); }); it('should return unknown types as-is', function () { const deserialized = deserializeEvaluationResult({ type: 'SomethingSomethingResultType', printable: 'Hello', }); expect(deserialized).to.have.property( 'type', 'SomethingSomethingResultType' ); expect(deserialized).to.have.property('printable', 'Hello'); }); }); describe('connection options', function () { it('should serialize and deserialize FLE1 connection options', function () { const options: DevtoolsConnectOptions = { ...dummyOptions, autoEncryption: { schemaMap: { 'hr.employees': { bsonType: 'object', properties: { taxid: { encrypt: { keyId: [new UUID('a21ddc6a-8806-4384-9fdf-8ba02a767b5f')], bsonType: 'string', algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random', }, }, }, }, }, }, }; const serialized = serializeConnectOptions(options); expect(serialized).to.deep.equal({ ...dummyOptions, autoEncryption: { schemaMap: { 'hr.employees': { bsonType: 'object', properties: { taxid: { encrypt: { keyId: [ { $binary: { base64: 'oh3caogGQ4Sf34ugKnZ7Xw==', subType: '04', }, }, ], bsonType: 'string', algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random', }, }, }, }, }, }, }); expect(deserializeConnectOptions(serialized)).to.deep.equal(options); }); it('should serialize and deserialize FLE2 connection options', function () { const options: DevtoolsConnectOptions = { ...dummyOptions, autoEncryption: { encryptedFieldsMap: { 'hr.employees': { fields: [ { path: 'phoneNumber', keyId: new UUID('fd6275d7-9260-4e6c-a86b-68ec5240814a'), bsonType: 'string', queries: { queryType: 'equality', contention: new Long(0) }, }, ], }, }, }, }; const serialized = serializeConnectOptions(options); expect(serialized).to.deep.equal({ ...dummyOptions, autoEncryption: { encryptedFieldsMap: { 'hr.employees': { fields: [ { path: 'phoneNumber', keyId: { $binary: { base64: '/WJ115JgTmyoa2jsUkCBSg==', subType: '04', }, }, bsonType: 'string', queries: { queryType: 'equality', contention: { $numberLong: '0' }, }, }, ], }, }, }, }); expect(deserializeConnectOptions(serialized)).to.deep.equal(options); }); }); });