UNPKG

mongochangestream

Version:

Sync MongoDB collections via change streams into any database.

303 lines (292 loc) 8.3 kB
import { Collection } from 'mongodb' import assert from 'node:assert' import { describe, test } from 'vitest' import { castError, docToChangeStreamInsert, generatePipelineFromOmit, omitFieldsForUpdate, removeUnusedFields, safeRetry, } from './util.js' type ErrorWithCause = Error & { cause: unknown } describe('util', () => { describe('generatePipelineFromOmit', () => { test('should generate pipeline from omit with no dotted fields', () => { const pipeline = generatePipelineFromOmit(['documents', 'createdAt']) assert.deepEqual(pipeline, [ { $unset: [ 'fullDocument.documents', 'updateDescription.updatedFields.documents', 'fullDocument.createdAt', 'updateDescription.updatedFields.createdAt', ], }, ]) }) }) describe('removeUnusedFields', () => { test('should remove all unneeded fields', () => { const schema = { bsonType: 'object', required: ['a', 'b'], additionalProperties: false, properties: { a: { title: 'An array of strings', bsonType: 'array', items: { bsonType: 'string', title: 'A string', }, }, b: { description: 'foo or bar', bsonType: 'string', enum: ['foo', 'bar'], oneOf: [ { bsonType: 'string', const: 'foo' }, { bsonType: 'string', const: 'bar' }, ], }, c: { bsonType: 'object', additionalProperties: true, properties: { d: { bsonType: 'number', description: 'A number', }, }, }, }, } removeUnusedFields(schema) assert.deepEqual(schema, { bsonType: 'object', additionalProperties: false, properties: { a: { bsonType: 'array', items: { bsonType: 'string', }, }, b: { bsonType: 'string', enum: ['foo', 'bar'], }, c: { bsonType: 'object', additionalProperties: true, properties: { d: { bsonType: 'number', }, }, }, }, }) }) }) describe('omitFieldsForUpdate', () => { test('should remove omitted fields from removedFields - exact', () => { const event: any = { updateDescription: { updatedFields: {}, removedFields: ['address.geo.long'], truncatedArrays: [], }, } const expected = { updateDescription: { updatedFields: {}, removedFields: [], truncatedArrays: [], }, } omitFieldsForUpdate(['address.geo.long'], event) assert.deepEqual(event, expected) }) test('should remove omitted fields from removedFields - prefix', () => { const event: any = { updateDescription: { updatedFields: {}, removedFields: ['address.geo.long'], truncatedArrays: [], }, } const expected = { updateDescription: { updatedFields: {}, removedFields: [], truncatedArrays: [], }, } omitFieldsForUpdate(['address.geo'], event) assert.deepEqual(event, expected) }) test('should remove omitted fields from updatedFields - exact', () => { const event: any = { updateDescription: { updatedFields: { name: 'unknown', 'address.city': 'San Diego', }, removedFields: [], truncatedArrays: [], }, } const expected = { updateDescription: { updatedFields: { name: 'unknown', }, removedFields: [], truncatedArrays: [], }, } omitFieldsForUpdate(['address.city'], event) assert.deepEqual(event, expected) }) test('should remove omitted fields from updatedFields - prefix', () => { const event: any = { updateDescription: { updatedFields: { name: 'unknown', 'address.geo.lat': 24, }, removedFields: [], truncatedArrays: [], }, } const expected = { updateDescription: { updatedFields: { name: 'unknown', }, removedFields: [], truncatedArrays: [], }, } omitFieldsForUpdate(['address.geo'], event) assert.deepEqual(event, expected) }) test('should remove omitted fields from updatedFields - nested', () => { const event: any = { updateDescription: { updatedFields: { name: 'unknown', 'address.geo': { lat: 24, long: 25 }, }, removedFields: [], truncatedArrays: [], }, } const expected = { updateDescription: { updatedFields: { name: 'unknown', 'address.geo': { long: 25 }, }, removedFields: [], truncatedArrays: [], }, } omitFieldsForUpdate(['address.geo.lat'], event) assert.deepEqual(event, expected) }) }) describe('docToChangeStreamInsert', () => { test('should produce a correct change stream "insert" event', () => { const collection = { dbName: 'testdb', collectionName: 'testcoll', } as Collection const doc = { _id: '123', name: 'Joe', } const result = docToChangeStreamInsert(collection)(doc) assert.deepEqual(result, { fullDocument: { _id: '123', name: 'Joe' }, operationType: 'insert', ns: { db: 'testdb', coll: 'testcoll' }, documentKey: { _id: '123' }, }) }) }) }) describe('castError', () => { test('should return the same Error if input is already an Error', () => { const originalError = new Error('test error') const result = castError(originalError) assert.strictEqual(result, originalError) }) test('should convert string to Error', () => { const result = castError('test error') assert.ok(result instanceof Error) assert.strictEqual(result.message, 'test error') }) test('should handle unknown error types', () => { const unknownError = { foo: 'bar' } const result = castError(unknownError) as ErrorWithCause assert.ok(result instanceof Error) assert.strictEqual(result.message, 'Unknown error') assert.strictEqual(result.cause, unknownError) }) }) describe('safeRetry', () => { test('should successfully return resolved promise value', async () => { const result = await safeRetry(() => Promise.resolve(42)) assert.strictEqual(result, 42) }) test('should wrap string errors in Error object', async () => { let thrownError: Error | undefined try { await safeRetry( () => { throw 'custom error' }, { retries: 0 } ) } catch (err) { thrownError = err as Error } assert.ok(thrownError instanceof Error) assert.strictEqual(thrownError?.message, 'custom error') }) test('should wrap unknown error types in Error object', async () => { const customError = { code: 500 } let thrownError: ErrorWithCause | undefined try { await safeRetry( () => { throw customError }, { retries: 0 } ) } catch (err) { thrownError = err as ErrorWithCause } assert.ok(thrownError instanceof Error) assert.strictEqual(thrownError?.message, 'Unknown error') assert.strictEqual(thrownError?.cause, customError) }) test('should pass through Error objects unchanged', async () => { const originalError = new Error('test error') let thrownError: Error | undefined try { await safeRetry( () => { throw originalError }, { retries: 0 } ) } catch (err) { thrownError = err as Error } assert.strictEqual(thrownError, originalError) }) })