Add user database service and unit tests
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m28s

- Implement user database service with functions for user management (create, find, update, delete).
- Add comprehensive unit tests for user database service using Vitest.
- Mock database interactions to ensure isolated testing.
- Create setup files for unit tests to handle database connections and global mocks.
- Introduce error handling for unique constraints and foreign key violations.
- Enhance logging for better traceability during database operations.
This commit is contained in:
2025-12-04 15:30:27 -08:00
parent 4cf587c8f0
commit 80d2b1ffe6
62 changed files with 135 additions and 136 deletions

View File

@@ -12,7 +12,7 @@ vi.mock('../../services/logger', () => ({
}));
// Cast the mocked module to its mocked type to retain type safety and autocompletion.
// The aiApiClient is now mocked globally via src/tests/setup/unit-setup.ts.
// The aiApiClient is now mocked globally via src/tests/setup/tests-setup-unit.ts.
const mockedAiApiClient = aiApiClient as Mocked<typeof aiApiClient>;
const mockFlyerItems: FlyerItem[] = [

View File

@@ -6,7 +6,7 @@ import { ActivityLog } from './ActivityLog';
import * as apiClient from '../../services/apiClient';
import type { ActivityLogItem, User } from '../../types';
// The apiClient and logger are now mocked globally via src/tests/setup/unit-setup.ts.
// The apiClient and logger are now mocked globally via src/tests/setup/tests-setup-unit.ts.
// We can cast it to its mocked type to get type safety and autocompletion.
const mockedApiClient = vi.mocked(apiClient);

View File

@@ -7,7 +7,7 @@ import { AdminStatsPage } from './AdminStatsPage';
import * as apiClient from '../../services/apiClient';
import type { AppStats } from '../../services/apiClient';
// The apiClient and logger are now mocked globally via src/tests/setup/unit-setup.ts.
// The apiClient and logger are now mocked globally via src/tests/setup/tests-setup-unit.ts.
const mockedApiClient = vi.mocked(apiClient);
// Helper function to render the component within a router context, as it contains a <Link>

View File

@@ -7,7 +7,7 @@ import { CorrectionsPage } from './CorrectionsPage';
import * as apiClient from '../../services/apiClient';
import type { SuggestedCorrection, MasterGroceryItem, Category } from '../../types';
// The apiClient and logger are now mocked globally via src/tests/setup/unit-setup.ts.
// The apiClient and logger are now mocked globally via src/tests/setup/tests-setup-unit.ts.
const mockedApiClient = vi.mocked(apiClient);
// Mock the child CorrectionRow component to isolate the test to the page itself

View File

@@ -9,7 +9,7 @@ import type { Brand } from '../../../types';
// After mocking, we can get a type-safe mocked version of the module.
// This allows us to use .mockResolvedValue, .mockRejectedValue, etc. on the functions.
// The apiClient is now mocked globally via src/tests/setup/unit-setup.ts.
// The apiClient is now mocked globally via src/tests/setup/tests-setup-unit.ts.
const mockedApiClient = vi.mocked(apiClient);
const mockedToast = vi.mocked(toast, true);

View File

@@ -8,7 +8,7 @@ import * as apiClient from '../../../services/apiClient';
import type { SuggestedCorrection, MasterGroceryItem, Category } from '../../../types';
// Cast the mocked module to its mocked type to retain type safety and autocompletion.
// The apiClient is now mocked globally via src/tests/setup/unit-setup.ts.
// The apiClient is now mocked globally via src/tests/setup/tests-setup-unit.ts.
const mockedApiClient = apiClient as Mocked<typeof apiClient>;
// Mock the logger

View File

@@ -6,7 +6,7 @@ import { SystemCheck } from './SystemCheck';
import * as apiClient from '../../../services/apiClient';
// Get a type-safe mocked version of the apiClient module.
// The apiClient is now mocked globally via src/tests/setup/unit-setup.ts.
// The apiClient is now mocked globally via src/tests/setup/tests-setup-unit.ts.
// We can cast it to its mocked type to get type safety and autocompletion.
const mockedApiClient = vi.mocked(apiClient);

View File

@@ -5,7 +5,7 @@ import express, { Request, Response, NextFunction } from 'express';
import path from 'node:path';
import fs from 'node:fs/promises';
import adminRouter from './admin'; // Correctly imported
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { UserProfile } from '../types';
// Mock the entire db service

View File

@@ -1,10 +1,10 @@
// src/routes/admin.ts
// src/routes/admin.db.ts
import { Router, NextFunction } from 'express';
import passport from './passport';
import { isAdmin } from './passport'; // Correctly imported
import multer from 'multer';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { logger } from '../services/logger.server';
import { UserProfile } from '../types';
import { clearGeocodeCache } from '../services/geocodingService.server';

View File

@@ -5,7 +5,7 @@ import express, { type Request, type Response, type NextFunction } from 'express
import path from 'node:path';
import aiRouter from './ai';
import { UserProfile } from '../types';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
// Mock the AI service to avoid making real AI calls
vi.mock('../services/aiService.server');

View File

@@ -5,7 +5,7 @@ import path from 'path';
import fs from 'fs';
import passport from './passport';
import { optionalAuth } from './passport';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import * as aiService from '../services/aiService.server'; // Correctly import server-side AI service
import { generateFlyerIcon } from '../utils/imageProcessor';
import { logger } from '../services/logger.server';

View File

@@ -5,7 +5,7 @@ import express, { Request } from 'express';
import cookieParser from 'cookie-parser';
import * as bcrypt from 'bcrypt';
import authRouter from './auth';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { UserProfile } from '../types';
// 1. Mock the Service Layer directly.

View File

@@ -7,8 +7,8 @@ import crypto from 'crypto';
import rateLimit from 'express-rate-limit';
import passport from './passport';
import * as db from '../services/db';
import { getPool } from '../services/db/connection';
import * as db from '../services/db/index.db';
import { getPool } from '../services/db/connection.db';
import { logger } from '../services/logger.server';
import { sendPasswordResetEmail } from '../services/emailService.server';

View File

@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import express, { Request, Response, NextFunction } from 'express';
import budgetRouter from './budget';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { UserProfile, Budget, SpendingByCategory } from '../types';
// 1. Mock the Service Layer directly.

View File

@@ -7,7 +7,7 @@ import {
updateBudget,
deleteBudget,
getSpendingByCategory,
} from '../services/db';
} from '../services/db/index.db';
import { logger } from '../services/logger.server';
import { UserProfile } from '../types';

View File

@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
import supertest from 'supertest';
import express, { Request, Response, NextFunction } from 'express';
import gamificationRouter from './gamification';
import * as db from '../services/db/index';
import * as db from '../services/db/index.db';
import { UserProfile, Achievement, UserAchievement } from '../types';
// Mock the entire db service

View File

@@ -1,7 +1,7 @@
// src/routes/gamification.ts
import express, { NextFunction } from 'express';
import passport, { isAdmin } from './passport';
import { getAllAchievements, getUserAchievements, awardAchievement, getLeaderboard } from '../services/db';
import { getAllAchievements, getUserAchievements, awardAchievement, getLeaderboard } from '../services/db/index.db';
import { logger } from '../services/logger.server';
import { UserProfile } from '../types';

View File

@@ -28,7 +28,7 @@ vi.mock('passport-jwt', () => ({
ExtractJwt: { fromAuthHeaderAsBearerToken: vi.fn() },
}));
import * as db from '../services/db';
import * as db from '../services/db/index.db';
// Mock dependencies before importing the passport configuration
vi.mock('../services/db');

View File

@@ -7,7 +7,7 @@ import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';
import * as bcrypt from 'bcrypt';
import { Request, Response, NextFunction } from 'express';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { logger } from '../services/logger.server';
import { UserProfile } from '../types';
import { omit } from '../utils/objectUtils';

View File

@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import express from 'express';
import publicRouter from './public'; // Import the router we want to test
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import * as fs from 'fs/promises';
import { Flyer, Recipe } from '../types';

View File

@@ -1,6 +1,6 @@
// src/routes/public.ts
import { Router, Request, Response, NextFunction } from 'express';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { logger } from '../services/logger.server';
import fs from 'fs/promises';

View File

@@ -4,7 +4,7 @@ import express from 'express';
// Use * as bcrypt to match the implementation's import style and ensure mocks align.
import * as bcrypt from 'bcrypt';
import userRouter from './user';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { UserProfile, MasterGroceryItem, ShoppingList, ShoppingListItem, Appliance } from '../types';
// 1. Mock the Service Layer directly.

View File

@@ -6,7 +6,7 @@ import path from 'path';
import fs from 'fs/promises';
import * as bcrypt from 'bcrypt';
import zxcvbn from 'zxcvbn';
import * as db from '../services/db';
import * as db from '../services/db/index.db';
import { logger } from '../services/logger.server';
import { User, UserProfile, Address } from '../types';

View File

@@ -1,12 +1,12 @@
// src/services/backgroundJobService.test.ts
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
import cron from 'node-cron';
import * as db from './db';
import * as db from './db/index.db';
import { logger } from './logger.server';
import { sendDealNotificationEmail } from './emailService.server';
import { runDailyDealCheck, startBackgroundJobs } from './backgroundJobService';
import { WatchedItemDeal } from '../types';
import { AdminUserView } from './db/admin';
import { AdminUserView } from './db/admin.db';
// Mock dependencies
vi.mock('node-cron');

View File

@@ -1,6 +1,6 @@
// src/services/backgroundJobService.ts
import cron from 'node-cron';
import * as db from './db';
import * as db from './db/index.db';
import { logger } from './logger.server';
import { analyticsQueue } from './queueService.server';
import { sendDealNotificationEmail } from './emailService.server';

View File

@@ -1,5 +1,5 @@
// src/services/db/address.ts
import { getPool } from './connection';
// src/services/db/address.db.ts
import { getPool } from './connection.db';
import { logger } from '../logger.server';
import { Address } from '../../types';

View File

@@ -1,4 +1,4 @@
// src/services/db/admin.test.ts
// src/services/db/admin.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getSuggestedCorrections,
@@ -15,8 +15,8 @@ import {
getUnmatchedFlyerItems,
updateRecipeStatus,
updateReceiptStatus,
} from './admin';
import { getPool } from './connection';
} from './admin.db';
import { getPool } from './connection.db';
import type { SuggestedCorrection } from '../../types';
// Define test-local mock functions. These will be used to control the mock's behavior.

View File

@@ -1,6 +1,6 @@
// src/services/db/admin.ts
import { getPool } from './connection';
import { ForeignKeyConstraintError } from './errors';
// src/services/db/admin.db.ts
import { getPool } from './connection.db';
import { ForeignKeyConstraintError } from './errors.db';
import { logger } from '../logger';
import { SuggestedCorrection, MostFrequentSaleItem, Recipe, RecipeComment, UnmatchedFlyerItem, ActivityLogItem, Receipt, User } from '../../types';
/**

View File

@@ -1,4 +1,4 @@
// src/services/db/budget.test.ts
// src/services/db/budget.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getBudgetsForUser,
@@ -6,7 +6,7 @@ import {
updateBudget,
deleteBudget,
getSpendingByCategory,
} from './budget';
} from './budget.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();

View File

@@ -1,6 +1,6 @@
// src/services/db/budget.ts
import { getPool } from './connection';
import { ForeignKeyConstraintError } from './errors';
// src/services/db/budget.db.ts
import { getPool } from './connection.db';
import { ForeignKeyConstraintError } from './errors.db';
import { logger } from '../logger';
import { Budget, SpendingByCategory } from '../../types';

View File

@@ -1,4 +1,4 @@
// src/services/db/connection.test.ts
// src/services/db/connection.db.test.ts
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
// Mock the logger
@@ -12,37 +12,37 @@ vi.mock('../logger', () => ({
describe('DB Connection Service', () => {
beforeEach(async () => {
vi.clearAllMocks();
// We reset modules to ensure 'connection.ts' re-evaluates and calls the (mocked) Pool constructor again
// We reset modules to ensure 'connection.db.ts' re-evaluates and calls the (mocked) Pool constructor again
vi.resetModules();
});
// --- DIAGNOSTIC TEST ---
it('DEBUG: Verify pg import and Pool constructor', async () => {
const pg = await import('pg');
console.log('[DEBUG] connection.test.ts: keys(pg):', Object.keys(pg));
console.log('[DEBUG] connection.test.ts: typeof pg.Pool:', typeof pg.Pool);
console.log('[DEBUG] connection.db.test.ts: keys(pg):', Object.keys(pg));
console.log('[DEBUG] connection.db.test.ts: typeof pg.Pool:', typeof pg.Pool);
if (typeof pg.Pool === 'function') {
try {
const instance = new pg.Pool();
console.log('[DEBUG] connection.test.ts: new pg.Pool() SUCCESS. Instance:', Object.keys(instance));
console.log('[DEBUG] connection.db.test.ts: new pg.Pool() SUCCESS. Instance:', Object.keys(instance));
} catch (e) {
console.error('[DEBUG] connection.test.ts: new pg.Pool() FAILED:', e);
console.error('[DEBUG] connection.db.test.ts: new pg.Pool() FAILED:', e);
}
} else {
console.error('[DEBUG] connection.test.ts: pg.Pool is NOT a function!');
console.error('[DEBUG] connection.db.test.ts: pg.Pool is NOT a function!');
}
});
describe('getPool', () => {
it('should create a new pool instance on the first call', async () => {
// Re-import to ensure fresh state after resetModules
const { getPool } = await import('./connection');
const { getPool } = await import('./connection.db');
const { Pool } = await import('pg'); // Get the current mock
const pool = getPool();
console.log('[DEBUG] connection.test.ts: getPool() returned:', pool ? 'Object' : pool);
console.log('[DEBUG] connection.db.test.ts: getPool() returned:', pool ? 'Object' : pool);
// Verify Pool constructor was called
expect(Pool).toHaveBeenCalledTimes(1);
@@ -53,7 +53,7 @@ describe('DB Connection Service', () => {
});
it('should return the same pool instance on subsequent calls', async () => {
const { getPool } = await import('./connection');
const { getPool } = await import('./connection.db');
const { Pool } = await import('pg');
const pool1 = getPool();
@@ -66,7 +66,7 @@ describe('DB Connection Service', () => {
describe('checkTablesExist', () => {
it('should return an empty array if all tables exist', async () => {
const { getPool, checkTablesExist } = await import('./connection');
const { getPool, checkTablesExist } = await import('./connection.db');
const pool = getPool();
// Mock the query response on the *instance* returned by getPool
@@ -81,7 +81,7 @@ describe('DB Connection Service', () => {
});
it('should return an array of missing tables', async () => {
const { getPool, checkTablesExist } = await import('./connection');
const { getPool, checkTablesExist } = await import('./connection.db');
const pool = getPool();
(pool.query as Mock).mockResolvedValue({ rows: [{ table_name: 'users' }] });
@@ -95,7 +95,7 @@ describe('DB Connection Service', () => {
describe('getPoolStatus', () => {
it('should return the status counts from the pool instance', async () => {
const { getPoolStatus } = await import('./connection');
const { getPoolStatus } = await import('./connection.db');
const status = getPoolStatus();
// These values match the default mock implementation in unit-setup/mock-db

View File

@@ -1,4 +1,4 @@
// src/services/db/connection.ts
// src/services/db/connection.db.ts
import { Pool, PoolConfig } from 'pg';
import { logger } from '../logger';
@@ -31,8 +31,8 @@ const createPool = (): Pool => {
};
// --- ADD DEBUG LOGGING HERE ---
console.log(`[DEBUG] connection.ts: createPool called. poolId=${poolId}`);
console.log(`[DEBUG] connection.ts: typeof Pool is "${typeof Pool}"`);
console.log(`[DEBUG] connection.db.ts: createPool called. poolId=${poolId}`);
console.log(`[DEBUG] connection.db.ts: typeof Pool is "${typeof Pool}"`);
if (typeof Pool !== 'function') {
console.error('[DEBUG] CRITICAL: Pool is NOT a function/class!', Pool);
}
@@ -43,12 +43,12 @@ const createPool = (): Pool => {
newPool.poolId = poolId;
// --- ADDED SUCCESS LOG ---
console.log('[DEBUG] connection.ts: Successfully instantiated Pool.');
console.log('[DEBUG] connection.db.ts: Successfully instantiated Pool.');
// -------------------------
return newPool;
} catch (error) {
console.error('[DEBUG] connection.ts: "new Pool()" threw error:', error);
console.error('[DEBUG] connection.db.ts: "new Pool()" threw error:', error);
throw error;
}
};

View File

@@ -1,4 +1,4 @@
// src/services/db/errors.ts
// src/services/db/errors.db.ts
/**
* Base class for custom database errors to ensure they have a status property.

View File

@@ -1,4 +1,4 @@
// src/services/db/flyer.test.ts
// src/services/db/flyer.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getFlyers,
@@ -13,9 +13,9 @@ import {
updateStoreLogo,
trackFlyerItemInteraction,
getHistoricalPriceDataForItems,
} from './flyer';
} from './flyer.db';
import type { Flyer, FlyerItem } from '../../types';
import { getPool } from './connection';
import { getPool } from './connection.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();
@@ -36,7 +36,7 @@ describe('Flyer DB Service', () => {
beforeEach(() => {
// FIX: Use mockImplementation with a standard function to support 'new Pool()'
vi.mocked(Pool).mockImplementation(function() {
console.log('[DEBUG] flyer.test.ts: Local Pool mock instantiated via "new"');
console.log('[DEBUG] flyer.db.test.ts: Local Pool mock instantiated via "new"');
return {
query: mockQuery,
connect: mockConnect,

View File

@@ -1,6 +1,6 @@
// src/services/db/flyer.ts
import { getPool } from './connection';
import { UniqueConstraintError } from './errors';
// src/services/db/flyer.db.ts
import { getPool } from './connection.db';
import { UniqueConstraintError } from './errors.db';
import { logger } from '../logger.server';
import { geocodeAddress } from '../geocodingService.server';
import { Flyer, Brand, MasterGroceryItem, FlyerItem, Address } from '../../types';

View File

@@ -1,13 +1,13 @@
// src/services/db/gamification.test.ts
// src/services/db/gamification.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getAllAchievements,
getUserAchievements,
awardAchievement,
getLeaderboard,
} from './gamification';
} from './gamification.db';
import type { Achievement, UserAchievement, LeaderboardUser } from '../../types';
import { getPool } from './connection';
import { getPool } from './connection.db';
// Mock the getPool function to return a mocked pool object.
const mockQuery = vi.fn();
@@ -32,7 +32,7 @@ vi.mock('../logger', () => ({
describe('Gamification DB Service', () => {
beforeEach(() => {
vi.clearAllMocks();
console.log('[gamification.test.ts] Mocks cleared');
console.log('[gamification.db.test.ts] Mocks cleared');
});
describe('getAllAchievements', () => {

View File

@@ -1,6 +1,6 @@
// src/services/db/gamification.ts
import { getPool } from './connection';
import { ForeignKeyConstraintError } from './errors';
// src/services/db/gamification.db.ts
import { getPool } from './connection.db';
import { ForeignKeyConstraintError } from './errors.db';
import { logger } from '../logger';
import { Achievement, UserAchievement, LeaderboardUser } from '../../types';

View File

@@ -0,0 +1,13 @@
// src/services/db/index.db.ts
export * from './connection.db';
export * from './errors.db';
export * from './user.db';
export * from './flyer.db';
export * from './shopping.db';
export * from './personalization.db';
export * from './recipe.db';
export * from './admin.db';
export * from './notification.db';
export * from './gamification.db';
export * from './budget.db';
export * from './address.db'; // Add the new address service exports

View File

@@ -1,13 +0,0 @@
// src/services/db/index.ts
export * from './connection';
export * from './errors';
export * from './user';
export * from './flyer';
export * from './shopping';
export * from './personalization';
export * from './recipe';
export * from './admin';
export * from './notification';
export * from './gamification';
export * from './budget';
export * from './address'; // Add the new address service exports

View File

@@ -1,4 +1,4 @@
// src/services/db/notification.test.ts
// src/services/db/notification.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getNotificationsForUser,
@@ -6,7 +6,7 @@ import {
createBulkNotifications,
markAllNotificationsAsRead,
markNotificationAsRead,
} from './notification';
} from './notification.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();

View File

@@ -1,6 +1,6 @@
// src/services/db/notification.ts
import { getPool } from './connection';
import { ForeignKeyConstraintError } from './errors';
// src/services/db/notification.db.ts
import { getPool } from './connection.db';
import { ForeignKeyConstraintError } from './errors.db';
import { logger } from '../logger';
import { Notification } from '../../types';

View File

@@ -1,4 +1,4 @@
// src/services/db/personalization.test.ts
// src/services/db/personalization.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getWatchedItems,
@@ -16,9 +16,9 @@ import {
getUserAppliances,
setUserAppliances,
getRecipesForUserDiets,
} from './personalization';
} from './personalization.db';
import type { MasterGroceryItem } from '../../types';
import { getPool } from './connection';
import { getPool } from './connection.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();
@@ -39,7 +39,7 @@ describe('Personalization DB Service', () => {
beforeEach(() => {
mockQuery.mockReset();
vi.mocked(Pool).mockImplementation(function() {
console.log('[DEBUG] personalization.test.ts: Local Pool mock instantiated via "new"');
console.log('[DEBUG] personalization.db.test.ts: Local Pool mock instantiated via "new"');
return {
query: mockQuery,
connect: mockConnect,

View File

@@ -1,6 +1,6 @@
// src/services/db/personalization.ts
import { getPool } from './connection';
import { UniqueConstraintError, ForeignKeyConstraintError } from './errors';
// src/services/db/personalization.db.ts
import { getPool } from './connection.db';
import { UniqueConstraintError, ForeignKeyConstraintError } from './errors.db';
import { logger } from '../logger';
import {
MasterGroceryItem,

View File

@@ -1,4 +1,4 @@
// src/services/db/recipe.test.ts
// src/services/db/recipe.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getRecipesBySalePercentage,
@@ -10,7 +10,7 @@ import {
getRecipeComments,
addRecipeComment,
forkRecipe,
} from './recipe';
} from './recipe.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();
@@ -30,7 +30,7 @@ describe('Recipe DB Service', () => {
beforeEach(() => {
// FIX: Use mockImplementation with a standard function to support 'new Pool()'
vi.mocked(Pool).mockImplementation(function() {
console.log('[DEBUG] recipe.test.ts: Local Pool mock instantiated via "new"');
console.log('[DEBUG] recipe.db.test.ts: Local Pool mock instantiated via "new"');
return {
query: mockQuery,
connect: vi.fn().mockResolvedValue({ query: mockQuery, release: vi.fn() }),

View File

@@ -1,6 +1,6 @@
// src/services/db/recipe.ts
import { getPool } from './connection';
import { ForeignKeyConstraintError } from './errors';
// src/services/db/recipe.db.ts
import { getPool } from './connection.db';
import { ForeignKeyConstraintError } from './errors.db';
import { logger } from '../logger';
import { Recipe, FavoriteRecipe, RecipeComment } from '../../types';

View File

@@ -1,4 +1,4 @@
// src/services/db/shopping.test.ts
// src/services/db/shopping.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getShoppingLists,
@@ -16,7 +16,7 @@ import {
createReceipt,
findDealsForReceipt,
findReceiptOwner,
} from './shopping';
} from './shopping.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();
@@ -36,7 +36,7 @@ describe('Shopping DB Service', () => {
beforeEach(() => {
// FIX: Use mockImplementation with a standard function to support 'new Pool()'
vi.mocked(Pool).mockImplementation(function() {
console.log('[DEBUG] shopping.test.ts: Local Pool mock instantiated via "new"');
console.log('[DEBUG] shopping.db.test.ts: Local Pool mock instantiated via "new"');
return {
query: mockQuery,
connect: vi.fn().mockResolvedValue({ query: mockQuery, release: vi.fn() }),

View File

@@ -1,6 +1,6 @@
// src/services/db/shopping.ts
import { getPool } from './connection';
import { ForeignKeyConstraintError, UniqueConstraintError } from './errors';
// src/services/db/shopping.db.ts
import { getPool } from './connection.db';
import { ForeignKeyConstraintError, UniqueConstraintError } from './errors.db';
import { logger } from '../logger';
import {
ShoppingList,

View File

@@ -1,4 +1,4 @@
// src/services/db/user.test.ts
// src/services/db/user.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
findUserByEmail,
@@ -20,7 +20,7 @@ import {
unfollowUser,
getUserFeed,
logSearchQuery,
} from './user';
} from './user.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();
@@ -28,7 +28,7 @@ const mockRelease = vi.fn();
const mockConnect = vi.fn().mockResolvedValue({ query: mockQuery, release: mockRelease });
import type { Profile } from '../../types';
// Mock other db services that are used by functions in user.ts
// Mock other db services that are used by functions in user.db.ts
vi.mock('./shopping', () => ({
getShoppingLists: vi.fn(),
}));
@@ -194,8 +194,8 @@ describe('User DB Service', () => {
describe('exportUserData', () => {
it('should call profile, watched items, and shopping list functions', async () => {
// This test requires mocking the other db functions that exportUserData calls.
const { getShoppingLists } = await import('./shopping');
const { getWatchedItems } = await import('./personalization');
const { getShoppingLists } = await import('./shopping.db');
const { getWatchedItems } = await import('./personalization.db');
mockQuery.mockResolvedValue({ rows: [{ user_id: '123' }] }); // For findUserProfileById
vi.mocked(getWatchedItems).mockResolvedValue([]);

View File

@@ -1,10 +1,10 @@
// src/services/db/user.ts
import { getPool } from './connection';
// src/services/db/user.db.ts
import { getPool } from './connection.db';
import { logger } from '../logger';
import { UniqueConstraintError, ForeignKeyConstraintError } from './errors';
import { UniqueConstraintError, ForeignKeyConstraintError } from './errors.db';
import { Profile, MasterGroceryItem, ShoppingList, ActivityLogItem, UserProfile } from '../../types';
import { getShoppingLists } from './shopping';
import { getWatchedItems } from './personalization';
import { getShoppingLists } from './shopping.db';
import { getWatchedItems } from './personalization.db';
/**
* Defines the structure of a user object as returned from the database.

View File

@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
// FIX: Explicitly unmock the logger module for this test file.
// This ensures we import the REAL implementation (which calls console.log),
// rather than the global mock defined in setup/unit-setup.ts (which does nothing).
// rather than the global mock defined in setup/tests-setup-unit.ts (which does nothing).
vi.unmock('./logger');
import { logger } from './logger';

View File

@@ -9,7 +9,7 @@ import { promisify } from 'util';
import { logger } from './logger.server';
import * as aiService from './aiService.server';
import * as emailService from './emailService.server';
import * as db from './db';
import * as db from './db/index.db';
import { generateFlyerIcon } from '../utils/imageProcessor';
export const connection = new IORedis(process.env.REDIS_URL || 'redis://127.0.0.1:6379', {

View File

@@ -1,7 +1,7 @@
// src/tests/integration/admin.integration.test.ts
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import * as apiClient from '../../services/apiClient';
import { getPool } from '../../services/db/connection';
import { getPool } from '../../services/db/connection.db';
import type { User } from '../../types';
/**

View File

@@ -1,7 +1,7 @@
// src/tests/integration/auth.integration.test.ts
import { describe, it, expect } from 'vitest';
import { loginUser } from '../../services/apiClient';
import { getPool } from '../../services/db/connection';
import { getPool } from '../../services/db/connection.db';
/**
* @vitest-environment node

View File

@@ -1,8 +1,8 @@
// src/tests/integration/db.integration.test.ts
import { describe, it, expect } from 'vitest';
import * as db from '../../services/db';
import * as db from '../../services/db/index.db';
import * as bcrypt from 'bcrypt';
import { getPool } from '../../services/db/connection';
import { getPool } from '../../services/db/connection.db';
describe('Database Service Integration Tests', () => {
it('should create a new user and be able to find them by email', async ({ onTestFinished }) => {

View File

@@ -4,8 +4,8 @@ import fs from 'fs/promises';
import path from 'path';
import * as apiClient from '../../services/apiClient';
import * as aiApiClient from '../../services/aiApiClient';
import * as db from '../../services/db';
import { getPool } from '../../services/db/connection';
import * as db from '../../services/db/index.db';
import { getPool } from '../../services/db/connection.db';
import { generateFileChecksum } from '../../utils/checksum';
import type { User, MasterGroceryItem } from '../../types';

View File

@@ -1,8 +1,8 @@
// src/tests/integration/shopping-list.integration.test.ts
import { describe, it, expect } from 'vitest';
import * as db from '../../services/db'; // This was missing
import * as db from '../../services/db/index.db'; // This was missing
import * as bcrypt from 'bcrypt';
import { getPool } from '../../services/db/connection';
import { getPool } from '../../services/db/connection.db';
describe('Shopping List DB Service Tests', () => {
it('should create and retrieve a shopping list for a user', async ({ onTestFinished }) => {

View File

@@ -2,7 +2,7 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import * as apiClient from '../../services/apiClient';
import { logger } from '../../services/logger.server';
import { getPool } from '../../services/db/connection';
import { getPool } from '../../services/db/connection.db';
import type { User, MasterGroceryItem, ShoppingList } from '../../types';
/**

View File

@@ -3,7 +3,7 @@ import { execSync } from 'child_process';
import type { Server } from 'http';
import app from '../../../server'; // Import the Express app
import { logger } from '../../services/logger';
import { getPool } from '../../services/db/connection';
import { getPool } from '../../services/db/connection.db';
let server: Server;