import { sql } from 'drizzle-orm'; import { drizzle, type NetlifyDbDatabase } from 'drizzle-orm/netlify-db'; import { migrate } from 'drizzle-orm/netlify-db/migrator'; import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core'; import { beforeAll, beforeEach, expect, test } from 'vitest'; import { skipTests } from '~/common'; import { randomString } from '~/utils'; import { tests, usersMigratorTable, usersTable } from './pg-common'; import { TestCache, TestGlobalCache, tests as cacheTests } from './pg-common-cache'; const ENABLE_LOGGING = false; let db: NetlifyDbDatabase; let dbGlobalCached: NetlifyDbDatabase; let cachedDb: NetlifyDbDatabase; beforeAll(async () => { const connectionString = process.env['NETLIFY_DB_URL']; if (!connectionString) { throw new Error('NETLIFY_DB_URL is not defined'); } db = drizzle(connectionString, { logger: ENABLE_LOGGING }); cachedDb = drizzle(connectionString, { logger: ENABLE_LOGGING, cache: new TestCache(), }); dbGlobalCached = drizzle(connectionString, { logger: ENABLE_LOGGING, cache: new TestGlobalCache(), }); }); beforeEach((ctx) => { ctx.pg = { db, }; ctx.cachedPg = { db: cachedDb, dbGlobalCached, }; }); test('migrator : default migration strategy', async () => { await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); await migrate(db, { migrationsFolder: './drizzle2/pg' }); await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); const result = await db.select().from(usersMigratorTable); expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); }); test('migrator : migrate with custom schema', async () => { await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: 'custom_migrations' }); // test if the custom migrations table was created const { rowCount } = await db.execute(sql`select * from custom_migrations."__drizzle_migrations";`); expect(rowCount && rowCount > 0).toBeTruthy(); // test if the migrated table are working as expected await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); const result = await db.select().from(usersMigratorTable); expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); await db.execute(sql`drop table custom_migrations."__drizzle_migrations"`); }); test('migrator : migrate with custom table', async () => { const customTable = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); // test if the custom migrations table was created const { rowCount } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); expect(rowCount && rowCount > 0).toBeTruthy(); // test if the migrated table are working as expected await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); const result = await db.select().from(usersMigratorTable); expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); }); test('migrator : migrate with custom table and custom schema', async () => { const customTable = randomString(); await db.execute(sql`drop table if exists all_columns`); await db.execute(sql`drop table if exists users12`); await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable, migrationsSchema: 'custom_migrations', }); // test if the custom migrations table was created const { rowCount } = await db.execute( sql`select * from custom_migrations.${sql.identifier(customTable)};`, ); expect(rowCount && rowCount > 0).toBeTruthy(); // test if the migrated table are working as expected await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); const result = await db.select().from(usersMigratorTable); expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); await db.execute(sql`drop table all_columns`); await db.execute(sql`drop table users12`); await db.execute(sql`drop table custom_migrations.${sql.identifier(customTable)}`); }); test('all date and time columns without timezone first case mode string', async () => { const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(6) not null ) `); // 1. Insert date in string format without timezone in it await db.insert(table).values([ { timestamp: '2022-01-01 02:00:00.123456' }, ]); // 2, Select in string format and check that values are the same const result = await db.select().from(table); expect(result).toEqual([{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); // 3. Select as raw query and check that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); expect(result2.rows).toEqual([{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); await db.execute(sql`drop table if exists ${table}`); }); test('all date and time columns without timezone second case mode string', async () => { const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(6) not null ) `); // 1. Insert date in string format with timezone in it await db.insert(table).values([ { timestamp: '2022-01-01T02:00:00.123456-02' }, ]); // 2, Select as raw query and check that values are the same const result = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); expect(result.rows).toEqual([{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); await db.execute(sql`drop table if exists ${table}`); }); test('all date and time columns without timezone third case mode date', async () => { const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(3) not null ) `); const insertedDate = new Date('2022-01-01 20:00:00.123+04'); // 1. Insert date as new date await db.insert(table).values([ { timestamp: insertedDate }, ]); // 2, Select as raw query as string const result = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC expect(new Date(result.rows[0]!.timestamp_string + 'Z').getTime()).toBe(insertedDate.getTime()); await db.execute(sql`drop table if exists ${table}`); }); test('test mode string for timestamp with timezone', async () => { const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(6) with time zone not null ) `); const timestampString = '2022-01-01 00:00:00.123456-0200'; // 1. Insert date in string format with timezone in it await db.insert(table).values([ { timestamp: timestampString }, ]); // 2. Select date in string format and check that the values are the same const result = await db.select().from(table); // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same expect(result).toEqual([{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); // 3. Select as raw query and checke that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same expect(result2.rows).toEqual([{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); await db.execute(sql`drop table if exists ${table}`); }); test('test mode date for timestamp with timezone', async () => { const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(3) with time zone not null ) `); const timestampString = new Date('2022-01-01 00:00:00.456-0200'); // 1. Insert date in string format with timezone in it await db.insert(table).values([ { timestamp: timestampString }, ]); // 2. Select date in string format and check that the values are the same const result = await db.select().from(table); // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same expect(result).toEqual([{ id: 1, timestamp: timestampString }]); // 3. Select as raw query and checke that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same expect(result2.rows).toEqual([{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]); await db.execute(sql`drop table if exists ${table}`); }); test('test mode string for timestamp with timezone in UTC timezone', async () => { // get current timezone from db const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); // set timezone to UTC await db.execute(sql`set time zone 'UTC'`); const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(6) with time zone not null ) `); const timestampString = '2022-01-01 00:00:00.123456-0200'; // 1. Insert date in string format with timezone in it await db.insert(table).values([ { timestamp: timestampString }, ]); // 2. Select date in string format and check that the values are the same const result = await db.select().from(table); // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same expect(result).toEqual([{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); // 3. Select as raw query and checke that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same expect(result2.rows).toEqual([{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); await db.execute(sql`drop table if exists ${table}`); }); test.skip('test mode string for timestamp with timezone in different timezone', async () => { // get current timezone from db const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); // set timezone to HST (UTC - 10) await db.execute(sql`set time zone 'HST'`); const table = pgTable('all_columns', { id: serial('id').primaryKey(), timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), }); await db.execute(sql`drop table if exists ${table}`); await db.execute(sql` create table ${table} ( id serial primary key, timestamp_string timestamp(6) with time zone not null ) `); const timestampString = '2022-01-01 00:00:00.123456-1000'; // 1. Insert date in string format with timezone in it await db.insert(table).values([ { timestamp: timestampString }, ]); // 2. Select date in string format and check that the values are the same const result = await db.select().from(table); expect(result).toEqual([{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]); // 3. Select as raw query and checke that values are the same const result2 = await db.execute<{ id: number; timestamp_string: string; }>(sql`select * from ${table}`); expect(result2.rows).toEqual([{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456+00' }]); await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); await db.execute(sql`drop table if exists ${table}`); }); skipTests([]); tests(); cacheTests(); beforeEach(async () => { await db.execute(sql`drop schema if exists public cascade`); await db.execute(sql`create schema public`); await db.execute( sql` create table users ( id serial primary key, name text not null, verified boolean not null default false, jsonb jsonb, created_at timestamptz not null default now() ) `, ); }); test('insert via db.execute + select via db.execute', async () => { await db.execute( sql`insert into ${usersTable} (${sql.identifier(usersTable.name.name)}) values (${'John'})`, ); const result = await db.execute<{ id: number; name: string }>( sql`select id, name from "users"`, ); expect(result.rows).toEqual([{ id: 1, name: 'John' }]); }); test('insert via db.execute + returning', async () => { const inserted = await db.execute<{ id: number; name: string }>( sql`insert into ${usersTable} (${ sql.identifier( usersTable.name.name, ) }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, ); expect(inserted.rows).toEqual([{ id: 1, name: 'John' }]); }); test('insert via db.execute w/ query builder', async () => { const inserted = await db.execute>( db .insert(usersTable) .values({ name: 'John' }) .returning({ id: usersTable.id, name: usersTable.name }), ); expect(inserted.rows).toEqual([{ id: 1, name: 'John' }]); });