many fixes resultnig from latest refactoring
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 5m46s

This commit is contained in:
2025-12-08 23:07:50 -08:00
parent e156c385a5
commit c1a032d5e6
23 changed files with 66 additions and 81 deletions

View File

@@ -1,12 +1,11 @@
// src/App.tsx
import React, { useState, useCallback, useEffect } from 'react';
import { Routes, Route, useParams, useLocation, Outlet } from 'react-router-dom';
import { Routes, Route, useParams, useLocation } from 'react-router-dom';
import { Toaster } from 'react-hot-toast';
import * as pdfjsLib from 'pdfjs-dist';
import { Header } from './components/Header';
import { logger } from './services/logger.client';
import type { Flyer, Profile, User, UserProfile } from './types';
import * as apiClient from './services/apiClient';
import type { Flyer, Profile, User } from './types';
import { ProfileManager } from './pages/admin/components/ProfileManager';
import { VoiceAssistant } from './features/voice-assistant/VoiceAssistant';
import { AdminPage } from './pages/admin/AdminPage';

View File

@@ -2,7 +2,7 @@
import React, { useState, useEffect } from 'react';
import * as apiClient from '../services/apiClient';
import { LeaderboardUser } from '../types';
import { logger } from '../services/logger';
import { logger } from '../services/logger.client';
import { Award, Crown, ShieldAlert } from 'lucide-react';
export const Leaderboard: React.FC = () => {

View File

@@ -8,7 +8,7 @@
import { Pool } from 'pg';
import bcrypt from 'bcrypt';
import { logger } from '../services/logger';
import { logger } from '../services/logger.server';
import { CATEGORIES } from '../types';
const pool = new Pool({

View File

@@ -8,7 +8,7 @@ import { TrashIcon } from '../../components/icons/TrashIcon';
import { SpeakerWaveIcon } from '../../components/icons/SpeakerWaveIcon';
import { generateSpeechFromText } from '../../services/aiApiClient';
import { decode, decodeAudioData } from '../../utils/audioUtils';
import { logger } from '../../services/logger';
import { logger } from '../../services/logger.client';
interface ShoppingListComponentProps {
user: User | null;

View File

@@ -9,7 +9,7 @@ import { CATEGORIES } from '../../types';
import { TrashIcon } from '../../components/icons/TrashIcon';
import { UserIcon } from '../../components/icons/UserIcon';
import { PlusCircleIcon } from '../../components/icons/PlusCircleIcon';
import { logger } from '../../services/logger';
import { logger } from '../../services/logger.client';
interface WatchedItemsListProps {
items: MasterGroceryItem[];
onAddItem: (itemName: string, category: string) => Promise<void>;

View File

@@ -5,7 +5,7 @@ import { MicrophoneIcon } from '../../components/icons/MicrophoneIcon';
// FIX: Corrected the import path. Types should be imported from the top-level '@google/genai' package.
import type { LiveServerMessage, Blob } from '@google/genai';
import { encode } from '../../utils/audioUtils';
import { logger } from '../../services/logger';
import { logger } from '../../services/logger.client';
import { XMarkIcon } from '../../components/icons/XMarkIcon';
interface VoiceAssistantProps {

View File

@@ -75,7 +75,7 @@ describe('useActiveDeals Hook', () => {
mockedApiClient.countFlyerItemsForFlyers.mockResolvedValue(new Response(JSON.stringify({ count: 0 })));
mockedApiClient.fetchFlyerItemsForFlyers.mockResolvedValue(new Response(JSON.stringify([])));
const { result } = renderHook(() => useActiveDeals(mockFlyers, mockWatchedItems));
renderHook(() => useActiveDeals(mockFlyers, mockWatchedItems));
await waitFor(() => {
// Only the valid flyer (id: 1) should be used in the API calls

View File

@@ -2,7 +2,7 @@
import React, { useState, useEffect } from 'react';
import { WatchedItemDeal } from '../types';
import { apiFetch } from '../services/apiClient';
import { logger } from '../services/logger';
import { logger } from '../services/logger.client';
import { AlertCircle, Tag, Store, Calendar } from 'lucide-react';
/**

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import * as apiClient from '../services/apiClient';
import { UserProfile, Achievement, UserAchievement } from '../types';
import { logger } from '../services/logger';
import { logger } from '../services/logger.client';
import { notifySuccess, notifyError } from '../services/notificationService';
import { AchievementsList } from '../components/AchievementsList';

View File

@@ -59,7 +59,7 @@ vi.mock('@bull-board/express', () => ({
}));
// Import the mocked modules to control them
import { BackgroundJobService, backgroundJobService } from '../services/backgroundJobService';
import { backgroundJobService } from '../services/backgroundJobService';
import { flyerQueue, analyticsQueue, cleanupQueue } from '../services/queueService.server';
// Mock the logger

View File

@@ -88,7 +88,7 @@ const createApp = (user?: UserProfile) => {
});
}
app.use('/api/admin', adminRouter);
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
app.use((err: Error, req: Request, res: Response ) => {
res.status(500).json({ message: err.message || 'Internal Server Error' });
});
return app;

View File

@@ -66,7 +66,7 @@ const createApp = (user?: UserProfile) => {
});
}
app.use('/api/admin', adminRouter);
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
app.use((err: Error, req: Request, res: Response) => {
res.status(500).json({ message: err.message || 'Internal Server Error' });
});
return app;

View File

@@ -4,7 +4,7 @@ import supertest from 'supertest';
import express, { Request, Response, NextFunction } from 'express';
import adminRouter from './admin.routes';
import { createMockUserProfile } from '../tests/utils/mockFactories';
import { User, UserProfile, SuggestedCorrection, Brand, Recipe, RecipeComment, UnmatchedFlyerItem, ActivityLogItem, AdminUserView } from '../types';
import { User, UserProfile } from '../types';
// Mock the Service Layer directly.
// The admin.routes.ts file imports from '.../db/index.db'. We need to mock that module.
@@ -90,7 +90,7 @@ const createApp = (user?: UserProfile) => {
});
}
app.use('/api/admin', adminRouter);
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
app.use((err: Error, req: Request, res: Response) => {
res.status(500).json({ message: err.message || 'Internal Server Error' });
});
return app;

View File

@@ -1,7 +1,7 @@
// src/routes/ai.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import express, { type Request, type Response, type NextFunction, Router } from 'express';
import express, { type Request, type Response, type NextFunction } from 'express';
import path from 'node:path';
import aiRouter from './ai.routes';
import { createMockUserProfile, createMockFlyer } from '../tests/utils/mockFactories';
@@ -61,7 +61,7 @@ app.use(express.json({ strict: false }));
app.use('/api/ai', aiRouter);
// Add a generic error handler to catch errors passed via next()
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
app.use((err: Error, req: Request, res: Response) => {
res.status(500).json({ message: err.message || 'Internal Server Error' });
});

View File

@@ -276,7 +276,7 @@ router.post('/flyers/process', optionalAuth, uploadToDisk.single('flyerImage'),
// 3. Create flyer and its items in a transaction
const { flyer: newFlyer, items: newItems } = await createFlyerAndItems(flyerData, itemsArray);
logger.info(`Successfully processed and saved new flyer: ${newFlyer.file_name} (ID: ${newFlyer.flyer_id})`);
logger.info(`Successfully processed and saved new flyer: ${newFlyer.file_name} (ID: ${newFlyer.flyer_id}) with ${newItems.length} items.`);
// Log this significant event
await db.adminRepo.logActivity({

View File

@@ -54,7 +54,7 @@ type PassportCallback = (error: Error | null, user: Express.User | false, info?:
// Mock Passport middleware
vi.mock('./passport.routes', () => ({
default: {
authenticate: (strategy: string, options: Record<string, unknown>, callback: PassportCallback) => (req: Request, res: any, next: any) => {
authenticate: (strategy: string, options: Record<string, unknown>, callback: PassportCallback) => (req: Request, res: any) => {
// Logic to simulate passport authentication outcome based on test input
if (req.body.password === 'wrong_password') {
// Simulate incorrect credentials
@@ -84,7 +84,7 @@ app.use(cookieParser()); // Add cookie-parser middleware to populate req.cookies
app.use('/api/auth', authRouter);
// Add error handler to catch and log 500s during tests
app.use((err: any, req: Request, res: any, next: any) => {
app.use((err: any, req: Request, res: any) => {
console.error('[TEST APP ERROR]', err);
res.status(500).json({ message: err.message });
});

View File

@@ -76,7 +76,7 @@ router.post('/register', async (req, res, next) => {
newUser = await repoWithTransaction.createUser(email, hashedPassword, { full_name, avatar_url });
await client.query('COMMIT');
} catch (error: any) {
} catch (error: unknown) {
if (error instanceof UniqueConstraintError) {
// If the email is a duplicate, return a 409 Conflict status.
return res.status(409).json({ message: error.message });

View File

@@ -45,7 +45,6 @@ fs.mkdir(avatarUploadDir, { recursive: true }).catch(err => {
const avatarStorage = multer.diskStorage({
destination: (req, file, cb) => cb(null, avatarUploadDir),
filename: (req, file, cb) => {
const user = req.user as User;
const uniqueSuffix = `${(req.user as UserProfile).user.user_id}-${Date.now()}${path.extname(file.originalname)}`;
cb(null, uniqueSuffix);
},

View File

@@ -1,5 +1,5 @@
// src/services/aiService.server.test.ts
import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { MasterGroceryItem } from '../types';
// Import the class, not the singleton instance, so we can instantiate it with mocks.
import { AIService } from './aiService.server';

View File

@@ -1,5 +1,5 @@
// src/services/apiClient.test.ts
import { describe, it, expect, vi, beforeAll, afterAll, afterEach, beforeEach } from 'vitest';
import { describe, it, expect, vi, afterAll, afterEach, beforeEach } from 'vitest';
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
@@ -43,27 +43,31 @@ describe('API Client', () => {
// These variables will be used to capture details from the fetch call.
let capturedUrl: URL | null = null;
let capturedHeaders: Headers | null = null;
let capturedBody: any = null;
let capturedBody: string | FormData | Record<string, unknown> | null = null;
beforeEach(() => {
// This mock now captures the arguments passed to fetch before returning a successful response.
global.fetch = vi.fn((url, options) => {
// Define a correctly typed mock function for fetch.
const mockFetch = (url: RequestInfo | URL, options?: RequestInit): Promise<Response> => {
capturedUrl = new URL(url as string, 'http://localhost');
capturedHeaders = options?.headers as Headers;
// FIX: Parse body if it is a string
if (typeof options?.body === 'string') {
try {
capturedBody = JSON.parse(options.body);
} catch {
capturedBody = options.body;
}
} else if (options?.body instanceof FormData) {
capturedBody = options.body;
} else {
capturedBody = options?.body || null;
capturedBody = null;
}
return Promise.resolve(new Response(JSON.stringify({ data: 'mock-success' }), { status: 200, headers: new Headers() }));
}) as any;
return Promise.resolve(new Response(JSON.stringify({ data: 'mock-success' }), { status: 200, headers: new Headers() as Headers }));
};
// Assign the mock function to global.fetch and cast it to a Vitest mock.
global.fetch = vi.fn(mockFetch);
});
afterEach(() => {
@@ -184,10 +188,8 @@ describe('API Client', () => {
describe('Budget API Functions', () => {
it('getBudgets should call the correct endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/budgets', () => {
wasCalled = true;
return HttpResponse.json([]);
})
);
@@ -350,10 +352,8 @@ describe('API Client', () => {
it('forkRecipe should send a POST request to the correct URL', async () => {
const recipeId = 99;
let wasCalled = false;
server.use(
http.post(`http://localhost/api/recipes/${recipeId}/fork`, () => {
wasCalled = true;
return HttpResponse.json({ success: true });
})
);
@@ -459,10 +459,8 @@ describe('API Client', () => {
});
it('checkDbSchema should call the correct health check endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/health/db-schema', () => {
wasCalled = true;
return HttpResponse.json({ success: true });
})
);
@@ -471,10 +469,8 @@ describe('API Client', () => {
});
it('checkStorage should call the correct health check endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/health/storage', () => {
wasCalled = true;
return HttpResponse.json({ success: true });
})
);
@@ -483,10 +479,8 @@ describe('API Client', () => {
});
it('checkDbPoolHealth should call the correct health check endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/health/db-pool', () => {
wasCalled = true;
return HttpResponse.json({ success: true });
})
);
@@ -495,10 +489,8 @@ describe('API Client', () => {
});
it('checkRedisHealth should call the correct health check endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/health/redis', () => {
wasCalled = true;
return HttpResponse.json({ success: true });
})
);
@@ -507,10 +499,8 @@ describe('API Client', () => {
});
it('checkPm2Status should call the correct system endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/system/pm2-status', () => {
wasCalled = true;
return HttpResponse.json({ success: true });
})
);
@@ -519,10 +509,8 @@ describe('API Client', () => {
});
it('fetchFlyers should call the correct public endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/flyers', () => {
wasCalled = true;
return HttpResponse.json([]);
})
);
@@ -531,10 +519,8 @@ describe('API Client', () => {
});
it('fetchMasterItems should call the correct public endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/master-items', () => {
wasCalled = true;
return HttpResponse.json([]);
})
);
@@ -543,10 +529,8 @@ describe('API Client', () => {
});
it('fetchCategories should call the correct public endpoint', async () => {
let wasCalled = false;
server.use(
http.get('http://localhost/api/categories', () => {
wasCalled = true;
return HttpResponse.json([]);
})
);
@@ -556,10 +540,8 @@ describe('API Client', () => {
it('fetchFlyerItems should call the correct public endpoint for a specific flyer', async () => {
const flyerId = 123;
let wasCalled = false;
server.use(
http.get(`http://localhost/api/flyers/${flyerId}/items`, () => {
wasCalled = true;
return HttpResponse.json([]);
})
);

Some files were not shown because too many files have changed in this diff Show More