Refactor database tests to improve type safety and error handling
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
- Updated mock implementations in various database service tests to use specific types (Pool, PoolClient) instead of 'any'. - Enhanced error handling in tests by explicitly defining error types and codes, improving clarity and maintainability. - Removed unnecessary eslint-disable comments related to 'any' usage. - Cleaned up transaction mock implementations to ensure proper type casting. - Adjusted error handling in the UserRepository to provide more specific error messages for foreign key constraints. - Improved test assertions to ensure they are more robust and type-safe.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// src/services/db/address.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import type { Pool } from 'pg';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import { AddressRepository } from './address.db';
|
||||
import type { Address } from '../../types';
|
||||
@@ -19,7 +20,7 @@ describe('Address DB Service', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
addressRepo = new AddressRepository(mockPoolInstance as any);
|
||||
addressRepo = new AddressRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('getAddressById', () => {
|
||||
@@ -96,8 +97,8 @@ describe('Address DB Service', () => {
|
||||
|
||||
it('should throw UniqueConstraintError on duplicate address insert', async () => {
|
||||
const newAddressData = { address_line_1: '123 Main St', city: 'Anytown' };
|
||||
const dbError = new Error('duplicate key value violates unique constraint');
|
||||
(dbError as any).code = '23505';
|
||||
const dbError = new Error('duplicate key value violates unique constraint') as Error & { code: string };
|
||||
dbError.code = '23505';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
|
||||
await expect(addressRepo.upsertAddress(newAddressData, mockLogger)).rejects.toThrow(UniqueConstraintError);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// src/services/db/admin.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import type { Pool, PoolClient } from 'pg';
|
||||
import { ForeignKeyConstraintError, NotFoundError } from './errors.db';
|
||||
import { AdminRepository } from './admin.db';
|
||||
import type { SuggestedCorrection, AdminUserView, User } from '../../types';
|
||||
@@ -36,10 +37,10 @@ describe('Admin DB Service', () => {
|
||||
// Reset the withTransaction mock before each test
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
// Instantiate the repository with the mock pool for each test
|
||||
adminRepo = new AdminRepository(mockPoolInstance as any);
|
||||
adminRepo = new AdminRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('getSuggestedCorrections', () => {
|
||||
@@ -282,13 +283,13 @@ describe('Admin DB Service', () => {
|
||||
it('should execute a transaction to resolve an unmatched item', async () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query
|
||||
.mockResolvedValueOnce({ rows: [{ flyer_item_id: 55 }] }) // SELECT flyer_item_id
|
||||
.mockResolvedValueOnce({ rowCount: 1 }) // UPDATE flyer_items
|
||||
.mockResolvedValueOnce({ rowCount: 1 }); // UPDATE unmatched_flyer_items
|
||||
return callback(mockClient as any);
|
||||
(mockClient.query as Mock)
|
||||
.mockResolvedValueOnce({ rows: [{ flyer_item_id: 55 }] }) // SELECT flyer_item_id from unmatched_flyer_items
|
||||
.mockResolvedValueOnce({ rowCount: 1 }) // UPDATE flyer_items table
|
||||
.mockResolvedValueOnce({ rowCount: 1 }); // UPDATE unmatched_flyer_items table
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
|
||||
await adminRepo.resolveUnmatchedFlyerItem(1, 101, mockLogger);
|
||||
|
||||
const mockClient = (vi.mocked(withTransaction).mock.calls[0][0] as any).mock.instances[0];
|
||||
@@ -300,8 +301,8 @@ describe('Admin DB Service', () => {
|
||||
it('should throw NotFoundError if the unmatched item is not found', async () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockResolvedValueOnce({ rowCount: 0, rows: [] }); // SELECT finds nothing
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(NotFoundError);
|
||||
(mockClient.query as Mock).mockResolvedValueOnce({ rowCount: 0, rows: [] }); // SELECT finds nothing
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(NotFoundError);
|
||||
throw new NotFoundError(`Unmatched flyer item with ID 999 not found.`); // Re-throw for the outer expect
|
||||
});
|
||||
|
||||
@@ -313,10 +314,10 @@ describe('Admin DB Service', () => {
|
||||
const dbError = new Error('DB Error');
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query
|
||||
(mockClient.query as Mock)
|
||||
.mockResolvedValueOnce({ rows: [{ flyer_item_id: 55 }] }) // SELECT flyer_item_id
|
||||
.mockRejectedValueOnce(dbError); // UPDATE flyer_items fails
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError; // Re-throw for the outer expect
|
||||
});
|
||||
|
||||
@@ -481,7 +482,8 @@ describe('Admin DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if the user does not exist on update', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
// Create a more specific type for the error object to avoid using 'any'
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
|
||||
await expect(adminRepo.updateUserRole('non-existent-user', 'admin', mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
|
||||
@@ -390,7 +390,6 @@ export class AdminRepository {
|
||||
* Defines a type for JSON-compatible data structures, allowing for nested objects and arrays.
|
||||
* This provides a safer alternative to `any` for objects intended for JSON serialization.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async logActivity(logData: {
|
||||
userId?: string | null;
|
||||
action: string;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// src/services/db/budget.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ForeignKeyConstraintError, NotFoundError } from './errors.db';
|
||||
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
|
||||
import { ForeignKeyConstraintError } from './errors.db';
|
||||
|
||||
// Un-mock the module we are testing to ensure we use the real implementation.
|
||||
vi.unmock('./budget.db');
|
||||
|
||||
import { BudgetRepository } from './budget.db';
|
||||
import type { Pool, PoolClient } from 'pg';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import type { Budget, SpendingByCategory } from '../../types';
|
||||
|
||||
@@ -43,7 +44,7 @@ describe('Budget DB Service', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Instantiate the repository with the mock pool for each test
|
||||
budgetRepo = new BudgetRepository(mockPoolInstance as any);
|
||||
budgetRepo = new BudgetRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('getBudgetsForUser', () => {
|
||||
@@ -76,19 +77,22 @@ describe('Budget DB Service', () => {
|
||||
it('should execute an INSERT query and return the new budget', async () => {
|
||||
const budgetData = { name: 'Groceries', amount_cents: 50000, period: 'monthly' as const, start_date: '2024-01-01' };
|
||||
const mockCreatedBudget: Budget = { budget_id: 1, user_id: 'user-123', ...budgetData };
|
||||
|
||||
// Create a mock client that we can reference both inside and outside the transaction mock.
|
||||
const mockClient = { query: vi.fn() };
|
||||
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query
|
||||
// Configure the mock client's query behavior for this specific test.
|
||||
(mockClient.query as Mock)
|
||||
.mockResolvedValueOnce({ rows: [mockCreatedBudget] }) // For the INSERT...RETURNING
|
||||
.mockResolvedValueOnce({ rows: [] }); // For award_achievement
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const result = await budgetRepo.createBudget('user-123', budgetData, mockLogger);
|
||||
|
||||
// Now we can assert directly on the mockClient we created.
|
||||
const { GamificationRepository } = await import('./gamification.db');
|
||||
|
||||
const mockClient = (vi.mocked(withTransaction).mock.calls[0][0] as any).mock.instances[0];
|
||||
expect(mockClient.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.budgets'), expect.any(Array));
|
||||
expect(GamificationRepository.prototype.awardAchievement).toHaveBeenCalledWith('user-123', 'First Budget Created', mockLogger);
|
||||
expect(result).toEqual(mockCreatedBudget);
|
||||
@@ -98,12 +102,12 @@ describe('Budget DB Service', () => {
|
||||
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
|
||||
const budgetData = { name: 'Groceries', amount_cents: 50000, period: 'monthly' as const, start_date: '2024-01-01' };
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockRejectedValueOnce(dbError); // INSERT fails
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError; // Re-throw for the outer expect
|
||||
});
|
||||
|
||||
@@ -120,7 +124,7 @@ describe('Budget DB Service', () => {
|
||||
mockClient.query
|
||||
.mockResolvedValueOnce({ rows: [mockCreatedBudget] }) // INSERT...RETURNING
|
||||
.mockRejectedValueOnce(achievementError); // award_achievement fails
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(achievementError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(achievementError);
|
||||
throw achievementError; // Re-throw for the outer expect
|
||||
});
|
||||
|
||||
@@ -134,7 +138,7 @@ describe('Budget DB Service', () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockRejectedValueOnce(dbError); // INSERT fails
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError; // Re-throw for the outer expect
|
||||
});
|
||||
await expect(budgetRepo.createBudget('user-123', budgetData, mockLogger)).rejects.toThrow('Failed to create budget.');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// src/services/db/flyer.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import type { Pool, PoolClient } from 'pg';
|
||||
import { createMockFlyer, createMockFlyerItem, createMockBrand } from '../../tests/utils/mockFactories';
|
||||
|
||||
// Un-mock the module we are testing to ensure we use the real implementation
|
||||
@@ -32,10 +33,10 @@ describe('Flyer DB Service', () => {
|
||||
// In a transaction, `pool.connect()` returns a client. That client has a `release` method.
|
||||
// For these tests, we simulate this by having `connect` resolve to the pool instance itself,
|
||||
// and we ensure the `release` method is mocked on that instance.
|
||||
const mockClient = { ...mockPoolInstance, release: vi.fn() };
|
||||
vi.mocked(mockPoolInstance.connect).mockResolvedValue(mockClient as any);
|
||||
const mockClient = { ...mockPoolInstance, release: vi.fn() } as unknown as PoolClient;
|
||||
vi.mocked(mockPoolInstance.connect).mockResolvedValue(mockClient);
|
||||
|
||||
flyerRepo = new FlyerRepository(mockPoolInstance as any);
|
||||
flyerRepo = new FlyerRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('findOrCreateStore', () => {
|
||||
@@ -57,7 +58,7 @@ describe('Flyer DB Service', () => {
|
||||
|
||||
it('should handle race condition where store is created between SELECT and INSERT', async () => {
|
||||
const uniqueConstraintError = new Error('duplicate key value violates unique constraint');
|
||||
(uniqueConstraintError as any).code = '23505';
|
||||
(uniqueConstraintError as Error & { code: string }).code = '23505';
|
||||
|
||||
mockPoolInstance.query
|
||||
.mockResolvedValueOnce({ rows: [] }) // First SELECT finds nothing
|
||||
@@ -78,7 +79,7 @@ describe('Flyer DB Service', () => {
|
||||
|
||||
it('should throw an error if race condition recovery fails', async () => {
|
||||
const uniqueConstraintError = new Error('duplicate key value violates unique constraint');
|
||||
(uniqueConstraintError as any).code = '23505';
|
||||
(uniqueConstraintError as Error & { code: string }).code = '23505';
|
||||
|
||||
mockPoolInstance.query
|
||||
.mockResolvedValueOnce({ rows: [] }) // First SELECT
|
||||
@@ -131,7 +132,7 @@ describe('Flyer DB Service', () => {
|
||||
it('should throw UniqueConstraintError on duplicate checksum', async () => {
|
||||
const flyerData: FlyerDbInsert = { checksum: 'duplicate-checksum' } as FlyerDbInsert;
|
||||
const dbError = new Error('duplicate key value violates unique constraint "flyers_checksum_key"');
|
||||
(dbError as any).code = '23505';
|
||||
(dbError as Error & { code: string }).code = '23505';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
|
||||
await expect(flyerRepo.insertFlyer(flyerData, mockLogger)).rejects.toThrow(UniqueConstraintError);
|
||||
@@ -178,7 +179,7 @@ describe('Flyer DB Service', () => {
|
||||
it('should throw ForeignKeyConstraintError if flyerId is invalid', async () => {
|
||||
const itemsData: FlyerItemInsert[] = [{ item: 'Test', price_display: '$1', price_in_cents: 100, quantity: '1', category_name: 'Test', view_count: 0, click_count: 0 }];
|
||||
const dbError = new Error('insert or update on table "flyer_items" violates foreign key constraint "flyer_items_flyer_id_fkey"');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
|
||||
await expect(flyerRepo.insertFlyerItems(999, itemsData, mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
@@ -217,7 +218,7 @@ describe('Flyer DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [mockFlyer] }) // insertFlyer
|
||||
.mockResolvedValueOnce({ rows: mockItems }); // insertFlyerItems
|
||||
return callback(mockClient as any);
|
||||
});
|
||||
}); // Cast to any is acceptable here as we are mocking the implementation
|
||||
|
||||
const result = await createFlyerAndItems(flyerData, itemsData, mockLogger);
|
||||
|
||||
@@ -228,10 +229,10 @@ describe('Flyer DB Service', () => {
|
||||
expect(withTransaction).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Verify the individual functions were called with the client
|
||||
const callback = vi.mocked(withTransaction).mock.calls[0][0];
|
||||
const callback = (vi.mocked(withTransaction) as Mock).mock.calls[0][0];
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockResolvedValueOnce({ rows: [{ store_id: 1 }] }).mockResolvedValueOnce({ rows: [mockFlyer] }).mockResolvedValueOnce({ rows: mockItems });
|
||||
await callback(mockClient as any);
|
||||
await callback(mockClient as unknown as PoolClient);
|
||||
expect(mockClient.query).toHaveBeenCalledWith(expect.stringContaining('SELECT store_id FROM public.stores'), ['Transaction Store']);
|
||||
expect(mockClient.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO flyers'), expect.any(Array));
|
||||
expect(mockClient.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO flyer_items'), expect.any(Array));
|
||||
@@ -249,7 +250,7 @@ describe('Flyer DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [{ store_id: 1 }] }) // findOrCreateStore
|
||||
.mockRejectedValueOnce(dbError); // insertFlyer fails
|
||||
// The withTransaction helper will catch this and roll back
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
// re-throw because withTransaction re-throws
|
||||
throw dbError;
|
||||
});
|
||||
@@ -446,15 +447,16 @@ describe('Flyer DB Service', () => {
|
||||
|
||||
describe('deleteFlyer', () => {
|
||||
it('should use withTransaction to delete a flyer', async () => {
|
||||
// Create a mock client that we can reference both inside and outside the transaction mock.
|
||||
const mockClient = { query: vi.fn().mockResolvedValue({ rowCount: 1 }) };
|
||||
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn().mockResolvedValue({ rowCount: 1 }) };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
await flyerRepo.deleteFlyer(42, mockLogger);
|
||||
|
||||
expect(withTransaction).toHaveBeenCalledTimes(1);
|
||||
const mockClient = (vi.mocked(withTransaction).mock.calls[0][0] as any).mock.instances[0];
|
||||
expect(mockClient.query).toHaveBeenCalledWith('DELETE FROM public.flyers WHERE flyer_id = $1', [42]);
|
||||
});
|
||||
|
||||
@@ -462,7 +464,7 @@ describe('Flyer DB Service', () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn().mockResolvedValue({ rowCount: 0 }) };
|
||||
// The callback will throw NotFoundError, and withTransaction will re-throw it.
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(NotFoundError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(NotFoundError);
|
||||
throw new NotFoundError('Simulated re-throw');
|
||||
});
|
||||
|
||||
@@ -474,7 +476,7 @@ describe('Flyer DB Service', () => {
|
||||
const dbError = new Error('DB Error');
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn().mockRejectedValue(dbError) };
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// src/services/db/personalization.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import type { Pool, PoolClient } from 'pg';
|
||||
import { withTransaction } from './connection.db';
|
||||
import {
|
||||
PersonalizationRepository} from './personalization.db';
|
||||
@@ -37,10 +38,10 @@ describe('Personalization DB Service', () => {
|
||||
// Reset the withTransaction mock before each test
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
// Instantiate the repository with the mock pool for each test
|
||||
personalizationRepo = new PersonalizationRepository(mockPoolInstance as any);
|
||||
personalizationRepo = new PersonalizationRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('getAllMasterItems', () => {
|
||||
@@ -103,7 +104,7 @@ describe('Personalization DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [{ category_id: 1 }] }) // Find category
|
||||
.mockResolvedValueOnce({ rows: [mockItem] }) // Find master item
|
||||
.mockResolvedValueOnce({ rows: [] }); // Insert into watchlist
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
await personalizationRepo.addWatchedItem('user-123', 'New Item', 'Produce', mockLogger);
|
||||
@@ -124,7 +125,7 @@ describe('Personalization DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [] }) // Find master item (not found)
|
||||
.mockResolvedValueOnce({ rows: [mockNewItem] }) // INSERT new master item
|
||||
.mockResolvedValueOnce({ rows: [] }); // Insert into watchlist
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const result = await personalizationRepo.addWatchedItem('user-123', 'Brand New Item', 'Produce', mockLogger);
|
||||
@@ -142,7 +143,7 @@ describe('Personalization DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [{ category_id: 1 }] }) // Find category
|
||||
.mockResolvedValueOnce({ rows: [mockExistingItem] }) // Find master item
|
||||
.mockResolvedValueOnce({ rows: [] }); // INSERT...ON CONFLICT
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
// The function should resolve successfully without throwing an error.
|
||||
@@ -153,7 +154,7 @@ describe('Personalization DB Service', () => {
|
||||
it('should throw an error if the category is not found', async () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn().mockResolvedValue({ rows: [] }) };
|
||||
await expect(callback(mockClient as any)).rejects.toThrow("Category 'Fake Category' not found.");
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow("Category 'Fake Category' not found.");
|
||||
throw new Error("Category 'Fake Category' not found.");
|
||||
});
|
||||
|
||||
@@ -166,7 +167,7 @@ describe('Personalization DB Service', () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockResolvedValueOnce({ rows: [{ category_id: 1 }] }).mockRejectedValueOnce(dbError);
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
@@ -176,7 +177,7 @@ describe('Personalization DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError on invalid user or category', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
vi.mocked(withTransaction).mockRejectedValue(dbError);
|
||||
|
||||
await expect(personalizationRepo.addWatchedItem('non-existent-user', 'Some Item', 'Produce', mockLogger)).rejects.toThrow('The specified user or category does not exist.');
|
||||
@@ -358,7 +359,7 @@ describe('Personalization DB Service', () => {
|
||||
const mockClientQuery = vi.fn().mockResolvedValue({ rows: [] });
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: mockClientQuery };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
await personalizationRepo.setUserDietaryRestrictions('user-123', [1, 2], mockLogger);
|
||||
@@ -370,11 +371,11 @@ describe('Personalization DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if a restriction ID is invalid', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockResolvedValueOnce({ rows: [] }).mockRejectedValueOnce(dbError); // DELETE ok, INSERT fail
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
@@ -385,7 +386,7 @@ describe('Personalization DB Service', () => {
|
||||
const mockClientQuery = vi.fn().mockResolvedValue({ rows: [] });
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: mockClientQuery };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
await personalizationRepo.setUserDietaryRestrictions('user-123', [], mockLogger);
|
||||
@@ -455,7 +456,7 @@ describe('Personalization DB Service', () => {
|
||||
mockClientQuery
|
||||
.mockResolvedValueOnce({ rows: [] }) // DELETE
|
||||
.mockResolvedValueOnce({ rows: mockNewAppliances }); // INSERT
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const result = await personalizationRepo.setUserAppliances('user-123', [1, 2], mockLogger);
|
||||
@@ -468,11 +469,11 @@ describe('Personalization DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if an appliance ID is invalid', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockResolvedValueOnce({ rows: [] }).mockRejectedValueOnce(dbError); // DELETE ok, INSERT fail
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
@@ -483,7 +484,7 @@ describe('Personalization DB Service', () => {
|
||||
const mockClientQuery = vi.fn().mockResolvedValue({ rows: [] });
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: mockClientQuery };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const result = await personalizationRepo.setUserAppliances('user-123', [], mockLogger);
|
||||
@@ -499,7 +500,7 @@ describe('Personalization DB Service', () => {
|
||||
const dbError = new Error('DB Error');
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn().mockRejectedValue(dbError) };
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// src/services/db/recipe.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import type { Pool } from 'pg';
|
||||
import { RecipeRepository } from './recipe.db';
|
||||
|
||||
// Un-mock the module we are testing to ensure we use the real implementation.
|
||||
@@ -27,7 +28,7 @@ describe('Recipe DB Service', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Instantiate the repository with the mock pool for each test
|
||||
recipeRepo = new RecipeRepository(mockPoolInstance as any);
|
||||
recipeRepo = new RecipeRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('getRecipesBySalePercentage', () => {
|
||||
@@ -130,7 +131,7 @@ describe('Recipe DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if user or recipe does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockQuery.mockRejectedValue(dbError);
|
||||
await expect(recipeRepo.addFavoriteRecipe('user-123', 999, mockLogger)).rejects.toThrow('The specified user or recipe does not exist.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, userId: 'user-123', recipeId: 999 }, 'Database error in addFavoriteRecipe');
|
||||
@@ -284,7 +285,7 @@ describe('Recipe DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if recipe, user, or parent comment does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockQuery.mockRejectedValue(dbError);
|
||||
await expect(recipeRepo.addRecipeComment(999, 'user-123', 'Fail', mockLogger)).rejects.toThrow('The specified recipe, user, or parent comment does not exist.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, recipeId: 999, userId: 'user-123', parentCommentId: undefined }, 'Database error in addRecipeComment');
|
||||
@@ -310,7 +311,7 @@ describe('Recipe DB Service', () => {
|
||||
|
||||
it('should re-throw the specific error message from the database function', async () => {
|
||||
const dbError = new Error('Recipe is not public and cannot be forked.');
|
||||
(dbError as any).code = 'P0001'; // raise_exception
|
||||
(dbError as Error & { code: string }).code = 'P0001'; // raise_exception
|
||||
mockQuery.mockRejectedValue(dbError);
|
||||
|
||||
await expect(recipeRepo.forkRecipe('user-123', 1, mockLogger)).rejects.toThrow('Recipe is not public and cannot be forked.');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// src/services/db/shopping.db.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
|
||||
import type { Pool, PoolClient } from 'pg';
|
||||
import { withTransaction } from './connection.db';
|
||||
import { createMockShoppingList, createMockShoppingListItem } from '../../tests/utils/mockFactories';
|
||||
|
||||
@@ -8,7 +9,7 @@ import { createMockShoppingList, createMockShoppingListItem } from '../../tests/
|
||||
vi.unmock('./shopping.db');
|
||||
|
||||
import { ShoppingRepository } from './shopping.db';
|
||||
import { ForeignKeyConstraintError, UniqueConstraintError, NotFoundError } from './errors.db';
|
||||
import { ForeignKeyConstraintError, UniqueConstraintError } from './errors.db';
|
||||
|
||||
// Mock the logger to prevent console output during tests
|
||||
vi.mock('../logger.server', () => ({
|
||||
@@ -33,7 +34,7 @@ describe('Shopping DB Service', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Instantiate the repository with the mock pool for each test
|
||||
shoppingRepo = new ShoppingRepository(mockPoolInstance as any);
|
||||
shoppingRepo = new ShoppingRepository(mockPoolInstance as unknown as Pool);
|
||||
});
|
||||
|
||||
describe('getShoppingLists', () => {
|
||||
@@ -104,7 +105,7 @@ describe('Shopping DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
|
||||
const dbError = new Error('insert or update on table "shopping_lists" violates foreign key constraint "shopping_lists_user_id_fkey"');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(shoppingRepo.createShoppingList('non-existent-user', 'Wont work', mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
});
|
||||
@@ -174,7 +175,7 @@ describe('Shopping DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if list or master item does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(shoppingRepo.addShoppingListItem(999, { masterItemId: 999 }, mockLogger)).rejects.toThrow('Referenced list or item does not exist.');
|
||||
});
|
||||
@@ -263,7 +264,7 @@ describe('Shopping DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if the shopping list does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(shoppingRepo.completeShoppingList(999, 'user-123', mockLogger)).rejects.toThrow('The specified shopping list does not exist.');
|
||||
});
|
||||
@@ -356,14 +357,14 @@ describe('Shopping DB Service', () => {
|
||||
|
||||
it('should throw UniqueConstraintError on duplicate name', async () => {
|
||||
const dbError = new Error('duplicate key value violates unique constraint');
|
||||
(dbError as any).code = '23505';
|
||||
(dbError as Error & { code: string }).code = '23505';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(shoppingRepo.createPantryLocation('user-1', 'Fridge', mockLogger)).rejects.toThrow(UniqueConstraintError);
|
||||
});
|
||||
|
||||
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(shoppingRepo.createPantryLocation('non-existent-user', 'Pantry', mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
});
|
||||
@@ -410,7 +411,7 @@ describe('Shopping DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(shoppingRepo.createReceipt('non-existent-user', 'url', mockLogger)).rejects.toThrow('User not found');
|
||||
});
|
||||
@@ -451,7 +452,7 @@ describe('Shopping DB Service', () => {
|
||||
const mockClientQuery = vi.fn().mockResolvedValue({ rows: [] });
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: mockClientQuery };
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const items = [{ raw_item_description: 'Milk', price_paid_cents: 399 }];
|
||||
@@ -470,7 +471,7 @@ describe('Shopping DB Service', () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn().mockRejectedValue(dbError) };
|
||||
// The callback will throw, and withTransaction will catch and re-throw
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ export class ShoppingRepository {
|
||||
return { ...res.rows[0], items: [] };
|
||||
} catch (error) {
|
||||
// The patch requested this specific error handling.
|
||||
if ((error as any).code === '23503') {
|
||||
if (error instanceof Error && 'code' in error && error.code === '23503') {
|
||||
throw new ForeignKeyConstraintError('The specified user does not exist.');
|
||||
}
|
||||
logger.error({ err: error, userId, name }, 'Database error in createShoppingList');
|
||||
@@ -161,7 +161,7 @@ export class ShoppingRepository {
|
||||
return res.rows[0];
|
||||
} catch (error) {
|
||||
// The patch requested this specific error handling.
|
||||
if ((error as any).code === '23503') {
|
||||
if (error instanceof Error && 'code' in error && error.code === '23503') {
|
||||
throw new ForeignKeyConstraintError('Referenced list or item does not exist.');
|
||||
}
|
||||
logger.error({ err: error, listId, item }, 'Database error in addShoppingListItem');
|
||||
@@ -248,9 +248,9 @@ export class ShoppingRepository {
|
||||
);
|
||||
return res.rows[0];
|
||||
} catch (error) {
|
||||
if ((error as any).code === '23505') {
|
||||
if (error instanceof Error && 'code' in error && error.code === '23505') {
|
||||
throw new UniqueConstraintError('A pantry location with this name already exists.');
|
||||
} else if ((error as any).code === '23503') {
|
||||
} else if (error instanceof Error && 'code' in error && error.code === '23503') {
|
||||
throw new ForeignKeyConstraintError('User not found');
|
||||
}
|
||||
logger.error({ err: error, userId, name }, 'Database error in createPantryLocation');
|
||||
@@ -325,7 +325,7 @@ export class ShoppingRepository {
|
||||
return res.rows[0].complete_shopping_list;
|
||||
} catch (error) {
|
||||
// The patch requested this specific error handling.
|
||||
if ((error as any).code === '23503') {
|
||||
if (error instanceof Error && 'code' in error && error.code === '23503') {
|
||||
throw new ForeignKeyConstraintError('The specified shopping list does not exist.');
|
||||
}
|
||||
logger.error({ err: error, shoppingListId, userId }, 'Database error in completeShoppingList');
|
||||
@@ -388,7 +388,7 @@ export class ShoppingRepository {
|
||||
return res.rows[0];
|
||||
} catch (error) {
|
||||
// The patch requested this specific error handling.
|
||||
if ((error as any).code === '23503') {
|
||||
if (error instanceof Error && 'code' in error && error.code === '23503') {
|
||||
throw new ForeignKeyConstraintError('User not found');
|
||||
}
|
||||
logger.error({ err: error, userId, receiptImageUrl }, 'Database error in createReceipt');
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
// src/services/db/user.db.test.ts
|
||||
// --- FIX REGISTRY ---
|
||||
//
|
||||
// 2025-12-09: Corrected transaction rollback tests to expect generic error messages.
|
||||
// Updated `updateUserProfile` and `exportUserData` tests to use more robust
|
||||
// mocking strategies for internal method calls (spying on prototypes).
|
||||
//
|
||||
// --- END FIX REGISTRY ---
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { PoolClient } from 'pg';
|
||||
|
||||
@@ -60,9 +53,9 @@ describe('User DB Service', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
userRepo = new UserRepository(mockPoolInstance as any);
|
||||
userRepo = new UserRepository(mockPoolInstance as unknown as PoolClient);
|
||||
// Provide a default mock implementation for withTransaction for all tests.
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback: (client: PoolClient) => Promise<any>) => callback(mockPoolInstance as any));
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback: (client: PoolClient) => Promise<unknown>) => callback(mockPoolInstance as unknown as PoolClient));
|
||||
});
|
||||
|
||||
describe('findUserByEmail', () => {
|
||||
@@ -113,7 +106,7 @@ describe('User DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [] }) // set_config
|
||||
.mockResolvedValueOnce({ rows: [mockUser] }) // INSERT user
|
||||
.mockResolvedValueOnce({ rows: [mockDbProfile] }); // SELECT profile
|
||||
return callback(mockClient as any);
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const result = await userRepo.createUser('new@example.com', 'hashedpass', { full_name: 'New User' }, mockLogger);
|
||||
@@ -127,7 +120,7 @@ describe('User DB Service', () => {
|
||||
vi.mocked(withTransaction).mockImplementation(async (callback) => {
|
||||
const mockClient = { query: vi.fn() };
|
||||
mockClient.query.mockRejectedValueOnce(dbError); // set_config or INSERT fails
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
@@ -144,7 +137,7 @@ describe('User DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [] }) // set_config
|
||||
.mockResolvedValueOnce({ rows: [mockUser] }) // INSERT user
|
||||
.mockRejectedValueOnce(dbError); // SELECT profile fails
|
||||
await expect(callback(mockClient as any)).rejects.toThrow(dbError);
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(dbError);
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
@@ -154,7 +147,7 @@ describe('User DB Service', () => {
|
||||
|
||||
it('should throw UniqueConstraintError if the email already exists', async () => {
|
||||
const dbError = new Error('duplicate key value violates unique constraint');
|
||||
(dbError as any).code = '23505';
|
||||
(dbError as Error & { code: string }).code = '23505';
|
||||
|
||||
vi.mocked(withTransaction).mockRejectedValue(dbError);
|
||||
|
||||
@@ -417,7 +410,7 @@ describe('User DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.createPasswordResetToken('non-existent-user', 'hash', new Date(), mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
});
|
||||
@@ -471,12 +464,12 @@ describe('User DB Service', () => {
|
||||
const { ShoppingRepository } = await import('./shopping.db');
|
||||
const { PersonalizationRepository } = await import('./personalization.db');
|
||||
|
||||
const findProfileSpy = vi.spyOn(UserRepository.prototype, 'findUserProfileById')
|
||||
.mockResolvedValue({ user_id: '123' } as any);
|
||||
const getWatchedItemsSpy = vi.spyOn(PersonalizationRepository.prototype, 'getWatchedItems')
|
||||
.mockResolvedValue([]);
|
||||
const getShoppingListsSpy = vi.spyOn(ShoppingRepository.prototype, 'getShoppingLists')
|
||||
.mockResolvedValue([]);
|
||||
const findProfileSpy = vi.spyOn(UserRepository.prototype, 'findUserProfileById');
|
||||
findProfileSpy.mockResolvedValue({ user_id: '123' } as Profile);
|
||||
const getWatchedItemsSpy = vi.spyOn(PersonalizationRepository.prototype, 'getWatchedItems');
|
||||
getWatchedItemsSpy.mockResolvedValue([]);
|
||||
const getShoppingListsSpy = vi.spyOn(ShoppingRepository.prototype, 'getShoppingLists');
|
||||
getShoppingListsSpy.mockResolvedValue([]);
|
||||
|
||||
await exportUserData('123', mockLogger);
|
||||
|
||||
@@ -522,7 +515,7 @@ describe('User DB Service', () => {
|
||||
|
||||
it('should throw ForeignKeyConstraintError if a user does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as any).code = '23503';
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.followUser('follower-1', 'non-existent-user', mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
});
|
||||
|
||||
@@ -452,9 +452,6 @@ export class UserRepository {
|
||||
[followerId, followingId]
|
||||
);
|
||||
} catch (error) {
|
||||
if ((error as any).code === '23503') {
|
||||
throw new ForeignKeyConstraintError('User not found.');
|
||||
}
|
||||
if (error instanceof Error && 'code' in error && error.code === '23503') {
|
||||
throw new ForeignKeyConstraintError('One or both users do not exist.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user