lootsa tests fixes
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m29s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m29s
This commit is contained in:
@@ -54,10 +54,12 @@ describe('App Component', () => {
|
||||
// Clear local storage to prevent auth state from leaking between tests.
|
||||
localStorage.clear();
|
||||
// Default mocks for API calls
|
||||
mockedApiClient.fetchFlyers.mockResolvedValue(new Response(JSON.stringify([])));
|
||||
mockedApiClient.fetchMasterItems.mockResolvedValue(new Response(JSON.stringify([])));
|
||||
mockedApiClient.fetchWatchedItems.mockResolvedValue(new Response(JSON.stringify([])));
|
||||
mockedApiClient.fetchShoppingLists.mockResolvedValue(new Response(JSON.stringify([])));
|
||||
// Use mockImplementation to create a new Response object for each call,
|
||||
// preventing "Body has already been read" errors.
|
||||
mockedApiClient.fetchFlyers.mockImplementation(() => Promise.resolve(new Response(JSON.stringify([]))));
|
||||
mockedApiClient.fetchMasterItems.mockImplementation(() => Promise.resolve(new Response(JSON.stringify([]))));
|
||||
mockedApiClient.fetchWatchedItems.mockImplementation(() => Promise.resolve(new Response(JSON.stringify([]))));
|
||||
mockedApiClient.fetchShoppingLists.mockImplementation(() => Promise.resolve(new Response(JSON.stringify([]))));
|
||||
mockedApiClient.getAuthenticatedUserProfile.mockRejectedValue(new Error('Not authenticated'));
|
||||
});
|
||||
|
||||
|
||||
@@ -269,8 +269,8 @@ describe('SystemCheck', () => {
|
||||
// Instead of test-ids, we check for the result: the icon's color class.
|
||||
// This is more robust as it doesn't depend on the icon component's internal props.
|
||||
const passIcons = container.querySelectorAll('svg.text-green-500'); // Redis check is now passing
|
||||
// 7 checks should pass (API key, backend, PM2, DB Pool, Redis, Seed, Storage)
|
||||
expect(passIcons.length).toBe(7);
|
||||
// All 8 checks should pass in this scenario, but one is mocked to fail.
|
||||
expect(passIcons.length).toBe(7); // This was correct, the error was likely a red herring from other failing tests.
|
||||
|
||||
// Check for the fail icon's color class
|
||||
const failIcon = container.querySelector('svg.text-red-500');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/routes/admin.test.ts
|
||||
import { describe, it, expect, vi, beforeEach, afterAll, type Mocked, type Mock } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterAll, type Mocked } from 'vitest';
|
||||
import supertest from 'supertest';
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import path from 'path';
|
||||
@@ -7,7 +7,7 @@ import fs from 'node:fs/promises';
|
||||
import adminRouter from './admin.routes'; // Correctly imported
|
||||
import { createMockUserProfile, createMockSuggestedCorrection, createMockBrand, createMockRecipe, createMockRecipeComment, createMockActivityLogItem } from '../tests/utils/mockFactories';
|
||||
import { Job } from 'bullmq';
|
||||
import { UserProfile, SuggestedCorrection, Brand, Recipe, RecipeComment, User, UnmatchedFlyerItem } from '../types';
|
||||
import { SuggestedCorrection, Brand, User, UnmatchedFlyerItem } from '../types';
|
||||
|
||||
// Mock the specific DB modules used by the admin router.
|
||||
// This is more robust than mocking the barrel file ('../services/db').
|
||||
@@ -55,16 +55,18 @@ vi.mock('../services/queueService.server', () => ({
|
||||
// The adapters expect real BullMQ queues, which we don't have in tests.
|
||||
vi.mock('@bull-board/api', () => ({
|
||||
// Mock createBullBoard to do nothing.
|
||||
createBullBoard: vi.fn(),
|
||||
// Mock the adapter constructor.
|
||||
BullMQAdapter: vi.fn(),
|
||||
createBullBoard: vi.fn(() => ({ router: (req: Request, res: Response, next: NextFunction) => next() })),
|
||||
// Mock the adapter as a class since the code uses `new BullMQAdapter()`.
|
||||
BullMQAdapter: class MockBullMQAdapter {},
|
||||
}));
|
||||
vi.mock('@bull-board/express', () => ({
|
||||
// Mock the ExpressAdapter to return a dummy router.
|
||||
ExpressAdapter: vi.fn(() => ({
|
||||
setBasePath: vi.fn(),
|
||||
getRouter: () => (req: Request, res: Response, next: NextFunction) => next(),
|
||||
})),
|
||||
// Mock the ExpressAdapter as a class since the code uses `new ExpressAdapter()`.
|
||||
ExpressAdapter: class MockExpressAdapter {
|
||||
setBasePath() {}
|
||||
getRouter() {
|
||||
return (req: Request, res: Response, next: NextFunction) => next();
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
// Import the mocked modules to control them in tests.
|
||||
|
||||
@@ -14,7 +14,6 @@ vi.mock('../services/aiService.server');
|
||||
// Mock the specific DB modules used by the AI router.
|
||||
vi.mock('../services/db/flyer.db');
|
||||
vi.mock('../services/db/admin.db');
|
||||
const mockedDb = { ...flyerDb, ...adminDb } as Mocked<typeof flyerDb & typeof adminDb>;
|
||||
|
||||
// Mock the logger to keep test output clean
|
||||
vi.mock('../services/logger.server', () => ({
|
||||
|
||||
@@ -13,7 +13,6 @@ import { UserProfile } from '../types';
|
||||
// This decouples the route tests from the SQL implementation details.
|
||||
vi.mock('../services/db/user.db');
|
||||
vi.mock('../services/db/admin.db');
|
||||
const mockedDb = { ...userDb, ...adminDb } as Mocked<typeof userDb & typeof adminDb>;
|
||||
// Mock the logger to keep test output clean
|
||||
vi.mock('../services/logger.server', () => ({
|
||||
logger: {
|
||||
|
||||
@@ -5,11 +5,16 @@ import express, { Request, Response, NextFunction } from 'express';
|
||||
import budgetRouter from './budget.routes';
|
||||
import * as budgetDb from '../services/db/budget.db';
|
||||
import { createMockUserProfile, createMockBudget, createMockSpendingByCategory } from '../tests/utils/mockFactories';
|
||||
import { Budget, SpendingByCategory } from '../types';
|
||||
|
||||
// 1. Mock the Service Layer directly.
|
||||
// This decouples the route tests from the database logic.
|
||||
vi.mock('../services/db/budget.db');
|
||||
vi.mock('../services/db/budget.db', () => ({
|
||||
getBudgetsForUser: vi.fn(),
|
||||
createBudget: vi.fn(),
|
||||
updateBudget: vi.fn(),
|
||||
deleteBudget: vi.fn(),
|
||||
getSpendingByCategory: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock the logger to keep test output clean
|
||||
vi.mock('../services/logger.server', () => ({
|
||||
|
||||
@@ -5,7 +5,6 @@ import express, { Request, Response, NextFunction } from 'express';
|
||||
import gamificationRouter from './gamification.routes';
|
||||
import * as gamificationDb from '../services/db/gamification.db';
|
||||
import { createMockUserProfile, createMockAchievement, createMockUserAchievement } from '../tests/utils/mockFactories';
|
||||
import { Achievement, UserAchievement } from '../types';
|
||||
|
||||
// Mock the entire db service
|
||||
vi.mock('../services/db/gamification.db');
|
||||
|
||||
@@ -29,9 +29,12 @@ vi.mock('passport-jwt', () => ({
|
||||
}));
|
||||
|
||||
import * as db from '../services/db/index.db';
|
||||
import { UserProfile } from '../types';
|
||||
|
||||
// Mock dependencies before importing the passport configuration
|
||||
vi.mock('../services/db');
|
||||
vi.mock('../services/db/index.db', () => ({
|
||||
findUserProfileById: vi.fn(),
|
||||
}));
|
||||
const mockedDb = db as Mocked<typeof db>;
|
||||
|
||||
vi.mock('../services/logger.server', () => ({
|
||||
@@ -65,12 +68,8 @@ describe('Passport Configuration', () => {
|
||||
describe('JwtStrategy (Isolated Callback Logic)', () => {
|
||||
it('should call done(null, userProfile) on successful authentication', async () => {
|
||||
// Arrange
|
||||
const jwtPayload = { user_id: 'user-123' };
|
||||
const mockProfile: Profile = {
|
||||
user_id: 'user-123',
|
||||
role: 'user',
|
||||
points: 100, // Add missing required property
|
||||
};
|
||||
const jwtPayload = { user_id: 'user-123' };
|
||||
const mockProfile = { user_id: 'user-123', role: 'user', points: 100, user: { user_id: 'user-123', email: 'test@test.com' } } as UserProfile;
|
||||
vi.mocked(mockedDb.findUserProfileById).mockResolvedValue(mockProfile);
|
||||
const done = vi.fn();
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import * as flyerDb from '../services/db/flyer.db';
|
||||
import * as recipeDb from '../services/db/recipe.db';
|
||||
import * as adminDb from '../services/db/admin.db';
|
||||
import { createMockFlyer, createMockFlyerItem, createMockMasterGroceryItem, createMockRecipe } from '../tests/utils/mockFactories';
|
||||
import { Flyer, Recipe } from '../types';
|
||||
|
||||
// 1. Mock the Service Layer directly.
|
||||
// This decouples the route tests from the SQL implementation details.
|
||||
|
||||
@@ -8,13 +8,17 @@ import systemRouter from './system.routes';
|
||||
// Define a type for the exec callback to avoid using `any`.
|
||||
type ExecCallback = (error: ExecException | null, stdout: string, stderr: string) => void;
|
||||
|
||||
// Mock the 'child_process' module.
|
||||
// Mock the 'child_process' module to control the behavior of `exec`.
|
||||
vi.mock('child_process', () => {
|
||||
// The mock for `exec` needs to accept the command and a callback,
|
||||
// and then it must *call* that callback to prevent the test from hanging.
|
||||
const execMock = vi.fn((command: string, callback: ExecCallback) => {
|
||||
// Provide a default success behavior. Individual tests can override this with .mockImplementation().
|
||||
callback(null, 'PM2 is online', '');
|
||||
return {} as ChildProcess; // Return a dummy ChildProcess object.
|
||||
});
|
||||
return {
|
||||
// Mock the named export 'exec'.
|
||||
exec: vi.fn(),
|
||||
// Also mock the default export if it's used elsewhere.
|
||||
default: { exec: vi.fn() },
|
||||
exec: execMock,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as userDb from '../services/db/user.db';
|
||||
import * as personalizationDb from '../services/db/personalization.db';
|
||||
import * as shoppingDb from '../services/db/shopping.db';
|
||||
import { createMockUserProfile, createMockMasterGroceryItem, createMockShoppingList, createMockShoppingListItem } from '../tests/utils/mockFactories';
|
||||
import { UserProfile, Appliance } from '../types';
|
||||
import { Appliance } from '../types';
|
||||
|
||||
// 1. Mock the Service Layer directly.
|
||||
vi.mock('../services/db/user.db', () => ({
|
||||
|
||||
@@ -2,22 +2,24 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
|
||||
// Mock the nodemailer library BEFORE importing the email service.
|
||||
const mockSendMail = vi.fn().mockImplementation((mailOptions) => {
|
||||
console.log('[TEST DEBUG] mockSendMail called with:', mailOptions?.to);
|
||||
return Promise.resolve({
|
||||
messageId: 'default-id',
|
||||
// Fix: Provide a buffer for the message to prevent "cannot read properties of undefined (reading toString)" in service logger
|
||||
message: Buffer.from('mock-email-content'),
|
||||
});
|
||||
});
|
||||
// Use vi.hoisted to define mocks that need to be available inside vi.mock factories.
|
||||
// This prevents "Cannot access before initialization" errors.
|
||||
const mocks = vi.hoisted(() => ({
|
||||
sendMail: vi.fn().mockImplementation((mailOptions) => {
|
||||
console.log('[TEST DEBUG] mockSendMail called with:', mailOptions?.to);
|
||||
return Promise.resolve({
|
||||
messageId: 'default-id',
|
||||
message: Buffer.from('mock-email-content'),
|
||||
});
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('nodemailer', () => {
|
||||
return {
|
||||
// Mock the default export which is what the service uses
|
||||
default: {
|
||||
// The createTransport function returns an object with a sendMail method
|
||||
createTransport: vi.fn(() => ({ sendMail: mockSendMail })),
|
||||
createTransport: vi.fn(() => ({ sendMail: mocks.sendMail })),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -39,7 +41,7 @@ describe('Email Service (Server)', () => {
|
||||
console.log('[TEST SETUP] Setting up Email Service mocks');
|
||||
vi.clearAllMocks();
|
||||
// Reset to default successful implementation
|
||||
mockSendMail.mockImplementation((mailOptions) => {
|
||||
mocks.sendMail.mockImplementation((mailOptions) => {
|
||||
console.log('[TEST DEBUG] mockSendMail (default) called with:', mailOptions?.to);
|
||||
return Promise.resolve({
|
||||
messageId: 'default-mock-id',
|
||||
@@ -55,8 +57,8 @@ describe('Email Service (Server)', () => {
|
||||
|
||||
await sendPasswordResetEmail(to, resetLink);
|
||||
|
||||
expect(mockSendMail).toHaveBeenCalledTimes(1);
|
||||
const mailOptions = mockSendMail.mock.calls[0][0];
|
||||
expect(mocks.sendMail).toHaveBeenCalledTimes(1);
|
||||
const mailOptions = mocks.sendMail.mock.calls[0][0];
|
||||
|
||||
expect(mailOptions.to).toBe(to);
|
||||
expect(mailOptions.subject).toBe('Your Password Reset Request');
|
||||
@@ -70,12 +72,12 @@ describe('Email Service (Server)', () => {
|
||||
const to = 'new.user@example.com';
|
||||
const name = 'Jane Doe';
|
||||
|
||||
mockSendMail.mockResolvedValue({ messageId: 'test-id', message: Buffer.from('content') });
|
||||
mocks.sendMail.mockResolvedValue({ messageId: 'test-id', message: Buffer.from('content') });
|
||||
|
||||
await sendWelcomeEmail(to, name);
|
||||
|
||||
expect(mockSendMail).toHaveBeenCalledTimes(1);
|
||||
const mailOptions = mockSendMail.mock.calls[0][0];
|
||||
expect(mocks.sendMail).toHaveBeenCalledTimes(1);
|
||||
const mailOptions = mocks.sendMail.mock.calls[0][0];
|
||||
|
||||
expect(mailOptions.to).toBe(to);
|
||||
expect(mailOptions.subject).toBe('Welcome to Flyer Crawler!');
|
||||
@@ -86,12 +88,12 @@ describe('Email Service (Server)', () => {
|
||||
it('should send a generic welcome email when a name is not provided', async () => {
|
||||
const to = 'another.user@example.com';
|
||||
|
||||
mockSendMail.mockResolvedValue({ messageId: 'test-id', message: Buffer.from('content') });
|
||||
mocks.sendMail.mockResolvedValue({ messageId: 'test-id', message: Buffer.from('content') });
|
||||
|
||||
await sendWelcomeEmail(to, null);
|
||||
|
||||
expect(mockSendMail).toHaveBeenCalledTimes(1);
|
||||
const mailOptions = mockSendMail.mock.calls[0][0];
|
||||
expect(mocks.sendMail).toHaveBeenCalledTimes(1);
|
||||
const mailOptions = mocks.sendMail.mock.calls[0][0];
|
||||
|
||||
expect(mailOptions.text).toContain('Hello there,');
|
||||
expect(mailOptions.html).toContain('<p>Hello there,</p>');
|
||||
|
||||
Reference in New Issue
Block a user