Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions packages/core/src/__tests__/operator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,141 @@ describe('operator', () => {
expect(() => _getOperator('$invalid')).toThrow('unknown operator: $invalid')
})
})

describe('operator error handling', () => {
describe('$pull with non-array values', () => {
it('should handle string value instead of array', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: 'not-an-array' } as unknown as Doc
const operator = _getOperator('$pull')
operator(doc, { field: 'value' })
expect((doc as any).field).toEqual([])
})

it('should handle object value instead of array', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: { key: 'value' } } as unknown as Doc
const operator = _getOperator('$pull')
operator(doc, { field: 'value' })
expect((doc as any).field).toEqual([])
})

it('should handle number value instead of array', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: 42 } as unknown as Doc
const operator = _getOperator('$pull')
operator(doc, { field: 'value' })
expect((doc as any).field).toEqual([])
})

it('should handle null value', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: null } as unknown as Doc
const operator = _getOperator('$pull')
operator(doc, { field: 'value' })
expect((doc as any).field).toEqual([])
})

it('should work correctly with valid array', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: [1, 2, 3] } as unknown as Doc
const operator = _getOperator('$pull')
operator(doc, { field: 2 })
expect((doc as any).field).toEqual([1, 3])
})
})

describe('$update with non-array values', () => {
it('should handle string value instead of array', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: 'not-an-array' } as unknown as Doc
const operator = _getOperator('$update')
operator(doc, { field: { $query: { id: 1 }, $update: { value: 'new' } } })
expect((doc as any).field).toEqual([])
})

it('should handle object value instead of array', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: { key: 'value' } } as unknown as Doc
const operator = _getOperator('$update')
operator(doc, { field: { $query: { id: 1 }, $update: { value: 'new' } } })
expect((doc as any).field).toEqual([])
})

it('should handle null value', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, field: null } as unknown as Doc
const operator = _getOperator('$update')
operator(doc, { field: { $query: { id: 1 }, $update: { value: 'new' } } })
expect((doc as any).field).toEqual([])
})

it('should work correctly with valid array', () => {
const doc: Doc = {
_id: '1' as any,
_class: 'test' as any,
field: [
{ id: 1, value: 'old' },
{ id: 2, value: 'keep' }
]
} as unknown as Doc
const operator = _getOperator('$update')
operator(doc, { field: { $query: { id: 1 }, $update: { value: 'new' } } })
expect((doc as any).field).toEqual([
{ id: 1, value: 'new' },
{ id: 2, value: 'keep' }
])
})
})

describe('$inc with non-numeric values', () => {
it('should handle NaN current value', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: NaN } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: 5 })
expect((doc as any).count).toBe(5)
})

it('should handle string current value', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: 'not-a-number' } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: 5 })
expect((doc as any).count).toBe(5)
})

it('should handle NaN increment value', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: 10 } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: NaN })
expect((doc as any).count).toBe(10)
})

it('should handle string increment value', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: 10 } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: 'not-a-number' as any })
expect((doc as any).count).toBe(10)
})

it('should handle undefined current value (should default to 0)', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: 5 })
expect((doc as any).count).toBe(5)
})

it('should work correctly with valid numbers', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: 10 } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: 5 })
expect((doc as any).count).toBe(15)
})

it('should handle negative increments', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: 10 } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: -3 })
expect((doc as any).count).toBe(7)
})

it('should handle zero increment', () => {
const doc: Doc = { _id: '1' as any, _class: 'test' as any, count: 10 } as unknown as Doc
const operator = _getOperator('$inc')
operator(doc, { count: 0 })
expect((doc as any).count).toBe(10)
})
})
})
})
30 changes: 27 additions & 3 deletions packages/core/src/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ function $pull (document: Doc, keyval: Record<string, PropertyType>): void {
if (doc[key] === undefined) {
doc[key] = []
}
const arr = doc[key] as Array<any>
// Ensure doc[key] is an array before attempting to filter
if (!Array.isArray(doc[key])) {
Analytics.handleError(new Error(`$pull operation on non-array field: ${key}, value: ${JSON.stringify(doc[key])}`))
doc[key] = []
continue
}
const arr = doc[key]
const kvk = keyval[key]
if (typeof kvk === 'object' && kvk !== null) {
const { $in } = kvk as PullArray<PropertyType>
Expand Down Expand Up @@ -111,9 +117,15 @@ function $update (document: Doc, keyval: Record<string, PropertyType>): void {
if (doc[key] === undefined) {
doc[key] = []
}
// Ensure doc[key] is an array before attempting to update
if (!Array.isArray(doc[key])) {
Analytics.handleError(new Error(`$update operation on non-array field: ${key}, value: ${JSON.stringify(doc[key])}`))
doc[key] = []
continue
}
const val = keyval[key]
if (typeof val === 'object') {
const arr = doc[key] as Array<any>
const arr = doc[key]
const desc = val as QueryUpdate<PropertyType>
for (const m of matchArrayElement(arr, desc.$query)) {
for (const [k, v] of Object.entries(desc.$update)) {
Expand All @@ -128,7 +140,19 @@ function $inc (document: Doc, keyval: Record<string, number>): void {
const doc = document as unknown as Record<string, number | undefined>
for (const key in keyval) {
const cur = doc[key] ?? 0
doc[key] = cur + keyval[key]
// Ensure current value is a number
if (typeof cur !== 'number' || isNaN(cur)) {
Analytics.handleError(new Error(`$inc operation on non-numeric field: ${key}, value: ${JSON.stringify(doc[key])}`))
doc[key] = keyval[key]
continue
}
const increment = keyval[key]
// Ensure increment value is a valid number
if (typeof increment !== 'number' || isNaN(increment)) {
Analytics.handleError(new Error(`$inc operation with invalid increment: ${key}, increment: ${JSON.stringify(increment)}`))
continue
}
doc[key] = cur + increment
}
}

Expand Down
Loading