more fixin tests
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
This commit is contained in:
@@ -33,13 +33,19 @@ import type { Profile, ActivityLogItem, SearchQuery, UserProfile } from '../../t
|
||||
// Update mocks to put methods on prototype so spyOn works in exportUserData tests
|
||||
vi.mock('./shopping.db', () => ({
|
||||
ShoppingRepository: class {
|
||||
getShoppingLists() { return Promise.resolve([]); }
|
||||
createShoppingList() { return Promise.resolve({}); }
|
||||
getShoppingLists() {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
createShoppingList() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
},
|
||||
}));
|
||||
vi.mock('./personalization.db', () => ({
|
||||
PersonalizationRepository: class {
|
||||
getWatchedItems() { return Promise.resolve([]); }
|
||||
getWatchedItems() {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -56,7 +62,10 @@ describe('User DB Service', () => {
|
||||
vi.clearAllMocks();
|
||||
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<unknown>) => callback(mockPoolInstance as unknown as PoolClient));
|
||||
vi.mocked(withTransaction).mockImplementation(
|
||||
async (callback: (client: PoolClient) => Promise<unknown>) =>
|
||||
callback(mockPoolInstance as unknown as PoolClient),
|
||||
);
|
||||
});
|
||||
|
||||
describe('findUserByEmail', () => {
|
||||
@@ -66,22 +75,33 @@ describe('User DB Service', () => {
|
||||
|
||||
const result = await userRepo.findUserByEmail('test@example.com', mockLogger);
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('FROM public.users WHERE email = $1'), ['test@example.com']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('FROM public.users WHERE email = $1'),
|
||||
['test@example.com'],
|
||||
);
|
||||
expect(result).toEqual(mockUser);
|
||||
});
|
||||
|
||||
it('should return undefined if user is not found', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
const result = await userRepo.findUserByEmail('notfound@example.com', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('FROM public.users WHERE email = $1'), ['notfound@example.com']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('FROM public.users WHERE email = $1'),
|
||||
['notfound@example.com'],
|
||||
);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Connection Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.findUserByEmail('test@example.com', mockLogger)).rejects.toThrow('Failed to retrieve user from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, email: 'test@example.com' }, 'Database error in findUserByEmail');
|
||||
await expect(userRepo.findUserByEmail('test@example.com', mockLogger)).rejects.toThrow(
|
||||
'Failed to retrieve user from database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, email: 'test@example.com' },
|
||||
'Database error in findUserByEmail',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,8 +111,15 @@ describe('User DB Service', () => {
|
||||
const now = new Date().toISOString();
|
||||
// This is the flat structure returned by the DB query inside createUser
|
||||
const mockDbProfile = {
|
||||
user_id: 'new-user-id', email: 'new@example.com', role: 'user', full_name: 'New User',
|
||||
avatar_url: null, points: 0, preferences: null, created_at: now, updated_at: now
|
||||
user_id: 'new-user-id',
|
||||
email: 'new@example.com',
|
||||
role: 'user',
|
||||
full_name: 'New User',
|
||||
avatar_url: null,
|
||||
points: 0,
|
||||
preferences: null,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
// This is the nested structure the function is expected to return
|
||||
const expectedProfile: UserProfile = {
|
||||
@@ -115,15 +142,26 @@ describe('User DB Service', () => {
|
||||
return callback(mockClient as unknown as PoolClient);
|
||||
});
|
||||
|
||||
const result = await userRepo.createUser('new@example.com', 'hashedpass', { full_name: 'New User' }, mockLogger);
|
||||
const result = await userRepo.createUser(
|
||||
'new@example.com',
|
||||
'hashedpass',
|
||||
{ full_name: 'New User' },
|
||||
mockLogger,
|
||||
);
|
||||
|
||||
console.log('[TEST DEBUG] createUser - Result from function:', JSON.stringify(result, null, 2));
|
||||
console.log('[TEST DEBUG] createUser - Expected result:', JSON.stringify(expectedProfile, null, 2));
|
||||
console.log(
|
||||
'[TEST DEBUG] createUser - Result from function:',
|
||||
JSON.stringify(result, null, 2),
|
||||
);
|
||||
console.log(
|
||||
'[TEST DEBUG] createUser - Expected result:',
|
||||
JSON.stringify(expectedProfile, null, 2),
|
||||
);
|
||||
|
||||
// Use objectContaining because the real implementation might have other DB-generated fields.
|
||||
expect(result).toEqual(expect.objectContaining(expectedProfile));
|
||||
expect(withTransaction).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should rollback the transaction if creating the user fails', async () => {
|
||||
const dbError = new Error('User insert failed');
|
||||
@@ -134,8 +172,13 @@ describe('User DB Service', () => {
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
await expect(userRepo.createUser('fail@example.com', 'badpass', {}, mockLogger)).rejects.toThrow('Failed to create user in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, email: 'fail@example.com' }, 'Error during createUser transaction');
|
||||
await expect(
|
||||
userRepo.createUser('fail@example.com', 'badpass', {}, mockLogger),
|
||||
).rejects.toThrow('Failed to create user in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, email: 'fail@example.com' },
|
||||
'Error during createUser transaction',
|
||||
);
|
||||
});
|
||||
|
||||
it('should rollback the transaction if fetching the final profile fails', async () => {
|
||||
@@ -151,8 +194,13 @@ describe('User DB Service', () => {
|
||||
throw dbError;
|
||||
});
|
||||
|
||||
await expect(userRepo.createUser('fail@example.com', 'pass', {}, mockLogger)).rejects.toThrow('Failed to create user in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, email: 'fail@example.com' }, 'Error during createUser transaction');
|
||||
await expect(userRepo.createUser('fail@example.com', 'pass', {}, mockLogger)).rejects.toThrow(
|
||||
'Failed to create user in database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, email: 'fail@example.com' },
|
||||
'Error during createUser transaction',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw UniqueConstraintError if the email already exists', async () => {
|
||||
@@ -174,7 +222,9 @@ describe('User DB Service', () => {
|
||||
}
|
||||
|
||||
expect(withTransaction).toHaveBeenCalledTimes(1);
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith(`Attempted to create a user with an existing email: exists@example.com`);
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith(
|
||||
`Attempted to create a user with an existing email: exists@example.com`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if profile is not found after user creation', async () => {
|
||||
@@ -187,12 +237,19 @@ describe('User DB Service', () => {
|
||||
.mockResolvedValueOnce({ rows: [mockUser] }) // INSERT user succeeds
|
||||
.mockResolvedValueOnce({ rows: [] }); // SELECT profile returns nothing
|
||||
// The callback will throw, which is caught and re-thrown by withTransaction
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow('Failed to create or retrieve user profile after registration.');
|
||||
await expect(callback(mockClient as unknown as PoolClient)).rejects.toThrow(
|
||||
'Failed to create or retrieve user profile after registration.',
|
||||
);
|
||||
throw new Error('Internal failure'); // Simulate re-throw from withTransaction
|
||||
});
|
||||
|
||||
await expect(userRepo.createUser('no-profile@example.com', 'pass', {}, mockLogger)).rejects.toThrow('Failed to create user in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), email: 'no-profile@example.com' }, 'Error during createUser transaction');
|
||||
await expect(
|
||||
userRepo.createUser('no-profile@example.com', 'pass', {}, mockLogger),
|
||||
).rejects.toThrow('Failed to create user in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), email: 'no-profile@example.com' },
|
||||
'Error during createUser transaction',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,7 +275,6 @@ describe('User DB Service', () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [mockDbResult] });
|
||||
|
||||
const expectedResult = {
|
||||
user_id: '123',
|
||||
full_name: 'Test User',
|
||||
avatar_url: null,
|
||||
role: 'user',
|
||||
@@ -228,7 +284,6 @@ describe('User DB Service', () => {
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
user: { user_id: '123', email: 'test@example.com' },
|
||||
email: 'test@example.com',
|
||||
password_hash: 'hash',
|
||||
failed_login_attempts: 0,
|
||||
last_failed_login: null,
|
||||
@@ -237,10 +292,19 @@ describe('User DB Service', () => {
|
||||
|
||||
const result = await userRepo.findUserWithProfileByEmail('test@example.com', mockLogger);
|
||||
|
||||
console.log('[TEST DEBUG] findUserWithProfileByEmail - Result from function:', JSON.stringify(result, null, 2));
|
||||
console.log('[TEST DEBUG] findUserWithProfileByEmail - Expected result:', JSON.stringify(expectedResult, null, 2));
|
||||
console.log(
|
||||
'[TEST DEBUG] findUserWithProfileByEmail - Result from function:',
|
||||
JSON.stringify(result, null, 2),
|
||||
);
|
||||
console.log(
|
||||
'[TEST DEBUG] findUserWithProfileByEmail - Expected result:',
|
||||
JSON.stringify(expectedResult, null, 2),
|
||||
);
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('JOIN public.profiles'), ['test@example.com']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('JOIN public.profiles'),
|
||||
['test@example.com'],
|
||||
);
|
||||
expect(result).toEqual(expect.objectContaining(expectedResult));
|
||||
});
|
||||
|
||||
@@ -253,8 +317,13 @@ describe('User DB Service', () => {
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Connection Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.findUserWithProfileByEmail('test@example.com', mockLogger)).rejects.toThrow('Failed to retrieve user with profile from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, email: 'test@example.com' }, 'Database error in findUserWithProfileByEmail');
|
||||
await expect(
|
||||
userRepo.findUserWithProfileByEmail('test@example.com', mockLogger),
|
||||
).rejects.toThrow('Failed to retrieve user with profile from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, email: 'test@example.com' },
|
||||
'Database error in findUserWithProfileByEmail',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -262,40 +331,65 @@ describe('User DB Service', () => {
|
||||
it('should query for a user by their ID', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }], rowCount: 1 });
|
||||
await userRepo.findUserById('123', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('FROM public.users WHERE user_id = $1'), ['123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('FROM public.users WHERE user_id = $1'),
|
||||
['123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw NotFoundError if user is not found', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [], rowCount: 0 });
|
||||
await expect(userRepo.findUserById('not-found-id', mockLogger)).rejects.toThrow('User with ID not-found-id not found.');
|
||||
await expect(userRepo.findUserById('not-found-id', mockLogger)).rejects.toThrow(
|
||||
'User with ID not-found-id not found.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Connection Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.findUserById('123', mockLogger)).rejects.toThrow('Failed to retrieve user by ID from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, userId: '123' }, 'Database error in findUserById');
|
||||
await expect(userRepo.findUserById('123', mockLogger)).rejects.toThrow(
|
||||
'Failed to retrieve user by ID from database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, userId: '123' },
|
||||
'Database error in findUserById',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('findUserWithPasswordHashById', () => {
|
||||
it('should query for a user and their password hash by ID', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123', password_hash: 'hash' }], rowCount: 1 });
|
||||
mockPoolInstance.query.mockResolvedValue({
|
||||
rows: [{ user_id: '123', password_hash: 'hash' }],
|
||||
rowCount: 1,
|
||||
});
|
||||
await userRepo.findUserWithPasswordHashById('123', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('SELECT user_id, email, password_hash'), ['123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('SELECT user_id, email, password_hash'),
|
||||
['123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw NotFoundError if user is not found', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [], rowCount: 0 });
|
||||
await expect(userRepo.findUserWithPasswordHashById('not-found-id', mockLogger)).rejects.toThrow(NotFoundError);
|
||||
await expect(userRepo.findUserWithPasswordHashById('not-found-id', mockLogger)).rejects.toThrow('User with ID not-found-id not found.');
|
||||
await expect(
|
||||
userRepo.findUserWithPasswordHashById('not-found-id', mockLogger),
|
||||
).rejects.toThrow(NotFoundError);
|
||||
await expect(
|
||||
userRepo.findUserWithPasswordHashById('not-found-id', mockLogger),
|
||||
).rejects.toThrow('User with ID not-found-id not found.');
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Connection Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.findUserWithPasswordHashById('123', mockLogger)).rejects.toThrow('Failed to retrieve user with sensitive data by ID from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, userId: '123' }, 'Database error in findUserWithPasswordHashById');
|
||||
await expect(userRepo.findUserWithPasswordHashById('123', mockLogger)).rejects.toThrow(
|
||||
'Failed to retrieve user with sensitive data by ID from database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, userId: '123' },
|
||||
'Database error in findUserWithPasswordHashById',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -304,52 +398,92 @@ describe('User DB Service', () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }] });
|
||||
await userRepo.findUserProfileById('123', mockLogger);
|
||||
// The actual query uses 'p.user_id' due to the join alias
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('WHERE p.user_id = $1'), ['123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('WHERE p.user_id = $1'),
|
||||
['123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw NotFoundError if user profile is not found', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [], rowCount: 0 });
|
||||
await expect(userRepo.findUserProfileById('not-found-id', mockLogger)).rejects.toThrow('Profile not found for this user.');
|
||||
await expect(userRepo.findUserProfileById('not-found-id', mockLogger)).rejects.toThrow(
|
||||
'Profile not found for this user.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Connection Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.findUserProfileById('123', mockLogger)).rejects.toThrow('Failed to retrieve user profile from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, userId: '123' }, 'Database error in findUserProfileById');
|
||||
await expect(userRepo.findUserProfileById('123', mockLogger)).rejects.toThrow(
|
||||
'Failed to retrieve user profile from database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, userId: '123' },
|
||||
'Database error in findUserProfileById',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateUserProfile', () => {
|
||||
it('should execute an UPDATE query for the user profile', async () => {
|
||||
const mockProfile: Profile = { full_name: 'Updated Name', role: 'user', points: 0, created_at: new Date().toISOString(), updated_at: new Date().toISOString() };
|
||||
const mockProfile: Profile = {
|
||||
full_name: 'Updated Name',
|
||||
role: 'user',
|
||||
points: 0,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [mockProfile] });
|
||||
|
||||
await userRepo.updateUserProfile('123', { full_name: 'Updated Name' }, mockLogger);
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.profiles'), expect.any(Array));
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('UPDATE public.profiles'),
|
||||
expect.any(Array),
|
||||
);
|
||||
});
|
||||
|
||||
it('should execute an UPDATE query for avatar_url', async () => {
|
||||
const mockProfile: Profile = { avatar_url: 'new-avatar.png', role: 'user', points: 0, created_at: new Date().toISOString(), updated_at: new Date().toISOString() };
|
||||
const mockProfile: Profile = {
|
||||
avatar_url: 'new-avatar.png',
|
||||
role: 'user',
|
||||
points: 0,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [mockProfile] });
|
||||
|
||||
await userRepo.updateUserProfile('123', { avatar_url: 'new-avatar.png' }, mockLogger);
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('avatar_url = $1'), ['new-avatar.png', '123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('avatar_url = $1'),
|
||||
['new-avatar.png', '123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should execute an UPDATE query for address_id', async () => {
|
||||
const mockProfile: Profile = { address_id: 99, role: 'user', points: 0, created_at: new Date().toISOString(), updated_at: new Date().toISOString() };
|
||||
const mockProfile: Profile = {
|
||||
address_id: 99,
|
||||
role: 'user',
|
||||
points: 0,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [mockProfile] });
|
||||
|
||||
await userRepo.updateUserProfile('123', { address_id: 99 }, mockLogger);
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('address_id = $1'), [99, '123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('address_id = $1'),
|
||||
[99, '123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch the current profile if no update fields are provided', async () => {
|
||||
const mockProfile: Profile = createMockUserProfile({ user: { user_id: '123', email: '123@example.com' }, full_name: 'Current Name' });
|
||||
const mockProfile: Profile = createMockUserProfile({
|
||||
user: { user_id: '123', email: '123@example.com' },
|
||||
full_name: 'Current Name',
|
||||
});
|
||||
// FIX: Instead of mocking `mockResolvedValue` on the instance method which might fail if not spied correctly,
|
||||
// we mock the underlying `db.query` call that `findUserProfileById` makes.
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [mockProfile] });
|
||||
@@ -357,20 +491,30 @@ describe('User DB Service', () => {
|
||||
const result = await userRepo.updateUserProfile('123', { full_name: undefined }, mockLogger);
|
||||
|
||||
// Check that it calls query for finding profile (since no updates were made)
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('SELECT'), expect.any(Array));
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('SELECT'),
|
||||
expect.any(Array),
|
||||
);
|
||||
expect(result).toEqual(mockProfile);
|
||||
});
|
||||
|
||||
it('should throw an error if the user to update is not found', async () => {
|
||||
// Simulate the DB returning 0 rows affected
|
||||
mockPoolInstance.query.mockResolvedValue({ rowCount: 0, rows: [] });
|
||||
await expect(userRepo.updateUserProfile('999', { full_name: 'Fail' }, mockLogger)).rejects.toThrow('User not found or user does not have permission to update.');
|
||||
await expect(
|
||||
userRepo.updateUserProfile('999', { full_name: 'Fail' }, mockLogger),
|
||||
).rejects.toThrow('User not found or user does not have permission to update.');
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await expect(userRepo.updateUserProfile('123', { full_name: 'Fail' }, mockLogger)).rejects.toThrow('Failed to update user profile in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), userId: '123', profileData: { full_name: 'Fail' } }, 'Database error in updateUserProfile');
|
||||
await expect(
|
||||
userRepo.updateUserProfile('123', { full_name: 'Fail' }, mockLogger),
|
||||
).rejects.toThrow('Failed to update user profile in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), userId: '123', profileData: { full_name: 'Fail' } },
|
||||
'Database error in updateUserProfile',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -378,19 +522,29 @@ describe('User DB Service', () => {
|
||||
it('should execute an UPDATE query for user preferences', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [{}] });
|
||||
await userRepo.updateUserPreferences('123', { darkMode: true }, mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining("SET preferences = COALESCE(preferences, '{}'::jsonb) || $1"), [{ darkMode: true }, '123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining("SET preferences = COALESCE(preferences, '{}'::jsonb) || $1"),
|
||||
[{ darkMode: true }, '123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the user to update is not found', async () => {
|
||||
// Simulate the DB returning 0 rows affected
|
||||
mockPoolInstance.query.mockResolvedValue({ rowCount: 0, rows: [] });
|
||||
await expect(userRepo.updateUserPreferences('999', { darkMode: true }, mockLogger)).rejects.toThrow('User not found or user does not have permission to update.');
|
||||
await expect(
|
||||
userRepo.updateUserPreferences('999', { darkMode: true }, mockLogger),
|
||||
).rejects.toThrow('User not found or user does not have permission to update.');
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await expect(userRepo.updateUserPreferences('123', { darkMode: true }, mockLogger)).rejects.toThrow('Failed to update user preferences in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), userId: '123', preferences: { darkMode: true } }, 'Database error in updateUserPreferences');
|
||||
await expect(
|
||||
userRepo.updateUserPreferences('123', { darkMode: true }, mockLogger),
|
||||
).rejects.toThrow('Failed to update user preferences in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), userId: '123', preferences: { darkMode: true } },
|
||||
'Database error in updateUserPreferences',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -398,13 +552,21 @@ describe('User DB Service', () => {
|
||||
it('should execute an UPDATE query for the user password', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
await userRepo.updateUserPassword('123', 'newhash', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith('UPDATE public.users SET password_hash = $1 WHERE user_id = $2', ['newhash', '123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'UPDATE public.users SET password_hash = $1 WHERE user_id = $2',
|
||||
['newhash', '123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await expect(userRepo.updateUserPassword('123', 'newhash', mockLogger)).rejects.toThrow('Failed to update user password in database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), userId: '123' }, 'Database error in updateUserPassword');
|
||||
await expect(userRepo.updateUserPassword('123', 'newhash', mockLogger)).rejects.toThrow(
|
||||
'Failed to update user password in database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), userId: '123' },
|
||||
'Database error in updateUserPassword',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -412,13 +574,21 @@ describe('User DB Service', () => {
|
||||
it('should execute a DELETE query for the user', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
await userRepo.deleteUserById('123', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.users WHERE user_id = $1', ['123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'DELETE FROM public.users WHERE user_id = $1',
|
||||
['123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await expect(userRepo.deleteUserById('123', mockLogger)).rejects.toThrow('Failed to delete user from database.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), userId: '123' }, 'Database error in deleteUserById');
|
||||
await expect(userRepo.deleteUserById('123', mockLogger)).rejects.toThrow(
|
||||
'Failed to delete user from database.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), userId: '123' },
|
||||
'Database error in deleteUserById',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -426,13 +596,21 @@ describe('User DB Service', () => {
|
||||
it('should execute an UPDATE query to save the refresh token', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
await userRepo.saveRefreshToken('123', 'new-token', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith('UPDATE public.users SET refresh_token = $1 WHERE user_id = $2', ['new-token', '123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'UPDATE public.users SET refresh_token = $1 WHERE user_id = $2',
|
||||
['new-token', '123'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await expect(userRepo.saveRefreshToken('123', 'new-token', mockLogger)).rejects.toThrow('Failed to save refresh token.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), userId: '123' }, 'Database error in saveRefreshToken');
|
||||
await expect(userRepo.saveRefreshToken('123', 'new-token', mockLogger)).rejects.toThrow(
|
||||
'Failed to save refresh token.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), userId: '123' },
|
||||
'Database error in saveRefreshToken',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -440,22 +618,34 @@ describe('User DB Service', () => {
|
||||
it('should query for a user by their refresh token', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }], rowCount: 1 });
|
||||
await userRepo.findUserByRefreshToken('a-token', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('WHERE refresh_token = $1'), ['a-token']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('WHERE refresh_token = $1'),
|
||||
['a-token'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw NotFoundError if token is not found', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [], rowCount: 0 });
|
||||
await expect(userRepo.findUserByRefreshToken('a-token', mockLogger)).rejects.toThrow(NotFoundError);
|
||||
await expect(userRepo.findUserByRefreshToken('a-token', mockLogger)).rejects.toThrow('User not found for the given refresh token.');
|
||||
await expect(userRepo.findUserByRefreshToken('a-token', mockLogger)).rejects.toThrow(
|
||||
NotFoundError,
|
||||
);
|
||||
await expect(userRepo.findUserByRefreshToken('a-token', mockLogger)).rejects.toThrow(
|
||||
'User not found for the given refresh token.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
|
||||
await expect(userRepo.findUserByRefreshToken('a-token', mockLogger)).rejects.toThrow('Failed to find user by refresh token.');
|
||||
await expect(userRepo.findUserByRefreshToken('a-token', mockLogger)).rejects.toThrow(
|
||||
'Failed to find user by refresh token.',
|
||||
);
|
||||
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError }, 'Database error in findUserByRefreshToken');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError },
|
||||
'Database error in findUserByRefreshToken',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -464,7 +654,8 @@ describe('User DB Service', () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
await userRepo.deleteRefreshToken('a-token', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'UPDATE public.users SET refresh_token = NULL WHERE refresh_token = $1', ['a-token']
|
||||
'UPDATE public.users SET refresh_token = NULL WHERE refresh_token = $1',
|
||||
['a-token'],
|
||||
);
|
||||
});
|
||||
|
||||
@@ -474,7 +665,10 @@ describe('User DB Service', () => {
|
||||
await expect(userRepo.deleteRefreshToken('a-token', mockLogger)).resolves.toBeUndefined();
|
||||
// We can still check that the query was attempted.
|
||||
expect(mockPoolInstance.query).toHaveBeenCalled();
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error) }, 'Database error in deleteRefreshToken');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error) },
|
||||
'Database error in deleteRefreshToken',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -483,23 +677,36 @@ describe('User DB Service', () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
const expires = new Date();
|
||||
await userRepo.createPasswordResetToken('123', 'token-hash', expires, mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.password_reset_tokens WHERE user_id = $1', ['123']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.password_reset_tokens'), ['123', 'token-hash', expires]);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'DELETE FROM public.password_reset_tokens WHERE user_id = $1',
|
||||
['123'],
|
||||
);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('INSERT INTO public.password_reset_tokens'),
|
||||
['123', 'token-hash', expires],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(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);
|
||||
await expect(
|
||||
userRepo.createPasswordResetToken('non-existent-user', 'hash', new Date(), mockLogger),
|
||||
).rejects.toThrow(ForeignKeyConstraintError);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
const expires = new Date();
|
||||
await expect(userRepo.createPasswordResetToken('123', 'token-hash', expires, mockLogger)).rejects.toThrow('Failed to create password reset token.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, userId: '123' }, 'Database error in createPasswordResetToken');
|
||||
await expect(
|
||||
userRepo.createPasswordResetToken('123', 'token-hash', expires, mockLogger),
|
||||
).rejects.toThrow('Failed to create password reset token.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, userId: '123' },
|
||||
'Database error in createPasswordResetToken',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -507,13 +714,20 @@ describe('User DB Service', () => {
|
||||
it('should query for tokens where expires_at > NOW()', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
await userRepo.getValidResetTokens(mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('WHERE expires_at > NOW()'));
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('WHERE expires_at > NOW()'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await expect(userRepo.getValidResetTokens(mockLogger)).rejects.toThrow('Failed to retrieve valid reset tokens.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error) }, 'Database error in getValidResetTokens');
|
||||
await expect(userRepo.getValidResetTokens(mockLogger)).rejects.toThrow(
|
||||
'Failed to retrieve valid reset tokens.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error) },
|
||||
'Database error in getValidResetTokens',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -521,13 +735,19 @@ describe('User DB Service', () => {
|
||||
it('should execute a DELETE query for the token hash', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rows: [] });
|
||||
await userRepo.deleteResetToken('token-hash', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.password_reset_tokens WHERE token_hash = $1', ['token-hash']);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'DELETE FROM public.password_reset_tokens WHERE token_hash = $1',
|
||||
['token-hash'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should log an error if the database query fails', async () => {
|
||||
mockPoolInstance.query.mockRejectedValue(new Error('DB Error'));
|
||||
await userRepo.deleteResetToken('token-hash', mockLogger);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: expect.any(Error), tokenHash: 'token-hash' }, 'Database error in deleteResetToken');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Error), tokenHash: 'token-hash' },
|
||||
'Database error in deleteResetToken',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -535,16 +755,25 @@ describe('User DB Service', () => {
|
||||
it('should execute a DELETE query for expired tokens and return the count', async () => {
|
||||
mockPoolInstance.query.mockResolvedValue({ rowCount: 5 });
|
||||
const result = await userRepo.deleteExpiredResetTokens(mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.password_reset_tokens WHERE expires_at < NOW()');
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'DELETE FROM public.password_reset_tokens WHERE expires_at < NOW()',
|
||||
);
|
||||
expect(result).toBe(5);
|
||||
expect(mockLogger.info).toHaveBeenCalledWith('[DB deleteExpiredResetTokens] Deleted 5 expired password reset tokens.');
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||
'[DB deleteExpiredResetTokens] Deleted 5 expired password reset tokens.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.deleteExpiredResetTokens(mockLogger)).rejects.toThrow('Failed to delete expired password reset tokens.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError }, 'Database error in deleteExpiredResetTokens');
|
||||
await expect(userRepo.deleteExpiredResetTokens(mockLogger)).rejects.toThrow(
|
||||
'Failed to delete expired password reset tokens.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError },
|
||||
'Database error in deleteExpiredResetTokens',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -562,7 +791,9 @@ describe('User DB Service', () => {
|
||||
const { PersonalizationRepository } = await import('./personalization.db');
|
||||
|
||||
const findProfileSpy = vi.spyOn(UserRepository.prototype, 'findUserProfileById');
|
||||
findProfileSpy.mockResolvedValue(createMockUserProfile({ user: { user_id: '123', email: '123@example.com' } }));
|
||||
findProfileSpy.mockResolvedValue(
|
||||
createMockUserProfile({ user: { user_id: '123', email: '123@example.com' } }),
|
||||
);
|
||||
const getWatchedItemsSpy = vi.spyOn(PersonalizationRepository.prototype, 'getWatchedItems');
|
||||
getWatchedItemsSpy.mockResolvedValue([]);
|
||||
const getShoppingListsSpy = vi.spyOn(ShoppingRepository.prototype, 'getShoppingLists');
|
||||
@@ -583,19 +814,27 @@ describe('User DB Service', () => {
|
||||
// Arrange: Mock findUserProfileById to throw a NotFoundError, as per its contract (ADR-001).
|
||||
// The exportUserData function will catch this and re-throw a generic error.
|
||||
const { NotFoundError } = await import('./errors.db');
|
||||
vi.spyOn(UserRepository.prototype, 'findUserProfileById').mockRejectedValue(new NotFoundError('Profile not found'));
|
||||
vi.spyOn(UserRepository.prototype, 'findUserProfileById').mockRejectedValue(
|
||||
new NotFoundError('Profile not found'),
|
||||
);
|
||||
|
||||
// Act & Assert: The outer function catches the NotFoundError and re-throws it.
|
||||
await expect(exportUserData('123', mockLogger)).rejects.toThrow('Failed to export user data.');
|
||||
await expect(exportUserData('123', mockLogger)).rejects.toThrow(
|
||||
'Failed to export user data.',
|
||||
);
|
||||
expect(withTransaction).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should throw an error if the database query fails', async () => {
|
||||
// Arrange: Force a failure in one of the parallel calls
|
||||
vi.spyOn(UserRepository.prototype, 'findUserProfileById').mockRejectedValue(new Error('DB Error'));
|
||||
// Arrange: Force a failure in one of the parallel calls
|
||||
vi.spyOn(UserRepository.prototype, 'findUserProfileById').mockRejectedValue(
|
||||
new Error('DB Error'),
|
||||
);
|
||||
|
||||
// Act & Assert
|
||||
await expect(exportUserData('123', mockLogger)).rejects.toThrow('Failed to export user data.');
|
||||
await expect(exportUserData('123', mockLogger)).rejects.toThrow(
|
||||
'Failed to export user data.',
|
||||
);
|
||||
expect(withTransaction).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -606,7 +845,7 @@ describe('User DB Service', () => {
|
||||
await userRepo.followUser('follower-1', 'following-1', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'INSERT INTO public.user_follows (follower_id, following_id) VALUES ($1, $2) ON CONFLICT (follower_id, following_id) DO NOTHING',
|
||||
['follower-1', 'following-1']
|
||||
['follower-1', 'following-1'],
|
||||
);
|
||||
});
|
||||
|
||||
@@ -614,14 +853,21 @@ describe('User DB Service', () => {
|
||||
const dbError = new Error('violates foreign key constraint');
|
||||
(dbError as Error & { code: string }).code = '23503';
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.followUser('follower-1', 'non-existent-user', mockLogger)).rejects.toThrow(ForeignKeyConstraintError);
|
||||
await expect(
|
||||
userRepo.followUser('follower-1', 'non-existent-user', mockLogger),
|
||||
).rejects.toThrow(ForeignKeyConstraintError);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.followUser('follower-1', 'following-1', mockLogger)).rejects.toThrow('Failed to follow user.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, followerId: 'follower-1', followingId: 'following-1' }, 'Database error in followUser');
|
||||
await expect(userRepo.followUser('follower-1', 'following-1', mockLogger)).rejects.toThrow(
|
||||
'Failed to follow user.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, followerId: 'follower-1', followingId: 'following-1' },
|
||||
'Database error in followUser',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -631,15 +877,20 @@ describe('User DB Service', () => {
|
||||
await userRepo.unfollowUser('follower-1', 'following-1', mockLogger);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'DELETE FROM public.user_follows WHERE follower_id = $1 AND following_id = $2',
|
||||
['follower-1', 'following-1']
|
||||
['follower-1', 'following-1'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.unfollowUser('follower-1', 'following-1', mockLogger)).rejects.toThrow('Failed to unfollow user.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, followerId: 'follower-1', followingId: 'following-1' }, 'Database error in unfollowUser');
|
||||
await expect(userRepo.unfollowUser('follower-1', 'following-1', mockLogger)).rejects.toThrow(
|
||||
'Failed to unfollow user.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, followerId: 'follower-1', followingId: 'following-1' },
|
||||
'Database error in unfollowUser',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -662,7 +913,7 @@ describe('User DB Service', () => {
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('FROM public.activity_log al'),
|
||||
['user-123', 10, 0]
|
||||
['user-123', 10, 0],
|
||||
);
|
||||
expect(result).toEqual(mockFeedItems);
|
||||
});
|
||||
@@ -676,8 +927,13 @@ describe('User DB Service', () => {
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.getUserFeed('user-123', 10, 0, mockLogger)).rejects.toThrow('Failed to retrieve user feed.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, userId: 'user-123', limit: 10, offset: 0 }, 'Database error in getUserFeed');
|
||||
await expect(userRepo.getUserFeed('user-123', 10, 0, mockLogger)).rejects.toThrow(
|
||||
'Failed to retrieve user feed.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, userId: 'user-123', limit: 10, offset: 0 },
|
||||
'Database error in getUserFeed',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -700,12 +956,7 @@ describe('User DB Service', () => {
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(
|
||||
'INSERT INTO public.search_queries (user_id, query_text, result_count, was_successful) VALUES ($1, $2, $3, $4) RETURNING *',
|
||||
[
|
||||
queryData.user_id,
|
||||
queryData.query_text,
|
||||
queryData.result_count,
|
||||
queryData.was_successful,
|
||||
]
|
||||
[queryData.user_id, queryData.query_text, queryData.result_count, queryData.was_successful],
|
||||
);
|
||||
expect(result).toEqual(mockLoggedQuery);
|
||||
});
|
||||
@@ -726,14 +977,24 @@ describe('User DB Service', () => {
|
||||
|
||||
await userRepo.logSearchQuery(queryData, mockLogger);
|
||||
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.any(String), [null, 'anonymous search', 10, true]);
|
||||
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.any(String), [
|
||||
null,
|
||||
'anonymous search',
|
||||
10,
|
||||
true,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw a generic error if the database query fails', async () => {
|
||||
const dbError = new Error('DB Error');
|
||||
mockPoolInstance.query.mockRejectedValue(dbError);
|
||||
await expect(userRepo.logSearchQuery({ query_text: 'fail' }, mockLogger)).rejects.toThrow('Failed to log search query.');
|
||||
expect(mockLogger.error).toHaveBeenCalledWith({ err: dbError, queryData: { query_text: 'fail' } }, 'Database error in logSearchQuery');
|
||||
await expect(userRepo.logSearchQuery({ query_text: 'fail' }, mockLogger)).rejects.toThrow(
|
||||
'Failed to log search query.',
|
||||
);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(
|
||||
{ err: dbError, queryData: { query_text: 'fail' } },
|
||||
'Database error in logSearchQuery',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user