tests cannot connect to db
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 1m3s

This commit is contained in:
2025-11-23 09:26:12 -08:00
parent 93f5481f94
commit a97e38fc6e
9 changed files with 63 additions and 12 deletions

View File

@@ -1,5 +1,6 @@
import express, { Request, Response, NextFunction } from 'express'; import express, { Request, Response, NextFunction } from 'express';
import cookieParser from 'cookie-parser'; import cookieParser from 'cookie-parser';
import { getPool } from './src/services/db/connection';
import passport from './src/routes/passport'; import passport from './src/routes/passport';
import { logger } from './src/services/logger'; import { logger } from './src/services/logger';
@@ -23,6 +24,17 @@ logger.info(` Host: ${process.env.DB_HOST}`);
logger.info(` Port: ${process.env.DB_PORT}`); logger.info(` Port: ${process.env.DB_PORT}`);
logger.info(` User: ${process.env.DB_USER}`); logger.info(` User: ${process.env.DB_USER}`);
logger.info(` Database: ${process.env.DB_DATABASE}`); logger.info(` Database: ${process.env.DB_DATABASE}`);
// Query the users table to see what the server process sees on startup.
getPool().query('SELECT id, email, role FROM public.users u JOIN public.profiles p ON u.id = p.id')
.then(res => {
logger.debug('[SERVER PROCESS] Users found in DB on startup:');
console.table(res.rows);
})
.catch(err => {
logger.error('[SERVER PROCESS] Could not query users table on startup.', { err });
});
logger.info('-----------------------------------------------\n'); logger.info('-----------------------------------------------\n');
const app = express(); const app = express();

View File

@@ -177,6 +177,12 @@ async function main() {
} }
logger.info(`Seeded shopping list "Weekly Groceries" with ${shoppingListItems.length} items for Test User.`); logger.info(`Seeded shopping list "Weekly Groceries" with ${shoppingListItems.length} items for Test User.`);
// --- SEED SCRIPT DEBUG LOGGING ---
const allUsersInDb = await client.query('SELECT id, email, role FROM public.users u JOIN public.profiles p ON u.id = p.id');
logger.debug('[SEED SCRIPT] Final state of users table after seeding:');
console.table(allUsersInDb.rows);
// --- END DEBUG LOGGING ---
await client.query('COMMIT'); await client.query('COMMIT');
logger.info('✅ Database seeding completed successfully!'); logger.info('✅ Database seeding completed successfully!');

View File

@@ -92,7 +92,21 @@ router.post('/register', async (req: Request, res: Response, next: NextFunction)
// Login Route // Login Route
router.post('/login', (req: Request, res: Response, next: NextFunction) => { router.post('/login', (req: Request, res: Response, next: NextFunction) => {
passport.authenticate('local', { session: false }, (err: Error, user: Express.User | false, info: { message: string }) => { passport.authenticate('local', { session: false }, async (err: Error, user: Express.User | false, info: { message: string }) => {
// --- LOGIN ROUTE DEBUG LOGGING ---
logger.debug(`[API /login] Received login request for email: ${req.body.email}`);
if (err) logger.error('[API /login] Passport reported an error.', { err });
if (!user) logger.warn('[API /login] Passport reported NO USER found.', { info });
if (user) logger.info('[API /login] Passport reported USER FOUND.', { user });
try {
const allUsersInDb = await db.getPool().query('SELECT id, email, role FROM public.users u JOIN public.profiles p ON u.id = p.id');
logger.debug('[API /login] Current users in DB from SERVER perspective:');
console.table(allUsersInDb.rows);
} catch (dbError) {
logger.error('[API /login] Could not query users table for debugging.', { dbError });
}
// --- END DEBUG LOGGING ---
const { rememberMe } = req.body; const { rememberMe } = req.body;
if (err) { if (err) {
logger.error('Login authentication error in /login route:', { error: err }); logger.error('Login authentication error in /login route:', { error: err });
@@ -133,8 +147,11 @@ router.post('/forgot-password', forgotPasswordLimiter, async (req: Request, res:
} }
try { try {
logger.debug(`[API /forgot-password] Received request for email: ${email}`);
const user = await db.findUserByEmail(email); const user = await db.findUserByEmail(email);
let token: string | undefined; let token: string | undefined;
logger.debug(`[API /forgot-password] Database search result for ${email}:`, { user: user ? { id: user.id, email: user.email } : 'NOT FOUND' });
if (user) { if (user) {
token = crypto.randomBytes(32).toString('hex'); token = crypto.randomBytes(32).toString('hex');
const saltRounds = 10; const saltRounds = 10;

View File

@@ -155,11 +155,15 @@ router.delete('/users/account', async (req: Request, res: Response) => {
return res.status(400).json({ message: 'Password is required for account deletion.' }); return res.status(400).json({ message: 'Password is required for account deletion.' });
} }
logger.info(`Account deletion requested for user ID: ${authenticatedUser.id}`); logger.info(`[API /users/account] Account deletion requested for user ID: ${authenticatedUser.id}`);
try { try {
const userWithHash = await db.findUserWithPasswordHashById(authenticatedUser.id); const userWithHash = await db.findUserWithPasswordHashById(authenticatedUser.id);
// --- DELETE ACCOUNT DEBUG LOGGING ---
logger.debug(`[API /users/account] DB search result for user ID ${authenticatedUser.id}:`, { user: userWithHash ? 'FOUND' : 'NOT FOUND' });
// --- END DEBUG LOGGING ---
if (!userWithHash || !userWithHash.password_hash) { if (!userWithHash || !userWithHash.password_hash) {
return res.status(404).json({ message: 'User not found or is an OAuth user.' }); return res.status(404).json({ message: 'User not found or is an OAuth user.' });
} }

View File

@@ -115,6 +115,13 @@ export const apiFetch = async (url: string, options: RequestInit = {}, tokenOver
} }
} }
// --- DEBUG LOGGING for failed requests ---
if (!response.ok) {
const responseText = await response.clone().text();
logger.error(`apiFetch: Request to ${fullUrl} failed with status ${response.status}. Response body:`, responseText);
}
// --- END DEBUG LOGGING ---
return response; return response;
}; };

View File

@@ -9,6 +9,9 @@ import { logger } from '../logger';
let poolInstance: Pool | undefined; let poolInstance: Pool | undefined;
const createPool = (): Pool => { const createPool = (): Pool => {
// Add a unique ID to each pool instance for definitive tracking in logs.
const poolId = Math.random().toString(36).substring(2, 8);
// --- START DEBUG LOGGING --- // --- START DEBUG LOGGING ---
// Log the database connection details being used by the server process. // Log the database connection details being used by the server process.
// This helps confirm it's using the correct .env file for the environment. // This helps confirm it's using the correct .env file for the environment.
@@ -17,6 +20,7 @@ const createPool = (): Pool => {
logger.info(` Port: ${process.env.DB_PORT}`); logger.info(` Port: ${process.env.DB_PORT}`);
logger.info(` User: ${process.env.DB_USER}`); logger.info(` User: ${process.env.DB_USER}`);
logger.info(` Database: ${process.env.DB_DATABASE}`); logger.info(` Database: ${process.env.DB_DATABASE}`);
logger.info(` Pool Instance ID: ${poolId}`);
logger.info('----------------------------------------------------'); logger.info('----------------------------------------------------');
const newPool = new Pool({ const newPool = new Pool({

View File

@@ -14,9 +14,10 @@ type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'DEBUG';
* methods. Using `unknown[]` is more type-safe than `any[]` and satisfies the linter. * methods. Using `unknown[]` is more type-safe than `any[]` and satisfies the linter.
*/ */
const log = <T extends unknown[]>(level: LogLevel, message: string, ...args: T) => { const log = <T extends unknown[]>(level: LogLevel, message: string, ...args: T) => {
const pid = process.pid;
const timestamp = getTimestamp(); const timestamp = getTimestamp();
// We construct the log message with a timestamp and level for better context. // We construct the log message with a timestamp, PID, and level for better context.
const logMessage = `[${timestamp}] [${level}] ${message}`; const logMessage = `[${timestamp}] [PID:${pid}] [${level}] ${message}`;
switch (level) { switch (level) {
case 'INFO': case 'INFO':

View File

@@ -6,20 +6,20 @@ import { execSync } from 'child_process';
let serverProcess: ChildProcess; let serverProcess: ChildProcess;
export async function setup() { export async function setup() {
console.log('\n--- Running Integration Test Setup ---'); console.log(`\n--- [PID:${process.pid}] Running Integration Test GLOBAL Setup ---`);
// The integration setup is now the single source of truth for preparing the test DB. // The integration setup is now the single source of truth for preparing the test DB.
// It runs the same seed script that `npm run db:reset:test` used. // It runs the same seed script that `npm run db:reset:test` used.
try { try {
console.log('Resetting and seeding test database...'); console.log(`\n[PID:${process.pid}] Running database seed script...`);
execSync('npm run db:reset:test', { stdio: 'inherit' }); execSync('npm run db:reset:test', { stdio: 'inherit' });
console.log('✅ Test database is ready.'); console.log(`✅ [PID:${process.pid}] Database seed script finished.`);
} catch (error) { } catch (error) {
console.error('🔴 Failed to reset and seed the test database. Aborting tests.', error); console.error('🔴 Failed to reset and seed the test database. Aborting tests.', error);
process.exit(1); process.exit(1);
} }
console.log('Starting backend server for integration tests...'); console.log(`\n[PID:${process.pid}] Starting backend server as a separate process...`);
// Use the dedicated test server script, which correctly uses the .env.test file. // Use the dedicated test server script, which correctly uses the .env.test file.
serverProcess = exec('npm run start:test'); serverProcess = exec('npm run start:test');
@@ -27,7 +27,7 @@ export async function setup() {
// --- NEW LOGGING START --- // --- NEW LOGGING START ---
// Listen for premature exit to debug crashes immediately // Listen for premature exit to debug crashes immediately
serverProcess.on('exit', (code, signal) => { serverProcess.on('exit', (code, signal) => {
if (code !== null && code !== 0) { if (code !== 0 && signal !== 'SIGTERM') {
console.error(`\n[SERVER EXIT]: Backend server process exited prematurely with code ${code} and signal ${signal}`); console.error(`\n[SERVER EXIT]: Backend server process exited prematurely with code ${code} and signal ${signal}`);
console.error('Check [SERVER STDERR] logs above for details.\n'); console.error('Check [SERVER STDERR] logs above for details.\n');
} }
@@ -68,7 +68,7 @@ export async function setup() {
throw new Error(`Server process exited with code ${serverProcess.exitCode}`); throw new Error(`Server process exited with code ${serverProcess.exitCode}`);
} }
if (await pingTestBackend()) { if (await pingTestBackend().catch(() => false)) {
console.log('✅ Backend server is running and responsive.'); console.log('✅ Backend server is running and responsive.');
return; return;
} }
@@ -77,7 +77,7 @@ export async function setup() {
if (serverProcess.exitCode !== null) throw e; if (serverProcess.exitCode !== null) throw e;
logger.debug('Ping failed while waiting for server, this is expected.', { error: e }); logger.debug('Ping failed while waiting for server, this is expected.', { error: e });
} }
console.log(`Waiting for backend server... (attempt ${i + 1}/${maxRetries})`); console.log(`[PID:${process.pid}] Waiting for backend server... (attempt ${i + 1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, retryDelay)); await new Promise(resolve => setTimeout(resolve, retryDelay));
} }
@@ -87,7 +87,7 @@ export async function setup() {
} }
export async function teardown() { export async function teardown() {
console.log('--- Integration Test Teardown ---'); console.log(`\n--- [PID:${process.pid}] Running Integration Test GLOBAL Teardown ---`);
// Close the database connection pool after all integration tests have run. // Close the database connection pool after all integration tests have run.
// This is the correct place to ensure all connections are closed gracefully. // This is the correct place to ensure all connections are closed gracefully.
const pool = getPool(); const pool = getPool();

View File