import { describe, expect, test } from 'vitest' import { ident, joinSqlFragments, safeSql } from '../../src/pg-format' import { Query } from '../../src/query/Query' import * as QueryUtils from '../../src/query/Query.utils' import { QueryAction } from '../../src/query/QueryAction' import { QueryFilter } from '../../src/query/QueryFilter' import { QueryModifier } from '../../src/query/QueryModifier' import type { Filter, QueryTable, Sort } from '../../src/query/types' describe('Query', () => { test('from() should create a QueryAction with the correct table', () => { const query = new Query() const action = query.from('users', 'public') expect(action).toBeInstanceOf(QueryAction) expect(action['table']).toEqual({ name: 'users', schema: 'public' }) }) test('from() should use "public" as the default schema when not provided', () => { const query = new Query() const action = query.from('users') expect(action['table']).toEqual({ name: 'users', schema: 'public' }) }) }) describe('QueryAction', () => { const table: QueryTable = { name: 'users', schema: 'public' } test('count() should create a QueryFilter with the correct action', () => { const action = new QueryAction(table) const filter = action.count() expect(filter).toBeInstanceOf(QueryFilter) expect(filter['table']).toEqual(table) expect(filter['actionConfig'].action).toBe('count') }) test('delete() should create a QueryFilter with the correct action and options', () => { const action = new QueryAction(table) const filter = action.delete({ returning: true }) expect(filter).toBeInstanceOf(QueryFilter) expect(filter['table']).toEqual(table) expect(filter['actionConfig'].action).toBe('delete') expect(filter['actionOptions']).toEqual({ returning: true }) }) test('insert() should create a QueryFilter with the correct action, values and options', () => { const action = new QueryAction(table) const values = [{ id: 1, name: 'John' }] const filter = action.insert(values, { returning: true }) expect(filter).toBeInstanceOf(QueryFilter) expect(filter['table']).toEqual(table) expect(filter['actionConfig']).toEqual({ action: 'insert', actionValue: values }) expect(filter['actionOptions']).toEqual({ returning: true }) }) test('select() should create a QueryFilter with the correct action and columns', () => { const action = new QueryAction(table) const cols = joinSqlFragments([ident('id'), ident('name')], ', ') const filter = action.select(cols) expect(filter).toBeInstanceOf(QueryFilter) expect(filter['table']).toEqual(table) expect(filter['actionConfig']).toEqual({ action: 'select', actionValue: 'id, name' }) }) test('update() should create a QueryFilter with the correct action, value and options', () => { const action = new QueryAction(table) const value = { name: 'John' } const filter = action.update(value, { returning: true }) expect(filter).toBeInstanceOf(QueryFilter) expect(filter['table']).toEqual(table) expect(filter['actionConfig']).toEqual({ action: 'update', actionValue: value }) expect(filter['actionOptions']).toEqual({ returning: true }) }) test('truncate() should create a QueryFilter with the correct action and options', () => { const action = new QueryAction(table) const filter = action.truncate({ returning: true }) expect(filter).toBeInstanceOf(QueryFilter) expect(filter['table']).toEqual(table) expect(filter['actionConfig'].action).toBe('truncate') expect(filter['actionOptions']).toEqual({ returning: true }) }) }) describe('QueryFilter', () => { const table: QueryTable = { name: 'users', schema: 'public' } test('filter() should add a filter and return the filter instance', () => { const queryFilter = new QueryFilter(table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', '), }) const result = queryFilter.filter('id', '=', 1) expect(result).toBe(queryFilter) expect(queryFilter['filters']).toEqual([{ column: 'id', operator: '=', value: 1 }]) }) test('match() should add multiple filters and return the filter instance', () => { const queryFilter = new QueryFilter(table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', '), }) const result = queryFilter.match({ id: 1, name: 'John' }) expect(result).toBe(queryFilter) expect(queryFilter['filters']).toEqual([ { column: 'id', operator: '=', value: 1 }, { column: 'name', operator: '=', value: 'John' }, ]) }) test('order() should add a sort and return the filter instance', () => { const queryFilter = new QueryFilter(table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', '), }) const result = queryFilter.order('users', 'name', false, true) expect(result).toBe(queryFilter) expect(queryFilter['sorts']).toEqual([ { table: 'users', column: 'name', ascending: false, nullsFirst: true }, ]) }) test('range() should delegate to QueryModifier.range() and return the result', () => { const queryFilter = new QueryFilter(table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', '), }) const result = queryFilter.range(0, 10) expect(result).toBeInstanceOf(QueryModifier) // The pagination gets set in the QueryModifier expect(result['pagination']).toEqual({ offset: 0, limit: 11 }) }) test('toSql() should delegate to QueryModifier.toSql() and return the SQL string', () => { const queryFilter = new QueryFilter(table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', '), }) queryFilter.filter('id', '=', 1) const result = queryFilter.toSql() // Expected SQL should match the pattern from QueryUtils.selectQuery() expect(result).toBe('select id, name from public.users where id = 1;') }) }) describe('QueryModifier', () => { const table: QueryTable = { name: 'users', schema: 'public' } test('range() should set the pagination and return the modifier instance', () => { const queryModifier = new QueryModifier(table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', '), }) const result = queryModifier.range(0, 10) expect(result).toBe(queryModifier) expect(queryModifier['pagination']).toEqual({ offset: 0, limit: 11 }) }) test('toSql() should generate the correct SQL for a count query', () => { const queryModifier = new QueryModifier(table, { action: 'count' }) const result = queryModifier.toSql() expect(result).toBe('select count(*) from public.users;') }) test('toSql() should generate the correct SQL for a delete query with filters', () => { const queryModifier = new QueryModifier( table, { action: 'delete' }, { filters: [{ column: 'id', operator: '=', value: 1 }], actionOptions: { returning: true }, } ) const result = queryModifier.toSql() expect(result).toBe('delete from public.users where id = 1 returning *;') }) test('toSql() should generate the correct SQL for a select query with filters, sorts and pagination', () => { const queryModifier = new QueryModifier( table, { action: 'select', actionValue: joinSqlFragments([ident('id'), ident('name')], ', ') }, { filters: [{ column: 'id', operator: '>', value: 10 }], sorts: [{ table: 'users', column: 'name', ascending: true, nullsFirst: false }], } ) queryModifier.range(0, 5) const result = queryModifier.toSql() expect(result).toMatchInlineSnapshot( `"select id, name from public.users where id > 10 order by users.name asc nulls last limit 6 offset 0;"` ) }) test('toSql() should generate the correct SQL for a truncate query', () => { const queryModifier = new QueryModifier(table, { action: 'truncate' }) const result = queryModifier.toSql() expect(result).toBe('truncate public.users;') }) test('toSql() should generate the correct SQL for a truncate query with cascade', () => { const queryModifier = new QueryModifier( table, { action: 'truncate' }, { actionOptions: { cascade: true }, } ) const result = queryModifier.toSql() expect(result).toBe('truncate public.users cascade;') }) }) describe('Query.utils', () => { const table: QueryTable = { name: 'users', schema: 'public' } describe('countQuery', () => { test('should generate a correct count query without filters', () => { const result = QueryUtils.countQuery(table) expect(result).toBe('select count(*) from public.users;') }) test('should generate a correct count query with filters', () => { const filters = [{ column: 'id', operator: '>' as const, value: 1 }] const result = QueryUtils.countQuery(table, { filters: filters }) expect(result).toBe('select count(*) from public.users where id > 1;') }) }) describe('truncateQuery', () => { test('should generate a correct truncate query without cascade', () => { const result = QueryUtils.truncateQuery(table) expect(result).toBe('truncate public.users;') }) test('should generate a correct truncate query with cascade', () => { const result = QueryUtils.truncateQuery(table, { cascade: true }) expect(result).toBe('truncate public.users cascade;') }) }) describe('deleteQuery', () => { test('should throw an error if no filters are provided', () => { expect(() => QueryUtils.deleteQuery(table)).toThrow() }) test('should generate a correct delete query with filters', () => { const filters = [{ column: 'id', operator: '=' as const, value: 1 }] const result = QueryUtils.deleteQuery(table, filters) expect(result).toBe('delete from public.users where id = 1;') }) test('should include returning clause when specified', () => { const filters = [{ column: 'id', operator: '=' as const, value: 1 }] const result = QueryUtils.deleteQuery(table, filters, { returning: true }) expect(result).toBe('delete from public.users where id = 1 returning *;') }) test('should include enum array columns in returning clause when specified', () => { const filters = [{ column: 'id', operator: '=' as const, value: 1 }] const result = QueryUtils.deleteQuery(table, filters, { returning: true, enumArrayColumns: ['tags'], }) expect(result).toBe('delete from public.users where id = 1 returning *, tags::text[];') }) }) describe('insertQuery', () => { test('should throw an error if no values are provided', () => { expect(() => QueryUtils.insertQuery(table, [])).toThrow() }) test('should generate a correct insert query with values', () => { const values = [{ id: 1, name: 'John' }] const result = QueryUtils.insertQuery(table, values) expect(result).toMatchInlineSnapshot( `"insert into public.users (id,name) select id,name from jsonb_populate_recordset(null::public.users, '[{"id":1,"name":"John"}]');"` ) }) test('should include returning clause when specified', () => { const values = [{ id: 1, name: 'John' }] const result = QueryUtils.insertQuery(table, values, { returning: true }) expect(result).toMatchInlineSnapshot( `"insert into public.users (id,name) select id,name from jsonb_populate_recordset(null::public.users, '[{"id":1,"name":"John"}]') returning *;"` ) }) test('should include enum array columns in returning clause when specified', () => { const values = [{ id: 1, name: 'John' }] const result = QueryUtils.insertQuery(table, values, { returning: true, enumArrayColumns: ['tags'], }) expect(result).toMatchInlineSnapshot( `"insert into public.users (id,name) select id,name from jsonb_populate_recordset(null::public.users, '[{"id":1,"name":"John"}]') returning *, tags::text[];"` ) }) }) describe('selectQuery', () => { test('should generate a correct select query without options', () => { const result = QueryUtils.selectQuery(table) expect(result).toBe('select * from public.users;') }) test('should generate a correct select query with custom columns', () => { const result = QueryUtils.selectQuery( table, joinSqlFragments([ident('id'), ident('name')], ', ') ) expect(result).toBe('select id, name from public.users;') }) test('should generate a correct select query with filters', () => { const filters = [{ column: 'id', operator: '>' as const, value: 1 }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where id > 1;') }) test('should generate a correct select query with sorts', () => { const sorts = [{ table: 'users', column: 'name', ascending: true, nullsFirst: false }] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toBe('select * from public.users order by users.name asc nulls last;') }) test('should generate a correct select query with pagination', () => { const pagination = { limit: 10, offset: 0 } const result = QueryUtils.selectQuery(table, safeSql`*`, { pagination: pagination }) expect(result).toBe('select * from public.users limit 10 offset 0;') }) test('should ignore sorts with undefined column', () => { const sorts: Sort[] = [{ table: 'users', column: '', ascending: true, nullsFirst: false }] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toMatchInlineSnapshot(`"select * from public.users;"`) }) }) describe('updateQuery', () => { test('should throw an error if no filters are provided', () => { const value = { name: 'John' } expect(() => QueryUtils.updateQuery(table, value)).toThrow() }) test('should generate a correct update query with filters', () => { const value = { name: 'John' } const filters = [{ column: 'id', operator: '=' as const, value: 1 }] const result = QueryUtils.updateQuery(table, value, { filters: filters }) expect(result).toMatchInlineSnapshot( `"update public.users set (name) = (select name from json_populate_record(null::public.users, '{"name":"John"}')) where id = 1;"` ) }) test('should include returning clause when specified', () => { const value = { name: 'John' } const filters = [{ column: 'id', operator: '=' as const, value: 1 }] const result = QueryUtils.updateQuery(table, value, { filters: filters, returning: true, }) expect(result).toMatchInlineSnapshot( `"update public.users set (name) = (select name from json_populate_record(null::public.users, '{"name":"John"}')) where id = 1 returning *;"` ) }) test('should include enum array columns in returning clause when specified', () => { const value = { name: 'John' } const filters = [{ column: 'id', operator: '=' as const, value: 1 }] const result = QueryUtils.updateQuery(table, value, { filters: filters, returning: true, enumArrayColumns: ['tags'], }) expect(result).toMatchInlineSnapshot( `"update public.users set (name) = (select name from json_populate_record(null::public.users, '{"name":"John"}')) where id = 1 returning *, tags::text[];"` ) }) }) describe('Query.utils internal functions', () => { describe('applyFilters', () => { test('should correctly apply equality filters', () => { const filters: Filter[] = [{ column: 'name', operator: '=', value: 'John' }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe("select * from public.users where name = 'John';") }) test('should correctly apply multiple filters with AND logic', () => { const filters: Filter[] = [ { column: 'name', operator: '=', value: 'John' }, { column: 'age', operator: '>', value: 25 }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe("select * from public.users where name = 'John' and age > 25;") }) test('should correctly handle "in" operator with array values', () => { const filters: Filter[] = [{ column: 'id', operator: 'in', value: [1, 2, 3] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where id in (1,2,3);') }) test('should correctly handle "in" operator with comma-separated string', () => { const filters: Filter[] = [{ column: 'id', operator: 'in', value: '1,2,3' }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe("select * from public.users where id in ('1','2','3');") }) test('should correctly handle "is" operator with null value', () => { const filters: Filter[] = [{ column: 'email', operator: 'is', value: 'null' }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where email is null;') }) test('should correctly handle "is" operator with not null value', () => { const filters: Filter[] = [{ column: 'email', operator: 'is', value: 'not null' }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where email is not null;') }) test('should correctly handle "is" operator with boolean values', () => { const filters: Filter[] = [{ column: 'active', operator: 'is', value: 'true' }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where active is true;') }) test('should correctly escape string values in filters', () => { const filters: Filter[] = [{ column: 'name', operator: '=', value: "O'Reilly" }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toContain("where name = 'O''Reilly'") }) test('should error if tuple filter value length does not match column length', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '=', value: [1] }] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError( 'Tuple filter value must have the same length as the column array' ) }) test('should error if tuple filter value is not an array', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '=', value: 1 }] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError( 'Tuple filter value must be an array' ) }) test('should correctly handle tuple filters with equality operator', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '=', value: [1, 2] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where (id, version) = (1, 2);') }) test('should correctly handle tuple filters with greater than operator', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '>', value: [1, 2] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where (id, version) > (1, 2);') }) test('should correctly handle tuple filters with greater than or equal operator', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '>=', value: [1, 2] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where (id, version) >= (1, 2);') }) test('should correctly handle tuple filters with less than operator', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '<', value: [10, 5] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where (id, version) < (10, 5);') }) test('should correctly handle tuple filters with less than or equal operator', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '<=', value: [10, 5] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where (id, version) <= (10, 5);') }) test('should correctly handle tuple filters with not equal operator (<>)', () => { const filters: Filter[] = [{ column: ['id', 'version'], operator: '<>', value: [1, 2] }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where (id, version) <> (1, 2);') }) test('should correctly handle tuple filters with in operator', () => { const filters: Filter[] = [ { column: ['id', 'version'], operator: 'in', value: [ [1, 2], [3, 4], [5, 6], ], }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe( 'select * from public.users where (id, version) in ((1, 2), (3, 4), (5, 6));' ) }) test('should error if tuple filters with in operator do not have matching number of array values', () => { const filters: Filter[] = [ { column: ['id', 'version'], operator: 'in', value: [[1, 2], [3, 4], [5]], }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) test('should correctly handle tuple filters with in operator using strings', () => { const filters: Filter[] = [ { column: ['id', 'version'], operator: 'in', value: ['one,two', 'three,four', 'five,six'], }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe( `select * from public.users where (id, version) in (('one', 'two'), ('three', 'four'), ('five', 'six'));` ) }) test('should error if tuple filters with in operator do not have matching number of stringified values', () => { const filters: Filter[] = [ { column: ['id', 'version'], operator: 'in', value: ['one,two', 'three,four', 'five'], }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) test('should correctly handle tuple filters with string values', () => { const filters: Filter[] = [ { column: ['first_name', 'last_name'], operator: '=', value: ['John', 'Doe'] }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe( "select * from public.users where (first_name, last_name) = ('John', 'Doe');" ) }) test('should correctly handle mixed tuple and regular filters', () => { const filters: Filter[] = [ { column: ['id', 'version'], operator: '>', value: [1, 2] }, { column: 'active', operator: '=', value: true }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe( 'select * from public.users where (id, version) > (1, 2) and active = true;' ) }) test('should error when trying to use "is" operator as a tuple filter', () => { const filters: Filter[] = [ { column: ['id', 'version'], operator: 'is', value: [null, null], }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) test('should error when trying to use "~~" operator as a tuple filter', () => { const filters: Filter[] = [ { column: ['first_name', 'last_name'], operator: '~~', value: ['%John%', '%Doe%'], }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) test('should error when trying to use "~~*" operator as a tuple filter', () => { const filters: Filter[] = [ { column: ['first_name', 'last_name'], operator: '~~*', value: ['%john%', '%doe%'] }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) test('should error when trying to use "!~~" operator as a tuple filter', () => { const filters: Filter[] = [ { column: ['first_name', 'last_name'], operator: '!~~', value: ['%Admin%', '%System%'] }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) test('should error when trying to use "!~~*" operator as a tuple filter', () => { const filters: Filter[] = [ { column: ['first_name', 'last_name'], operator: '!~~*', value: ['%admin%', '%system%'] }, ] expect(() => QueryUtils.selectQuery(table, safeSql`*`, { filters: filters })).toThrowError() }) }) describe('applySorts', () => { test('should correctly apply a single sort with default options', () => { const sorts: Sort[] = [ { table: 'users', column: 'name', ascending: true, nullsFirst: false }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toBe('select * from public.users order by users.name asc nulls last;') }) test('should correctly apply a descending sort', () => { const sorts: Sort[] = [ { table: 'users', column: 'name', ascending: false, nullsFirst: false }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toBe('select * from public.users order by users.name desc nulls last;') }) test('should correctly apply nulls first option', () => { const sorts: Sort[] = [ { table: 'users', column: 'name', ascending: true, nullsFirst: true }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toBe('select * from public.users order by users.name asc nulls first;') }) test('should correctly apply multiple sorts', () => { const sorts: Sort[] = [ { table: 'users', column: 'last_name', ascending: true, nullsFirst: false }, { table: 'users', column: 'first_name', ascending: true, nullsFirst: false }, ] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toBe( 'select * from public.users order by users.last_name asc nulls last, users.first_name asc nulls last;' ) }) test('should ignore sorts with undefined column', () => { const sorts: Sort[] = [{ table: 'users', column: '', ascending: true, nullsFirst: false }] const result = QueryUtils.selectQuery(table, safeSql`*`, { sorts: sorts }) expect(result).toMatchInlineSnapshot(`"select * from public.users;"`) }) }) describe('filterLiteral', () => { test('should correctly handle array literal syntax', () => { const filters: Filter[] = [{ column: 'tags', operator: '=', value: "ARRAY['tag1','tag2']" }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe("select * from public.users where tags = ARRAY['tag1','tag2'];") }) test('should correctly handle non-string values', () => { const filters: Filter[] = [{ column: 'active', operator: '=', value: true }] const result = QueryUtils.selectQuery(table, safeSql`*`, { filters: filters }) expect(result).toBe('select * from public.users where active = true;') }) }) describe('queryTable', () => { test('should correctly format the table name with schema', () => { const result = QueryUtils.selectQuery({ name: 'orders', schema: 'shop' }) expect(result).toBe('select * from shop.orders;') }) }) }) }) describe('End-to-end query chaining', () => { test('should correctly build a simple select query', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .toSql() expect(sql).toBe('select id, name, email from public.users;') }) test('should correctly build a filtered select query', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .filter('id', '>', 10) .toSql() expect(sql).toBe('select id, name, email from public.users where id > 10;') }) test('should correctly build a select query with multiple filters', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .filter('id', '>', 10) .filter('name', '~~', '%John%') .toSql() expect(sql).toBe( "select id, name, email from public.users where id > 10 and name::text ~~ '%John%';" ) }) test('should correctly build a select query with match criteria', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .match({ active: true, role: 'admin' }) .toSql() expect(sql).toBe( "select id, name, email from public.users where active = true and role = 'admin';" ) }) test('should correctly build a select query with sorting', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .order('users', 'name', true, false) .toSql() expect(sql).toBe('select id, name, email from public.users order by users.name asc nulls last;') }) test('should correctly build a select query with pagination', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .range(0, 9) .toSql() expect(sql).toBe('select id, name, email from public.users limit 10 offset 0;') }) test('should correctly build a complete select query with filters, sorting and pagination', () => { const query = new Query() const sql = query .from('users', 'public') .select(joinSqlFragments([ident('id'), ident('name'), ident('email')], ', ')) .filter('id', '>', 10) .match({ active: true }) .order('users', 'name', true, false) .range(0, 9) .toSql() expect(sql).toBe( 'select id, name, email from public.users where id > 10 and active = true order by users.name asc nulls last limit 10 offset 0;' ) }) test('should correctly build an insert query', () => { const query = new Query() const sql = query .from('users', 'public') .insert([{ name: 'John', email: 'john@example.com' }], { returning: true }) .toSql() expect(sql).toMatchInlineSnapshot( `"insert into public.users (name,email) select name,email from jsonb_populate_recordset(null::public.users, '[{"name":"John","email":"john@example.com"}]') returning *;"` ) }) test('should correctly build an update query', () => { const query = new Query() const sql = query .from('users', 'public') .update({ name: 'Updated Name' }, { returning: true }) .filter('id', '=', 1) .toSql() expect(sql).toMatchInlineSnapshot( `"update public.users set (name) = (select name from json_populate_record(null::public.users, '{"name":"Updated Name"}')) where id = 1 returning *;"` ) }) test('should correctly build a delete query', () => { const query = new Query() const sql = query .from('users', 'public') .delete({ returning: true }) .filter('id', '=', 1) .toSql() expect(sql).toBe('delete from public.users where id = 1 returning *;') }) test('should correctly build a count query', () => { const query = new Query() const sql = query.from('users', 'public').count().filter('active', '=', true).toSql() expect(sql).toBe('select count(*) from public.users where active = true;') }) test('should correctly build a truncate query', () => { const query = new Query() const sql = query.from('users', 'public').truncate().toSql() expect(sql).toBe('truncate public.users;') }) })