import { afterAll, describe, expect, test } from 'vitest' import { ident, joinSqlFragments, safeSql } from '../../src/pg-format' import { Query } from '../../src/query/Query' import { cleanupRoot, createTestDatabase } from '../db/utils' type TestDb = Awaited> async function validateSql(db: TestDb, sql: string): Promise { try { const result = await db.executeQuery(sql) return result } catch (error) { throw new Error(`Invalid SQL generated: ${sql}\nError: ${error}`) } } const withTestDatabase = (name: string, fn: (db: TestDb) => Promise) => { test(name, async () => { const db = await createTestDatabase() try { // Setup test tables with special characters, spaces, and quotes await db.executeQuery(` CREATE TABLE "public"."normal_table" ( id SERIAL PRIMARY KEY, name TEXT ); CREATE TABLE "public"."table with spaces" ( id SERIAL PRIMARY KEY, "column with spaces" TEXT, "quoted""column" TEXT, "quoted'column" TEXT, "camelCaseColumn" TEXT, "special#$%^&Column" TEXT ); CREATE TABLE "public"."quoted""table" ( id SERIAL PRIMARY KEY, name TEXT ); CREATE TABLE "public"."quoted'table" ( id SERIAL PRIMARY KEY, name TEXT ); CREATE TABLE "public"."camelCaseTable" ( id SERIAL PRIMARY KEY, name TEXT ); CREATE TABLE "public"."special#$%^&Table" ( id SERIAL PRIMARY KEY, name TEXT ); `) // Insert test data into each table await db.executeQuery(` -- Add data to normal_table INSERT INTO "public"."normal_table" (name) VALUES ('John Doe'), ('Jane Smith'), ('O''Reilly Books'), (NULL); -- Add data to table with spaces INSERT INTO "public"."table with spaces" ( "column with spaces", "quoted""column", "quoted'column", "camelCaseColumn", "special#$%^&Column" ) VALUES ('value with spaces', 'value with "quotes"', 'value with ''quotes''', 'camelCaseValue', 'special#$%^&Value'), ('another value', 'another "quoted" value', 'another ''quoted'' value', 'anotherCamelCase', 'another#$%^&'); -- Add data to quoted"table INSERT INTO "public"."quoted""table" (name) VALUES ('quoted table row 1'), ('quoted table row 2'); -- Add data to quoted'table INSERT INTO "public"."quoted'table" (name) VALUES ('single quoted table row 1'), ('single quoted table row 2'); -- Add data to camelCaseTable INSERT INTO "public"."camelCaseTable" (name) VALUES ('camel case table row 1'), ('camel case table row 2'); -- Add data to special#$%^&Table INSERT INTO "public"."special#$%^&Table" (name) VALUES ('special char table row 1'), ('special char table row 2'); `) await fn(db) } finally { await db.cleanup() } }) } describe('Advanced Query Tests', () => { afterAll(async () => { await cleanupRoot() }) describe('Special Table and Column Names', () => { withTestDatabase('should handle tables with spaces', async (db) => { const query = new Query() const sql = query.from('table with spaces', 'public').select().toSql() expect(sql).toMatchInlineSnapshot(`"select * from public."table with spaces";"`) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0]['column with spaces']).toBe('value with spaces') expect(result[1]['column with spaces']).toBe('another value') }) withTestDatabase('should handle tables with double quotes', async (db) => { const query = new Query() const sql = query.from('quoted"table', 'public').select().toSql() expect(sql).toMatchInlineSnapshot(`"select * from public."quoted""table";"`) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0].name).toBe('quoted table row 1') expect(result[1].name).toBe('quoted table row 2') }) withTestDatabase('should handle tables with single quotes', async (db) => { const query = new Query() const sql = query.from("quoted'table", 'public').select().toSql() expect(sql).toMatchInlineSnapshot(`"select * from public."quoted'table";"`) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0].name).toBe('single quoted table row 1') expect(result[1].name).toBe('single quoted table row 2') }) withTestDatabase('should handle camelCase table names', async (db) => { const query = new Query() const sql = query.from('camelCaseTable', 'public').select().toSql() expect(sql).toMatchInlineSnapshot(`"select * from public."camelCaseTable";"`) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0].name).toBe('camel case table row 1') expect(result[1].name).toBe('camel case table row 2') }) withTestDatabase('should handle tables with special characters', async (db) => { const query = new Query() const sql = query.from('special#$%^&Table', 'public').select().toSql() expect(sql).toMatchInlineSnapshot(`"select * from public."special#$%^&Table";"`) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0].name).toBe('special char table row 1') expect(result[1].name).toBe('special char table row 2') }) withTestDatabase('should handle columns with spaces', async (db) => { const query = new Query() const sql = query .from('table with spaces', 'public') .select(safeSql`"column with spaces"`) .toSql() expect(sql).toMatchInlineSnapshot( `"select "column with spaces" from public."table with spaces";"` ) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0]['column with spaces']).toBe('value with spaces') expect(result[1]['column with spaces']).toBe('another value') }) withTestDatabase('should handle columns with double quotes', async (db) => { const query = new Query() const sql = query .from('table with spaces', 'public') .select(safeSql`"quoted""column"`) .toSql() expect(sql).toMatchInlineSnapshot( `"select "quoted""column" from public."table with spaces";"` ) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0]['quoted"column']).toBe('value with "quotes"') expect(result[1]['quoted"column']).toBe('another "quoted" value') }) withTestDatabase('should handle columns with single quotes', async (db) => { const query = new Query() const sql = query .from('table with spaces', 'public') .select(safeSql`"quoted'column"`) .toSql() expect(sql).toMatchInlineSnapshot(`"select "quoted'column" from public."table with spaces";"`) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0]["quoted'column"]).toBe("value with 'quotes'") expect(result[1]["quoted'column"]).toBe("another 'quoted' value") }) withTestDatabase('should handle camelCase column names', async (db) => { const query = new Query() const sql = query .from('table with spaces', 'public') .select(safeSql`"camelCaseColumn"`) .toSql() expect(sql).toMatchInlineSnapshot( `"select "camelCaseColumn" from public."table with spaces";"` ) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0].camelCaseColumn).toBe('camelCaseValue') expect(result[1].camelCaseColumn).toBe('anotherCamelCase') }) withTestDatabase('should handle columns with special characters', async (db) => { const query = new Query() const sql = query .from('table with spaces', 'public') .select(safeSql`"special#$%^&Column"`) .toSql() expect(sql).toMatchInlineSnapshot( `"select "special#$%^&Column" from public."table with spaces";"` ) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0]['special#$%^&Column']).toBe('special#$%^&Value') expect(result[1]['special#$%^&Column']).toBe('another#$%^&') }) }) describe('Complex Queries with Special Names', () => { withTestDatabase('should handle filtering on columns with spaces', async (db) => { // First ensure the table exists with the right column await db.executeQuery(` DROP TABLE IF EXISTS "public"."table with spaces"; CREATE TABLE "public"."table with spaces" ( id SERIAL PRIMARY KEY, "column with spaces" TEXT ); -- Insert test data INSERT INTO "public"."table with spaces" ("column with spaces") VALUES ('test value'), ('other value'); `) const query = new Query() // Specify the column name without extra quotes in the filter // The Query class handles the proper quoting const sql = query .from('table with spaces', 'public') .select() .filter('column with spaces', '=', 'test value') .toSql() expect(sql).toMatchInlineSnapshot( `"select * from public."table with spaces" where "column with spaces" = 'test value';"` ) // Validate the generated SQL directly against the database const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0]['column with spaces']).toBe('test value') }) withTestDatabase('should handle filtering with values containing quotes', async (db) => { await db.executeQuery(` INSERT INTO "public"."normal_table" (name) VALUES ('O''Reilly'); `) const query = new Query() const sql = query .from('normal_table', 'public') .select() .filter('name', '=', "O'Reilly") .toSql() expect(sql).toMatchInlineSnapshot( `"select * from public.normal_table where name = 'O''Reilly';"` ) const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0].name).toBe("O'Reilly") }) withTestDatabase('should handle updating with values containing quotes', async (db) => { const query = new Query() const sql = query .from('normal_table', 'public') .update({ name: "John O'Reilly" }, { returning: true }) .filter('id', '=', 1) .toSql() expect(sql).toMatchInlineSnapshot( `"update public.normal_table set (name) = (select name from json_populate_record(null::public.normal_table, '{"name":"John O''Reilly"}')) where id = 1 returning *;"` ) await validateSql(db, sql) }) withTestDatabase('should handle inserting with values containing quotes', async (db) => { const query = new Query() const sql = query .from('normal_table', 'public') .insert([{ name: "John O'Reilly" }], { returning: true }) .toSql() expect(sql).toMatchInlineSnapshot( `"insert into public.normal_table (name) select name from jsonb_populate_recordset(null::public.normal_table, '[{"name":"John O''Reilly"}]') returning *;"` ) await validateSql(db, sql) }) }) describe('Advanced SQL Generation and Validation', () => { withTestDatabase( 'should generate valid select with multiple filters and sorting', async (db) => { await db.executeQuery(` DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (id, name) VALUES (11, 'John Smith'), (12, 'John Doe'), (13, 'Jane Smith'), (14, 'Someone Else'); `) const query = new Query() const sql = query .from('normal_table', 'public') .select(safeSql`id, name`) .filter('id', '>', 10) .filter('name', '~~', '%John%') .order('normal_table', 'name', true, false) .range(0, 9) .toSql() expect(sql).toMatchInlineSnapshot( `"select id, name from public.normal_table where id > 10 and name::text ~~ '%John%' order by normal_table.name asc nulls last limit 10 offset 0;"` ) const result = await validateSql(db, sql) expect(result.length).toBe(2) expect(result[0].name).toBe('John Doe') // Alphabetically first expect(result[1].name).toBe('John Smith') expect(result.every((row: any) => row.id > 10)).toBe(true) } ) withTestDatabase('should generate valid insert with returning clause', async (db) => { const query = new Query() const sql = query .from('normal_table', 'public') .insert([{ name: 'John Doe' }], { returning: true }) .toSql() expect(sql).toMatchInlineSnapshot( `"insert into public.normal_table (name) select name from jsonb_populate_recordset(null::public.normal_table, '[{"name":"John Doe"}]') returning *;"` ) const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0].name).toBe('John Doe') }) withTestDatabase('should generate valid update with filtering', async (db) => { await db.executeQuery(` -- Clear and insert test data DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (id, name) VALUES (1, 'Original Name') ON CONFLICT (id) DO UPDATE SET name = 'Original Name'; `) const query = new Query() const sql = query .from('normal_table', 'public') .update({ name: 'Updated Name' }, { returning: true }) .filter('id', '=', 1) .toSql() expect(sql).toMatchInlineSnapshot( `"update public.normal_table set (name) = (select name from json_populate_record(null::public.normal_table, '{"name":"Updated Name"}')) where id = 1 returning *;"` ) const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0].id).toBe(1) expect(result[0].name).toBe('Updated Name') // Verify the update was actually persisted const verifyResult = await db.executeQuery('SELECT * FROM public.normal_table WHERE id = 1') expect(verifyResult[0].name).toBe('Updated Name') }) withTestDatabase('should generate valid delete with filtering', async (db) => { await db.executeQuery(` -- Clear and insert test data DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (id, name) VALUES (1, 'To Be Deleted') ON CONFLICT (id) DO UPDATE SET name = 'To Be Deleted'; `) const query = new Query() const sql = query .from('normal_table', 'public') .delete({ returning: true }) .filter('id', '=', 1) .toSql() expect(sql).toMatchInlineSnapshot( '"delete from public.normal_table where id = 1 returning *;"' ) const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0].id).toBe(1) expect(result[0].name).toBe('To Be Deleted') // Verify the row was actually deleted const verifyResult = await db.executeQuery('SELECT * FROM public.normal_table WHERE id = 1') expect(verifyResult.length).toBe(0) }) withTestDatabase('should generate valid count with filtering', async (db) => { await db.executeQuery(` -- Clear and insert test data DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (name) VALUES ('John Smith'), ('John Doe'), ('Jane Doe'); `) const query = new Query() const sql = query .from('normal_table', 'public') .count() .filter('name', '~~', '%John%') .toSql() expect(sql).toMatchInlineSnapshot( '"select count(*) from public.normal_table where name::text ~~ \'%John%\';"' ) const result = await validateSql(db, sql) expect(result[0].count).toBe(2) // PostgreSQL returns count as string }) withTestDatabase('should generate valid truncate query', async (db) => { await db.executeQuery(` INSERT INTO "public"."normal_table" (name) VALUES ('Test Row 1'), ('Test Row 2'); `) // Verify data exists const beforeCount = await db.executeQuery(`SELECT COUNT(*) FROM "public"."normal_table"`) expect(parseInt(beforeCount[0].count)).toBeGreaterThan(0) const query = new Query() const sql = query.from('normal_table', 'public').truncate().toSql() expect(sql).toMatchInlineSnapshot('"truncate public.normal_table;"') await validateSql(db, sql) // Verify truncate worked const afterCount = await db.executeQuery(`SELECT COUNT(*) FROM "public"."normal_table"`) expect(parseInt(afterCount[0].count)).toBe(0) }) }) describe('Corner Cases and Error Handling', () => { withTestDatabase('should throw error for delete without filters', async () => { const query = new Query() const action = query.from('normal_table', 'public').delete({ returning: true }) expect(() => action.toSql()).toThrow(/no filters/) }) withTestDatabase('should throw error for update without filters', async () => { const query = new Query() const action = query.from('normal_table', 'public').update({ name: 'Updated Name' }) expect(() => action.toSql()).toThrow(/no filters/) }) withTestDatabase('should throw error for insert without values', async () => { const query = new Query() // We're passing an empty array to test the runtime error const action = query.from('normal_table', 'public').insert([] as any, { returning: true }) expect(() => action.toSql()).toThrow(/no value to insert/) }) withTestDatabase('should handle special characters in values', async (db) => { const query = new Query() const sql = query .from('normal_table', 'public') .select() .filter('name', '=', 'Special $ ^ & * ( ) _ + { } | : < > ? characters') .toSql() expect(sql).toMatchInlineSnapshot( `"select * from public.normal_table where name = 'Special $ ^ & * ( ) _ + { } | : < > ? characters';"` ) await validateSql(db, sql) }) }) describe('Advanced Filtering', () => { withTestDatabase('should handle "in" operator with array values', async (db) => { await db.executeQuery(` DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (id, name) VALUES (1, 'Row 1'), (2, 'Row 2'), (3, 'Row 3'), (4, 'Row 4'); `) const query = new Query() const sql = query .from('normal_table', 'public') .select() .filter('id', 'in', [1, 2, 3]) .toSql() expect(sql).toMatchInlineSnapshot(`"select * from public.normal_table where id in (1,2,3);"`) const result = await validateSql(db, sql) expect(result.length).toBe(3) expect(result.map((row: any) => row.id).sort()).toEqual([1, 2, 3]) }) withTestDatabase('should handle "is" operator with null value', async (db) => { await db.executeQuery(` DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (id, name) VALUES (1, 'Not Null'), (2, NULL); `) const query = new Query() const sql = query.from('normal_table', 'public').select().filter('name', 'is', 'null').toSql() expect(sql).toMatchInlineSnapshot(`"select * from public.normal_table where name is null;"`) const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0].id).toBe(2) expect(result[0].name).toBeNull() }) withTestDatabase('should handle "is" operator with not null value', async (db) => { await db.executeQuery(` DELETE FROM "public"."normal_table"; INSERT INTO "public"."normal_table" (id, name) VALUES (1, 'Not Null'), (2, NULL); `) const query = new Query() const sql = query .from('normal_table', 'public') .select() .filter('name', 'is', 'not null') .toSql() expect(sql).toMatchInlineSnapshot( `"select * from public.normal_table where name is not null;"` ) const result = await validateSql(db, sql) expect(result.length).toBe(1) expect(result[0].id).toBe(1) expect(result[0].name).toBe('Not Null') }) }) })