splitting unit + integration testing apart attempt #1
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 3m51s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 3m51s
This commit is contained in:
@@ -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(() => {
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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.
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user