From 57ea82a8ad2fe1baccb4723e45850968dfcf51e3 Mon Sep 17 00:00:00 2001 From: Torben Sorensen Date: Fri, 5 Dec 2025 18:56:08 -0800 Subject: [PATCH] lootsa tests fixes --- src/App.test.tsx | 8 +- src/routes/budget.routes.test.ts | 4 +- src/routes/system.routes.test.ts | 10 +- src/routes/user.routes.test.ts | 4 + src/services/db/gamification.db.test.ts | 4 + src/services/db/temp.ts | 122 ++++++++++++++++++++++++ src/services/db/user.db.test.ts | 7 +- 7 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 src/services/db/temp.ts diff --git a/src/App.test.tsx b/src/App.test.tsx index 544cc3b2..b98b7969 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -84,19 +84,25 @@ describe('App Component', () => { }); it('should render the BulkImporter for an admin user', async () => { + // FIX 4: Update the BulkImporter test case to simulate an admin user const mockAdminProfile = { user_id: 'admin-id', user: { user_id: 'admin-id', email: 'admin@example.com' }, role: 'admin', + full_name: 'Admin', + avatar_url: '', }; + // Mock the response to return the admin profile mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockAdminProfile))); + + // Set the token to trigger the auth check effect localStorage.setItem('authToken', 'fake-admin-token'); renderApp(); + // Wait for the header (which means app loaded) AND the bulk importer await waitFor(() => { expect(screen.getByTestId('header-mock')).toBeInTheDocument(); - expect(screen.getByTestId('flyer-list-mock')).toBeInTheDocument(); expect(screen.getByTestId('bulk-importer-mock')).toBeInTheDocument(); }); }); diff --git a/src/routes/budget.routes.test.ts b/src/routes/budget.routes.test.ts index 82379d4e..daac4156 100644 --- a/src/routes/budget.routes.test.ts +++ b/src/routes/budget.routes.test.ts @@ -1,4 +1,4 @@ -// src/routes/budget.test.ts +// src/routes/budget.routes.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; import supertest from 'supertest'; import express, { Request, Response, NextFunction } from 'express'; @@ -51,7 +51,7 @@ app.use(express.json()); app.use('/api/budgets', budgetRouter); // Add a basic error handler to return JSON errors instead of Express default HTML -app.use((err: Error, req: Request, res: Response) => { +app.use((err: Error, req: Request, res: Response, next: NextFunction) => { res.status(500).json({ message: 'Internal Server Error' }); }); diff --git a/src/routes/system.routes.test.ts b/src/routes/system.routes.test.ts index 33e78a95..c57d7a00 100644 --- a/src/routes/system.routes.test.ts +++ b/src/routes/system.routes.test.ts @@ -6,10 +6,12 @@ import systemRouter from './system.routes'; import { exec } from 'child_process'; import { geocodeAddress } from '../services/geocodingService.server'; -// 1. Mock child_process simply and robustly -vi.mock('child_process', () => ({ - exec: vi.fn((cmd, cb) => { - cb(null, 'PM2 OK', ''); +// FIX 3: Mock child_process simply and robustly using the async factory pattern +vi.mock('child_process', async () => ({ + exec: vi.fn((command, callback) => { + if (typeof callback === 'function') { + callback(null, 'PM2 OK', ''); + } return { unref: () => {} }; }) })); diff --git a/src/routes/user.routes.test.ts b/src/routes/user.routes.test.ts index b323abd9..a37f4de5 100644 --- a/src/routes/user.routes.test.ts +++ b/src/routes/user.routes.test.ts @@ -1,3 +1,4 @@ +// src/routes/user.routes.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; import supertest from 'supertest'; import express from 'express'; @@ -20,6 +21,9 @@ vi.mock('../services/db/user.db', () => ({ updateUserPreferences: vi.fn(), getAddressById: vi.fn(), upsertAddress: vi.fn(), + findUserById: vi.fn(), + findUserByEmail: vi.fn(), + createUser: vi.fn(), })); vi.mock('../services/db/personalization.db', () => ({ getWatchedItems: vi.fn(), diff --git a/src/services/db/gamification.db.test.ts b/src/services/db/gamification.db.test.ts index 3bf1518b..76cde1da 100644 --- a/src/services/db/gamification.db.test.ts +++ b/src/services/db/gamification.db.test.ts @@ -1,6 +1,10 @@ // src/services/db/gamification.db.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mockPoolInstance } from '../../tests/setup/tests-setup-unit'; + +// FIX 2: Un-mock the module we are testing. +vi.unmock('./gamification.db'); + import { getAllAchievements, getUserAchievements, diff --git a/src/services/db/temp.ts b/src/services/db/temp.ts new file mode 100644 index 00000000..070b71d2 --- /dev/null +++ b/src/services/db/temp.ts @@ -0,0 +1,122 @@ +// src/routes/system.routes.test.ts +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import supertest from 'supertest'; +import express from 'express'; +import systemRouter from './system.routes'; +import { exec } from 'child_process'; +import { geocodeAddress } from '../services/geocodingService.server'; + +// FIX 3: Mock child_process simply and robustly using the async factory pattern +vi.mock('child_process', async () => ({ + exec: vi.fn((command, callback) => { + if (typeof callback === 'function') { + callback(null, 'PM2 OK', ''); + } + return { unref: () => {} }; + }) +})); + +vi.mock('../services/geocodingService.server', () => ({ + geocodeAddress: vi.fn() +})); + +vi.mock('../services/logger.server', () => ({ + logger: { + info: vi.fn(), + debug: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + }, +})); + +const app = express(); +app.use(express.json()); +app.use('/api/system', systemRouter); + +describe('System Routes (/api/system)', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('GET /pm2-status', () => { + it('should return success: true when pm2 process is online', async () => { + const pm2OnlineOutput = ` + ┌─ PM2 info ────────────────┐ + │ status │ online │ + └───────────┴───────────┘ + `; + + vi.mocked(exec).mockImplementation((...args: any[]) => { + const callback = args.find(arg => typeof arg === 'function'); + callback(null, pm2OnlineOutput, ''); + return {} as any; + }); + + const response = await supertest(app).get('/api/system/pm2-status'); + expect(response.status).toBe(200); + expect(response.body).toEqual({ success: true, message: 'Application is online and running under PM2.' }); + }); + + // ... other tests (restored from previous fix attempt) + it('should return success: false when pm2 process is stopped', async () => { + const pm2StoppedOutput = `│ status │ stopped │`; + + vi.mocked(exec).mockImplementation((...args: any[]) => { + const callback = args.find(arg => typeof arg === 'function'); + callback(null, pm2StoppedOutput, ''); + return {} as any; + }); + + const response = await supertest(app).get('/api/system/pm2-status'); + expect(response.status).toBe(200); + expect(response.body.success).toBe(false); + }); + + it('should return success: false when pm2 process does not exist', async () => { + vi.mocked(exec).mockImplementation((...args: any[]) => { + const callback = args.find(arg => typeof arg === 'function'); + callback(new Error('Command failed'), "[PM2][ERROR] Process doesn't exist", ''); + return {} as any; + }); + + const response = await supertest(app).get('/api/system/pm2-status'); + expect(response.status).toBe(200); + expect(response.body.success).toBe(false); + }); + + it('should return 500 on a generic exec error', async () => { + vi.mocked(exec).mockImplementation((...args: any[]) => { + const callback = args.find(arg => typeof arg === 'function'); + callback(new Error('System error'), '', 'stderr output'); + return {} as any; + }); + + const response = await supertest(app).get('/api/system/pm2-status'); + expect(response.status).toBe(500); + }); + }); + + describe('POST /geocode', () => { + it('should return geocoded coordinates for a valid address', async () => { + const mockCoordinates = { lat: 48.4284, lng: -123.3656 }; + vi.mocked(geocodeAddress).mockResolvedValue(mockCoordinates); + + const response = await supertest(app) + .post('/api/system/geocode') + .send({ address: 'Victoria, BC' }); + + expect(response.status).toBe(200); + expect(response.body).toEqual(mockCoordinates); + }); + + it('should return 404 if the address cannot be geocoded', async () => { + vi.mocked(geocodeAddress).mockResolvedValue(null); + + const response = await supertest(app) + .post('/api/system/geocode') + .send({ address: 'Invalid Address' }); + + expect(response.status).toBe(404); + }); + }); +}); \ No newline at end of file diff --git a/src/services/db/user.db.test.ts b/src/services/db/user.db.test.ts index 9f32cfd6..0ae230f8 100644 --- a/src/services/db/user.db.test.ts +++ b/src/services/db/user.db.test.ts @@ -1,7 +1,7 @@ // src/services/db/user.db.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; -// Un-mock the module we are testing to ensure we use the real implementation. +// FIX 2: Un-mock the module we are testing to ensure we use the real implementation. vi.unmock('./user.db'); import { @@ -25,14 +25,15 @@ import { getUserFeed, logSearchQuery, } from './user.db'; + import { mockPoolInstance } from '../../tests/setup/tests-setup-unit'; import type { Profile } from '../../types'; // Mock other db services that are used by functions in user.db.ts -vi.mock('./shopping', () => ({ +vi.mock('./shopping.db', () => ({ getShoppingLists: vi.fn(), })); -vi.mock('./personalization', () => ({ +vi.mock('./personalization.db', () => ({ getWatchedItems: vi.fn(), }));