Add user database service and unit tests
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m28s
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:
@@ -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[] = [
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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.
|
||||
@@ -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';
|
||||
/**
|
||||
@@ -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();
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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.
|
||||
@@ -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,
|
||||
@@ -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';
|
||||
@@ -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', () => {
|
||||
@@ -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';
|
||||
|
||||
13
src/services/db/index.db.ts
Normal file
13
src/services/db/index.db.ts
Normal 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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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() }),
|
||||
@@ -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';
|
||||
|
||||
@@ -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() }),
|
||||
@@ -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,
|
||||
@@ -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([]);
|
||||
@@ -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.
|
||||
@@ -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';
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user