splitting unit + integration testing apart attempt #1
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 3m46s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 3m46s
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
// src/services/db/connection.test.ts
|
// src/services/db/connection.test.ts
|
||||||
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
||||||
import { getPool, checkTablesExist, getPoolStatus } from './connection';
|
import { getPool, checkTablesExist, getPoolStatus } from './connection';
|
||||||
// Import the shared mock instance and constructor directly for assertions.
|
// IMPORTANT: We must import the pool instance from unit-setup via our exposed variable
|
||||||
import { mockPoolInstance, MockPool } from '../../tests/setup/mock-db';
|
// or simply trust the pg mock.
|
||||||
|
import { mockPoolInstance } from '../../tests/setup/unit-setup';
|
||||||
|
|
||||||
// Mock the logger
|
// Mock the logger
|
||||||
vi.mock('../logger', () => ({
|
vi.mock('../logger', () => ({
|
||||||
@@ -15,33 +16,21 @@ vi.mock('../logger', () => ({
|
|||||||
describe('DB Connection Service', () => {
|
describe('DB Connection Service', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
// This is important to reset the singleton for each test
|
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add this temporary debug test
|
|
||||||
it('DEBUG: Verify pg import inside test', async () => {
|
|
||||||
const pg = await import('pg');
|
|
||||||
console.log('[DEBUG] connection.test.ts: Imported pg module:', Object.keys(pg));
|
|
||||||
console.log('[DEBUG] connection.test.ts: pg.Pool is:', typeof pg.Pool);
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
new pg.Pool();
|
|
||||||
console.log('[DEBUG] connection.test.ts: new pg.Pool() succeeded');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[DEBUG] connection.test.ts: new pg.Pool() failed', e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getPool', () => {
|
describe('getPool', () => {
|
||||||
it('should create a new pool instance on the first call', async () => {
|
it('should create a new pool instance on the first call', async () => {
|
||||||
// Dynamically import to get the fresh, un-cached module
|
|
||||||
const { getPool } = await import('./connection');
|
const { getPool } = await import('./connection');
|
||||||
const pool = getPool();
|
const pool = getPool();
|
||||||
|
|
||||||
expect(MockPool).toHaveBeenCalledTimes(1);
|
// We can check if the pool returned matches our global mock instance
|
||||||
expect(pool).toBeDefined();
|
expect(pool).toBeDefined();
|
||||||
|
// It should be the mocked instance from our setup
|
||||||
|
expect(pool).toEqual(expect.objectContaining({
|
||||||
|
connect: expect.any(Function),
|
||||||
|
query: expect.any(Function)
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the same pool instance on subsequent calls', async () => {
|
it('should return the same pool instance on subsequent calls', async () => {
|
||||||
@@ -49,8 +38,6 @@ describe('DB Connection Service', () => {
|
|||||||
const pool1 = getPool();
|
const pool1 = getPool();
|
||||||
const pool2 = getPool();
|
const pool2 = getPool();
|
||||||
|
|
||||||
// The Pool constructor should only be called once because of the singleton pattern.
|
|
||||||
expect(MockPool).toHaveBeenCalledTimes(1);
|
|
||||||
expect(pool1).toBe(pool2);
|
expect(pool1).toBe(pool2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -59,7 +46,8 @@ describe('DB Connection Service', () => {
|
|||||||
it('should return an empty array if all tables exist', async () => {
|
it('should return an empty array if all tables exist', async () => {
|
||||||
const { getPool, checkTablesExist } = await import('./connection');
|
const { getPool, checkTablesExist } = await import('./connection');
|
||||||
const tableNames = ['users', 'flyers'];
|
const tableNames = ['users', 'flyers'];
|
||||||
const mockQuery = (getPool().query as Mocked<typeof mockPoolInstance.query>).mockResolvedValue({ rows: [{ table_name: 'users' }, { table_name: 'flyers' }] } as any);
|
const mockQuery = (getPool().query as any);
|
||||||
|
mockQuery.mockResolvedValue({ rows: [{ table_name: 'users' }, { table_name: 'flyers' }] });
|
||||||
|
|
||||||
const missingTables = await checkTablesExist(tableNames);
|
const missingTables = await checkTablesExist(tableNames);
|
||||||
|
|
||||||
@@ -70,7 +58,8 @@ describe('DB Connection Service', () => {
|
|||||||
it('should return an array of missing tables', async () => {
|
it('should return an array of missing tables', async () => {
|
||||||
const tableNames = ['users', 'flyers', 'products'];
|
const tableNames = ['users', 'flyers', 'products'];
|
||||||
const { getPool, checkTablesExist } = await import('./connection');
|
const { getPool, checkTablesExist } = await import('./connection');
|
||||||
const mockQuery = (getPool().query as Mocked<typeof mockPoolInstance.query>).mockResolvedValue({ rows: [{ table_name: 'users' }] } as any);
|
const mockQuery = (getPool().query as any);
|
||||||
|
mockQuery.mockResolvedValue({ rows: [{ table_name: 'users' }] });
|
||||||
|
|
||||||
const missingTables = await checkTablesExist(tableNames);
|
const missingTables = await checkTablesExist(tableNames);
|
||||||
|
|
||||||
@@ -83,7 +72,6 @@ describe('DB Connection Service', () => {
|
|||||||
const { getPoolStatus } = await import('./connection');
|
const { getPoolStatus } = await import('./connection');
|
||||||
const status = getPoolStatus();
|
const status = getPoolStatus();
|
||||||
|
|
||||||
// Assert against the values defined in the central mock-db.ts file.
|
|
||||||
expect(status).toEqual({
|
expect(status).toEqual({
|
||||||
totalCount: 10,
|
totalCount: 10,
|
||||||
idleCount: 5,
|
idleCount: 5,
|
||||||
|
|||||||
@@ -38,12 +38,18 @@ vi.mock('./personalization', () => ({
|
|||||||
|
|
||||||
describe('User DB Service', () => {
|
describe('User DB Service', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Configure the globally mocked Pool to use our test-local mock functions.
|
// FIX: mockReturnValue creates an arrow function which cannot be 'new'ed.
|
||||||
vi.mocked(Pool).mockReturnValue({
|
// We use mockImplementation with a standard function instead.
|
||||||
query: mockQuery,
|
vi.mocked(Pool).mockImplementation(function() {
|
||||||
connect: mockConnect,
|
console.log('[DEBUG] user.test.ts: Local Pool mock instantiated');
|
||||||
release: mockRelease,
|
return {
|
||||||
} as any);
|
query: mockQuery,
|
||||||
|
connect: mockConnect,
|
||||||
|
release: mockRelease,
|
||||||
|
on: vi.fn(),
|
||||||
|
end: vi.fn(),
|
||||||
|
} as any;
|
||||||
|
});
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ export const mockPoolInstance = {
|
|||||||
waitingCount: 0,
|
waitingCount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIX: Use a standard function for the constructor.
|
// FIX: Use a standard function for the constructor here too.
|
||||||
// An arrow function `() => {}` cannot be called with `new`, which was causing the
|
|
||||||
// "is not a constructor" TypeError across all database tests.
|
|
||||||
export const MockPool = vi.fn(function() {
|
export const MockPool = vi.fn(function() {
|
||||||
return mockPoolInstance;
|
return mockPoolInstance;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,12 +51,9 @@ afterEach(cleanup);
|
|||||||
// By placing mocks here, they are guaranteed to be hoisted and applied
|
// By placing mocks here, they are guaranteed to be hoisted and applied
|
||||||
// before any test files are executed, preventing initialization errors.
|
// before any test files are executed, preventing initialization errors.
|
||||||
|
|
||||||
// --- Global Mocks ---
|
// 1. Hoist ONLY the instance data, NOT the constructor function.
|
||||||
|
// This prevents the function from being transpiled into an arrow function during hoisting.
|
||||||
// 1. Define the mock pool instance and constructor *outside* vi.mock first
|
const { mockPoolInstance } = vi.hoisted(() => {
|
||||||
// We use vi.hoisted to ensure these variables are available to the mock factory
|
|
||||||
const { mockPoolInstance, MockPool } = vi.hoisted(() => {
|
|
||||||
console.log('[DEBUG] unit-setup.ts: Initializing hoisted mock variables');
|
|
||||||
const mockQuery = vi.fn().mockResolvedValue({ rows: [], rowCount: 0 });
|
const mockQuery = vi.fn().mockResolvedValue({ rows: [], rowCount: 0 });
|
||||||
const mockRelease = vi.fn();
|
const mockRelease = vi.fn();
|
||||||
const mockConnect = vi.fn().mockResolvedValue({
|
const mockConnect = vi.fn().mockResolvedValue({
|
||||||
@@ -74,33 +71,34 @@ const { mockPoolInstance, MockPool } = vi.hoisted(() => {
|
|||||||
waitingCount: 0,
|
waitingCount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The constructor function
|
return { mockPoolInstance: instance };
|
||||||
// FIX: Use a standard function so 'new' works, and log success
|
|
||||||
const Constructor = vi.fn(function() {
|
|
||||||
console.log('[DEBUG] unit-setup.ts: MockPool constructor called successfully via "new"!');
|
|
||||||
return instance;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { mockPoolInstance: instance, MockPool: Constructor };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Expose the mock instance globally so it can be imported by tests if needed,
|
||||||
|
// though typically they should import from 'pg' directly.
|
||||||
|
export { mockPoolInstance };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mocks the `pg` module globally for all unit tests using the centralized factory.
|
* Mocks the `pg` module globally.
|
||||||
|
* We define the MockPool function INSIDE the factory to ensure it remains a standard function.
|
||||||
*/
|
*/
|
||||||
// 2. Mock 'pg' using the hoisted variables
|
|
||||||
vi.mock('pg', () => {
|
vi.mock('pg', () => {
|
||||||
console.log('[DEBUG] unit-setup.ts: vi.mock("pg") factory executing');
|
// Define as a standard function expression to ensure 'new' capability.
|
||||||
|
// We attach it to a variable first for clarity.
|
||||||
|
const MockPoolConstructor = vi.fn(function() {
|
||||||
|
console.log('[DEBUG] unit-setup.ts: MockPool constructor called via "new"!');
|
||||||
|
return mockPoolInstance;
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Named export must be the constructor
|
Pool: MockPoolConstructor,
|
||||||
Pool: MockPool,
|
default: { Pool: MockPoolConstructor },
|
||||||
// Default export often contains Pool as a property
|
|
||||||
default: { Pool: MockPool },
|
|
||||||
types: { setTypeParser: vi.fn() },
|
types: { setTypeParser: vi.fn() },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mocks the Google Generative AI package. This prevents real API calls during tests.
|
* Mocks the Google Generative AI package.
|
||||||
*/
|
*/
|
||||||
vi.mock('@google/generative-ai', () => {
|
vi.mock('@google/generative-ai', () => {
|
||||||
const mockGenerativeModel = {
|
const mockGenerativeModel = {
|
||||||
|
|||||||
Reference in New Issue
Block a user