splitting unit + integration testing apart attempt #1
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 3m51s

This commit is contained in:
2025-11-30 17:39:19 -08:00
parent f6e7ea7e97
commit bcad064a1a
5 changed files with 41 additions and 37 deletions

View File

@@ -56,7 +56,7 @@ vi.mock('./passport', () => ({
const app = express(); const app = express();
app.use(express.json({ strict: false })); app.use(express.json({ strict: false }));
app.use(cookieParser()); // Add cookie-parser middleware to populate req.cookies app.use(cookieParser()); // Add cookie-parser middleware to populate req.cookies
app.use('/api/auth', authRouter); // This was missing app.use('/api/auth', authRouter);
describe('Auth Routes (/api/auth)', () => { describe('Auth Routes (/api/auth)', () => {
beforeEach(() => { beforeEach(() => {

View File

@@ -33,7 +33,7 @@ vi.mock('../services/logger.server', () => ({
const app = express(); const app = express();
app.use(express.json({ strict: false })); app.use(express.json({ strict: false }));
// Mount the router under a base path, similar to how it's done in the main server // Mount the router under a base path, similar to how it's done in the main server
app.use('/api', publicRouter); app.use('/api', publicRouter); // This was correct, no change needed. Re-evaluating.
describe('Public Routes (/api)', () => { describe('Public Routes (/api)', () => {
beforeEach(() => { beforeEach(() => {

View File

@@ -17,13 +17,20 @@ import {
updateRecipeStatus, updateRecipeStatus,
updateReceiptStatus, updateReceiptStatus,
} from './admin'; } from './admin';
import { Pool } from 'pg'; import { getPool } from './connection';
const mockQuery = vi.fn();
const mockRelease = vi.fn();
const mockConnect = vi.fn().mockResolvedValue({ query: mockQuery, release: mockRelease });
import type { SuggestedCorrection } from '../../types'; import type { SuggestedCorrection } from '../../types';
// Define test-local mock functions. These will be used to control the mock's behavior.
const mockQuery = vi.fn();
// Mock the entire connection module.
vi.mock('./connection', () => ({
// The mock factory for getPool returns an object that uses our test-local mockQuery.
getPool: () => ({
query: mockQuery,
}),
}));
// Mock the logger to prevent console output during tests // Mock the logger to prevent console output during tests
vi.mock('../logger', () => ({ vi.mock('../logger', () => ({
logger: { logger: {
@@ -36,12 +43,10 @@ vi.mock('../logger', () => ({
describe('Admin DB Service', () => { describe('Admin DB Service', () => {
beforeEach(() => { beforeEach(() => {
// Configure the globally mocked Pool to use our test-local mock functions. // --- DIAGNOSTIC LOGGING #3: "What Does My Test Think It's Importing?" ---
vi.mocked(Pool).mockReturnValue({ // This log will show that getPool().query is indeed our mock function.
query: mockQuery, console.log('[admin.test.ts] Is getPool().query a mock function?', vi.isMockFunction(getPool().query));
connect: mockConnect,
release: mockRelease,
} as any);
// Clear mock history before each test // Clear mock history before each test
vi.clearAllMocks(); vi.clearAllMocks();
}); });
@@ -55,7 +60,7 @@ describe('Admin DB Service', () => {
const result = await getSuggestedCorrections(); const result = await getSuggestedCorrections();
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining("FROM public.suggested_corrections sc")); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining("FROM public.suggested_corrections sc"));
expect(result).toEqual(mockCorrections); expect(result).toEqual(mockCorrections);
}); });
}); });
@@ -65,7 +70,7 @@ describe('Admin DB Service', () => {
mockQuery.mockResolvedValue({ rows: [] }); // Mock the function call mockQuery.mockResolvedValue({ rows: [] }); // Mock the function call
await approveCorrection(123); await approveCorrection(123);
expect(mockQuery).toHaveBeenCalledWith('SELECT public.approve_correction($1)', [123]); expect(getPool().query).toHaveBeenCalledWith('SELECT public.approve_correction($1)', [123]);
}); });
}); });
@@ -74,7 +79,7 @@ describe('Admin DB Service', () => {
mockQuery.mockResolvedValue({ rowCount: 1 }); mockQuery.mockResolvedValue({ rowCount: 1 });
await rejectCorrection(123); await rejectCorrection(123);
expect(mockQuery).toHaveBeenCalledWith( expect(getPool().query).toHaveBeenCalledWith(
expect.stringContaining("UPDATE public.suggested_corrections SET status = 'rejected'"), expect.stringContaining("UPDATE public.suggested_corrections SET status = 'rejected'"),
[123] [123]
); );
@@ -88,7 +93,7 @@ describe('Admin DB Service', () => {
const result = await updateSuggestedCorrection(1, '300'); const result = await updateSuggestedCorrection(1, '300');
expect(mockQuery).toHaveBeenCalledWith( expect(getPool().query).toHaveBeenCalledWith(
expect.stringContaining("UPDATE public.suggested_corrections SET suggested_value = $1"), expect.stringContaining("UPDATE public.suggested_corrections SET suggested_value = $1"),
['300', 1] ['300', 1]
); );
@@ -108,7 +113,7 @@ describe('Admin DB Service', () => {
const stats = await getApplicationStats(); const stats = await getApplicationStats();
expect(mockQuery).toHaveBeenCalledTimes(5); expect(getPool().query).toHaveBeenCalledTimes(5);
expect(stats).toEqual({ expect(stats).toEqual({
flyerCount: 10, flyerCount: 10,
userCount: 20, userCount: 20,
@@ -126,7 +131,7 @@ describe('Admin DB Service', () => {
const result = await getDailyStatsForLast30Days(); const result = await getDailyStatsForLast30Days();
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining("WITH date_series AS")); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining("WITH date_series AS"));
expect(result).toEqual(mockStats); expect(result).toEqual(mockStats);
}); });
}); });
@@ -137,7 +142,7 @@ describe('Admin DB Service', () => {
const logData = { userId: 'user-123', action: 'test_action', displayText: 'Test activity' }; const logData = { userId: 'user-123', action: 'test_action', displayText: 'Test activity' };
await logActivity(logData); await logActivity(logData);
expect(mockQuery).toHaveBeenCalledWith( expect(getPool().query).toHaveBeenCalledWith(
expect.stringContaining("INSERT INTO public.activity_log"), expect.stringContaining("INSERT INTO public.activity_log"),
[logData.userId, logData.action, logData.displayText, null, null] [logData.userId, logData.action, logData.displayText, null, null]
); );
@@ -148,7 +153,7 @@ describe('Admin DB Service', () => {
it('should call the correct database function', async () => { it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] }); mockQuery.mockResolvedValue({ rows: [] });
await getMostFrequentSaleItems(30, 10); await getMostFrequentSaleItems(30, 10);
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('FROM public.flyer_items fi'), [30, 10]); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('FROM public.flyer_items fi'), [30, 10]);
}); });
}); });
@@ -157,7 +162,7 @@ describe('Admin DB Service', () => {
const mockComment = { comment_id: 1, status: 'hidden' }; const mockComment = { comment_id: 1, status: 'hidden' };
mockQuery.mockResolvedValue({ rows: [mockComment], rowCount: 1 }); mockQuery.mockResolvedValue({ rows: [mockComment], rowCount: 1 });
const result = await updateRecipeCommentStatus(1, 'hidden'); const result = await updateRecipeCommentStatus(1, 'hidden');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.recipe_comments'), ['hidden', 1]); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.recipe_comments'), ['hidden', 1]);
expect(result).toEqual(mockComment); expect(result).toEqual(mockComment);
}); });
}); });
@@ -166,7 +171,7 @@ describe('Admin DB Service', () => {
it('should execute the correct query to get unmatched items', async () => { it('should execute the correct query to get unmatched items', async () => {
mockQuery.mockResolvedValue({ rows: [] }); mockQuery.mockResolvedValue({ rows: [] });
await getUnmatchedFlyerItems(); await getUnmatchedFlyerItems();
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('FROM public.unmatched_flyer_items ufi')); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('FROM public.unmatched_flyer_items ufi'));
}); });
}); });
@@ -175,7 +180,7 @@ describe('Admin DB Service', () => {
const mockRecipe = { recipe_id: 1, status: 'public' }; const mockRecipe = { recipe_id: 1, status: 'public' };
mockQuery.mockResolvedValue({ rows: [mockRecipe], rowCount: 1 }); mockQuery.mockResolvedValue({ rows: [mockRecipe], rowCount: 1 });
const result = await updateRecipeStatus(1, 'public'); const result = await updateRecipeStatus(1, 'public');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.recipes'), ['public', 1]); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.recipes'), ['public', 1]);
expect(result).toEqual(mockRecipe); expect(result).toEqual(mockRecipe);
}); });
}); });
@@ -184,7 +189,7 @@ describe('Admin DB Service', () => {
it('should execute an UPDATE query to increment failed attempts', async () => { it('should execute an UPDATE query to increment failed attempts', async () => {
mockQuery.mockResolvedValue({ rows: [] }); mockQuery.mockResolvedValue({ rows: [] });
await incrementFailedLoginAttempts('user-123'); await incrementFailedLoginAttempts('user-123');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.users SET failed_login_attempts'), ['user-123']); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.users SET failed_login_attempts'), ['user-123']);
}); });
}); });
@@ -192,7 +197,7 @@ describe('Admin DB Service', () => {
it('should execute an UPDATE query to reset failed attempts', async () => { it('should execute an UPDATE query to reset failed attempts', async () => {
mockQuery.mockResolvedValue({ rows: [] }); mockQuery.mockResolvedValue({ rows: [] });
await resetFailedLoginAttempts('user-123'); await resetFailedLoginAttempts('user-123');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.users SET failed_login_attempts = 0'), ['user-123']); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.users SET failed_login_attempts = 0'), ['user-123']);
}); });
}); });
@@ -200,7 +205,7 @@ describe('Admin DB Service', () => {
it('should execute an UPDATE query for the brand logo', async () => { it('should execute an UPDATE query for the brand logo', async () => {
mockQuery.mockResolvedValue({ rows: [] }); mockQuery.mockResolvedValue({ rows: [] });
await updateBrandLogo(1, '/logo.png'); await updateBrandLogo(1, '/logo.png');
expect(mockQuery).toHaveBeenCalledWith('UPDATE public.brands SET logo_url = $1 WHERE brand_id = $2', ['/logo.png', 1]); expect(getPool().query).toHaveBeenCalledWith('UPDATE public.brands SET logo_url = $1 WHERE brand_id = $2', ['/logo.png', 1]);
}); });
}); });
@@ -209,7 +214,7 @@ describe('Admin DB Service', () => {
const mockReceipt = { receipt_id: 1, status: 'completed' }; const mockReceipt = { receipt_id: 1, status: 'completed' };
mockQuery.mockResolvedValue({ rows: [mockReceipt], rowCount: 1 }); mockQuery.mockResolvedValue({ rows: [mockReceipt], rowCount: 1 });
const result = await updateReceiptStatus(1, 'completed'); const result = await updateReceiptStatus(1, 'completed');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.receipts'), ['completed', 1]); expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.receipts'), ['completed', 1]);
expect(result).toEqual(mockReceipt); expect(result).toEqual(mockReceipt);
}); });
}); });

View File

@@ -50,10 +50,10 @@ const createPool = (): Pool => {
export const getPool = (): Pool => { export const getPool = (): Pool => {
// This function acts as a singleton accessor for the database pool. // This function acts as a singleton accessor for the database pool.
if (!poolInstance) { if (!poolInstance) {
//logger.info('--- [DB POOL ACCESSOR] No pool instance found. Creating a new one. ---'); console.log('[DB POOL] Creating NEW pool instance.'); // DIAGNOSTIC LOGGING #2
poolInstance = createPool(); poolInstance = createPool();
} else { } else {
//logger.info(`--- [DB POOL ACCESSOR] Returning existing pool instance. ID: ${poolInstance.poolId} ---`); console.log(`[DB POOL] Returning EXISTING pool instance. ID: ${poolInstance.poolId}`); // DIAGNOSTIC LOGGING #2
} }
return poolInstance; return poolInstance;
}; };

View File

@@ -56,8 +56,8 @@ afterEach(cleanup);
* This is the central point for mocking the database connection pool for unit tests. * This is the central point for mocking the database connection pool for unit tests.
*/ */
vi.mock('pg', () => { vi.mock('pg', () => {
// --- DEBUG LOGGING --- // --- DIAGNOSTIC LOGGING #1: "Is the Mock Even Loading?" ---
// This log will appear in the test output to prove that this global mock is being used. // This log will appear once at the start of the test run to prove that this global mock is being used.
console.log('[vitest-mock-pg] Global `pg` mock from unit-setup.ts is being initialized.'); console.log('[vitest-mock-pg] Global `pg` mock from unit-setup.ts is being initialized.');
// These are the mock functions that will replace the real 'pg' methods. // These are the mock functions that will replace the real 'pg' methods.
@@ -78,14 +78,13 @@ vi.mock('pg', () => {
end: vi.fn(), end: vi.fn(),
})); }));
// The object returned by this factory function becomes the exports of the 'pg' module // The object returned here becomes the exports of the 'pg' module for all unit tests.
// for all unit tests.
return { return {
__esModule: true, // This is important for ES module interoperability. __esModule: true, // This is important for ES module interoperability.
Pool: mockPool, Pool: mockPool,
// We are intentionally NOT exporting the mock functions here anymore. // We are intentionally NOT exporting the mock functions. This was the source of the
// This was the source of the TypeScript errors. Test files will now define // TypeScript errors. Test files will now mock the `getPool` function from the
// their own mock functions and configure the mocked Pool to use them. // connection service directly.
}; };
}); });