UNPKG

@wener/miniquery

Version:

SQL Where like **safe** filter expression for ORM.

113 lines (107 loc) 3.58 kB
import { inspect } from 'node:util'; import { describe, expect, test } from 'vitest'; import { DisableKey } from './const'; import { formatDocumentQuery } from './formatDocumentQuery'; import type { DocumentQuery } from './types'; // Define a sample interface for type-safe tests interface User { id: number; name: string; status: 'active' | 'inactive'; tags: string[]; posts: Array<{ title: string; published: boolean }>; profile: { age: number; email?: string; address?: { city: string; street: string; }; }; } describe('formatDocumentQuery', () => { test('should format various cases', () => { const testCases: Array<[any, string | undefined]> = [ // Basic cases [null, undefined], [undefined, undefined], [{}, undefined], // Equality and comparison [{ status: 1 }, 'status = 1'], [{ status: { $eq: 1 } }, 'status = 1'], [{ a: { $ne: 'test' } }, 'a != "test"'], [{ a: { $gt: 10 } }, 'a > 10'], // IN and NOT IN [{ a: [1, 2, 3] }, 'a IN (1, 2, 3)'], [{ a: { $in: [1, 2, 3] } }, 'a IN (1, 2, 3)'], [{ a: { $nin: [1, 2, 3] } }, 'a NOT IN (1, 2, 3)'], // Logical operators [{ $not: { a: 1 } }, 'NOT (a = 1)'], [{ $and: { k: { a: 1 } } }, 'a = 1'], [{ $and: [{ a: 1 }, { b: 2 }] }, '(a = 1 AND b = 2)'], [{ $or: [{ a: 1 }, { b: 2 }] }, '(a = 1 OR b = 2)'], [{ $nor: [{ a: 1 }, { b: 2 }] }, 'NOT ((a = 1 OR b = 2))'], // Nested objects and dot notation [{ profile: { age: 18 } }, 'profile.age = 18'], [{ 'profile.age': 18 }, 'profile.age = 18'], // Test for nested dot notation bug fix [{ profile: { 'address.city': 'New York' } }, 'profile.address.city = "New York"'], // Special values [{ a: null }, 'a IS NULL'], // Disable key [{ [DisableKey]: true, b: 2 }, undefined], [{ a: { $eq: 1, [DisableKey]: true }, b: 2 }, 'b = 2'], // --- New Operator Tests --- // $exists [{ name: { $exists: true } }, 'name IS NOT NULL'], [{ name: { $exists: false } }, 'name IS NULL'], // $regex [{ name: { $regex: '^test' } }, `name RLIKE "^test"`], // $size [{ tags: { $size: 3 } }, 'LENGTH(tags) = 3'], // $all [{ tags: { $all: ['a', 'b'] } }, 'CONTAINS(tags, "a") AND CONTAINS(tags, "b")'], // $elemMatch [ { posts: { $elemMatch: { published: true, title: { $like: '%SQL%' } } } }, `ELEM_MATCH(posts, 'published = true AND title LIKE "%SQL%"')`, ], ]; for (const [query, expected] of testCases) { const result = formatDocumentQuery(query); console.log(inspect(query, { depth: 5, colors: true, compact: true }), `\n>`, result); const sortParts = (s: string | undefined) => s?.split(' AND ').sort().join(' AND '); expect(sortParts(result)).toEqual(sortParts(expected)); } }); test('should handle type safety', () => { const testCases: Array<[DocumentQuery<User>, string | undefined]> = [ [ { status: 'active', 'profile.age': { $gte: 18 }, }, 'profile.age >= 18 AND status = "active"', ], [ { profile: { age: { $gte: 18 } }, }, 'profile.age >= 18', ], [ { $or: [{ name: 'John' }, { name: 'Jane' }], status: 'inactive', }, '(name = "John" OR name = "Jane") AND status = "inactive"', ], ]; for (const [query, expected] of testCases) { const result = formatDocumentQuery(query); console.log(inspect(query, { depth: 5, colors: true, compact: true }), `\n>`, result); const sortParts = (s: string | undefined) => s?.split(' AND ').sort().join(' AND '); expect(sortParts(result)).toEqual(sortParts(expected)); } }); });