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();
app.use(express.json({ strict: false }));
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)', () => {
beforeEach(() => {

View File

@@ -33,7 +33,7 @@ vi.mock('../services/logger.server', () => ({
const app = express();
app.use(express.json({ strict: false }));
// 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)', () => {
beforeEach(() => {

View File

@@ -17,13 +17,20 @@ import {
updateRecipeStatus,
updateReceiptStatus,
} from './admin';
import { Pool } from 'pg';
const mockQuery = vi.fn();
const mockRelease = vi.fn();
const mockConnect = vi.fn().mockResolvedValue({ query: mockQuery, release: mockRelease });
import { getPool } from './connection';
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
vi.mock('../logger', () => ({
logger: {
@@ -36,12 +43,10 @@ vi.mock('../logger', () => ({
describe('Admin DB Service', () => {
beforeEach(() => {
// Configure the globally mocked Pool to use our test-local mock functions.
vi.mocked(Pool).mockReturnValue({
query: mockQuery,
connect: mockConnect,
release: mockRelease,
} as any);
// --- DIAGNOSTIC LOGGING #3: "What Does My Test Think It's Importing?" ---
// This log will show that getPool().query is indeed our mock function.
console.log('[admin.test.ts] Is getPool().query a mock function?', vi.isMockFunction(getPool().query));
// Clear mock history before each test
vi.clearAllMocks();
});
@@ -55,7 +60,7 @@ describe('Admin DB Service', () => {
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);
});
});
@@ -65,7 +70,7 @@ describe('Admin DB Service', () => {
mockQuery.mockResolvedValue({ rows: [] }); // Mock the function call
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 });
await rejectCorrection(123);
expect(mockQuery).toHaveBeenCalledWith(
expect(getPool().query).toHaveBeenCalledWith(
expect.stringContaining("UPDATE public.suggested_corrections SET status = 'rejected'"),
[123]
);
@@ -88,7 +93,7 @@ describe('Admin DB Service', () => {
const result = await updateSuggestedCorrection(1, '300');
expect(mockQuery).toHaveBeenCalledWith(
expect(getPool().query).toHaveBeenCalledWith(
expect.stringContaining("UPDATE public.suggested_corrections SET suggested_value = $1"),
['300', 1]
);
@@ -108,7 +113,7 @@ describe('Admin DB Service', () => {
const stats = await getApplicationStats();
expect(mockQuery).toHaveBeenCalledTimes(5);
expect(getPool().query).toHaveBeenCalledTimes(5);
expect(stats).toEqual({
flyerCount: 10,
userCount: 20,
@@ -126,7 +131,7 @@ describe('Admin DB Service', () => {
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);
});
});
@@ -137,7 +142,7 @@ describe('Admin DB Service', () => {
const logData = { userId: 'user-123', action: 'test_action', displayText: 'Test activity' };
await logActivity(logData);
expect(mockQuery).toHaveBeenCalledWith(
expect(getPool().query).toHaveBeenCalledWith(
expect.stringContaining("INSERT INTO public.activity_log"),
[logData.userId, logData.action, logData.displayText, null, null]
);
@@ -148,7 +153,7 @@ describe('Admin DB Service', () => {
it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
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' };
mockQuery.mockResolvedValue({ rows: [mockComment], rowCount: 1 });
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);
});
});
@@ -166,7 +171,7 @@ describe('Admin DB Service', () => {
it('should execute the correct query to get unmatched items', async () => {
mockQuery.mockResolvedValue({ rows: [] });
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' };
mockQuery.mockResolvedValue({ rows: [mockRecipe], rowCount: 1 });
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);
});
});
@@ -184,7 +189,7 @@ describe('Admin DB Service', () => {
it('should execute an UPDATE query to increment failed attempts', async () => {
mockQuery.mockResolvedValue({ rows: [] });
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 () => {
mockQuery.mockResolvedValue({ rows: [] });
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 () => {
mockQuery.mockResolvedValue({ rows: [] });
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' };
mockQuery.mockResolvedValue({ rows: [mockReceipt], rowCount: 1 });
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);
});
});

View File

@@ -50,10 +50,10 @@ const createPool = (): Pool => {
export const getPool = (): Pool => {
// This function acts as a singleton accessor for the database pool.
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();
} 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;
};

View File

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