/** * Test script for PostgreSQL connection and basic CRUD operations * Run with: npx tsx dev/test-postgres-connection.ts */ import dotenv from 'dotenv'; import path from 'path'; import { Pool, PoolClient } from 'pg'; // Load environment variables from .env.local FIRST dotenv.config({ path: path.resolve(__dirname, '../.env.local') }); interface TestCompany { id: number; company_name: string; company_number?: string; phone?: string; is_active?: boolean; created_at?: Date; updated_at?: Date; synced_at?: Date; is_deleted?: boolean; deleted_at?: Date; } // Create a simple database client for testing class TestDBClient { private pool: Pool; constructor() { // Use localhost when running outside Docker, postgres hostname is for Docker network const host = process.env.POSTGRES_HOST === 'postgres' ? 'localhost' : (process.env.POSTGRES_HOST || 'localhost'); this.pool = new Pool({ host, port: parseInt(process.env.POSTGRES_PORT || '5432'), database: process.env.POSTGRES_DB || 'pulse_autotask', user: process.env.POSTGRES_USER || 'pulse_user', password: process.env.POSTGRES_PASSWORD, max: 10, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); } async query(text: string, params?: any[]) { return await this.pool.query(text, params); } async testConnection(): Promise { try { await this.query('SELECT 1'); return true; } catch (error) { console.error('Database connection test failed:', error); return false; } } async close() { await this.pool.end(); } } async function testPostgresConnection() { console.log('šŸ” Testing PostgreSQL Connection and CRUD Operations\n'); // Debug: Check environment variables console.log('Environment variables:'); console.log(' POSTGRES_HOST:', process.env.POSTGRES_HOST); console.log(' POSTGRES_PORT:', process.env.POSTGRES_PORT); console.log(' POSTGRES_DB:', process.env.POSTGRES_DB); console.log(' POSTGRES_USER:', process.env.POSTGRES_USER); console.log(' POSTGRES_PASSWORD:', process.env.POSTGRES_PASSWORD ? '[SET]' : '[NOT SET]'); console.log(' Password type:', typeof process.env.POSTGRES_PASSWORD); console.log(); const db = new TestDBClient(); try { // Test 1: Connection Test console.log('Test 1: Connection Test'); const isConnected = await db.testConnection(); if (isConnected) { console.log('āœ… Database connection successful\n'); } else { console.log('āŒ Database connection failed\n'); return; } // Test 2: Insert Operation console.log('Test 2: Insert Operation'); const insertResult = await db.query( `INSERT INTO companies (id, company_name, company_number, phone, is_active, synced_at, is_deleted) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *`, [999999, 'Test Company Inc', 'TEST-001', '555-0123', true, new Date(), false] ); const inserted = insertResult.rows[0]; console.log('āœ… Insert successful:', inserted.company_name); console.log(' ID:', inserted.id, '\n'); // Test 3: Find by ID console.log('Test 3: Find by ID'); const findResult = await db.query( 'SELECT * FROM companies WHERE id = $1 AND is_deleted = false', [999999] ); const found = findResult.rows[0]; if (found && found.company_name === 'Test Company Inc') { console.log('āœ… Find by ID successful:', found.company_name, '\n'); } else { console.log('āŒ Find by ID failed\n'); } // Test 4: Update Operation console.log('Test 4: Update Operation'); const updateResult = await db.query( `UPDATE companies SET company_name = $1, phone = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $3 RETURNING *`, ['Test Company Updated', '555-9999', 999999] ); const updated = updateResult.rows[0]; console.log('āœ… Update successful:', updated.company_name); console.log(' Phone:', updated.phone, '\n'); // Test 5: Upsert Operation (Update existing) console.log('Test 5: Upsert Operation (Update existing)'); const upsertResult1 = await db.query( `INSERT INTO companies (id, company_name, company_number, phone, is_active, synced_at, is_deleted) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO UPDATE SET company_name = EXCLUDED.company_name, phone = EXCLUDED.phone, updated_at = CURRENT_TIMESTAMP RETURNING *`, [999999, 'Test Company Upserted', 'TEST-001', '555-8888', true, new Date(), false] ); const upserted1 = upsertResult1.rows[0]; console.log('āœ… Upsert (update) successful:', upserted1.company_name, '\n'); // Test 6: Upsert Operation (Insert new) console.log('Test 6: Upsert Operation (Insert new)'); const upsertResult2 = await db.query( `INSERT INTO companies (id, company_name, company_number, phone, is_active, synced_at, is_deleted) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO UPDATE SET company_name = EXCLUDED.company_name, phone = EXCLUDED.phone, updated_at = CURRENT_TIMESTAMP RETURNING *`, [999998, 'Test Company 2', 'TEST-002', '555-7777', true, new Date(), false] ); const upserted2 = upsertResult2.rows[0]; console.log('āœ… Upsert (insert) successful:', upserted2.company_name, '\n'); // Test 7: Find with criteria console.log('Test 7: Find with criteria'); const findAllResult = await db.query( `SELECT * FROM companies WHERE is_active = true AND is_deleted = false ORDER BY company_name LIMIT 5` ); const companies = findAllResult.rows; console.log(`āœ… Find successful: Found ${companies.length} active companies`); companies.slice(0, 3).forEach((c: TestCompany) => { console.log(` - ${c.company_name} (ID: ${c.id})`); }); console.log(); // Test 8: Count records console.log('Test 8: Count records'); const countResult = await db.query( 'SELECT COUNT(*) as count FROM companies WHERE is_active = true AND is_deleted = false' ); const count = parseInt(countResult.rows[0].count); console.log(`āœ… Count successful: ${count} active companies\n`); // Test 9: Soft Delete console.log('Test 9: Soft Delete'); await db.query( `UPDATE companies SET is_deleted = true, deleted_at = CURRENT_TIMESTAMP WHERE id = $1`, [999999] ); const deletedResult = await db.query( 'SELECT * FROM companies WHERE id = $1', [999999] ); const deletedRecord = deletedResult.rows[0]; if (deletedRecord && deletedRecord.is_deleted) { console.log('āœ… Soft delete successful'); console.log(' Record marked as deleted:', deletedRecord.is_deleted); console.log(' Deleted at:', deletedRecord.deleted_at, '\n'); } else { console.log('āŒ Soft delete failed\n'); } // Test 10: Verify soft-deleted record is excluded by default console.log('Test 10: Verify soft-deleted record excluded by default'); const notFoundResult = await db.query( 'SELECT * FROM companies WHERE id = $1 AND is_deleted = false', [999999] ); if (notFoundResult.rows.length === 0) { console.log('āœ… Soft-deleted record correctly excluded from default queries\n'); } else { console.log('āŒ Soft-deleted record should not be returned\n'); } // Cleanup: Delete all test records console.log('Cleanup: Deleting test records'); const testIds = [999999, 999998]; await db.query( 'DELETE FROM companies WHERE id = ANY($1::bigint[])', [testIds] ); console.log('āœ… Cleanup complete\n'); console.log('šŸŽ‰ All tests passed successfully!'); } catch (error) { console.error('āŒ Test failed with error:', error); throw error; } finally { // Close the connection pool await db.close(); console.log('\nšŸ”Œ Database connection closed'); } } // Run the tests testPostgresConnection() .then(() => { console.log('\nāœ… Test script completed successfully'); process.exit(0); }) .catch((error) => { console.error('\nāŒ Test script failed:', error); process.exit(1); });