lootsa tests fixes
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m30s

This commit is contained in:
2025-12-05 17:41:07 -08:00
parent 19d431057f
commit e07b0c6ae4
9 changed files with 126 additions and 145 deletions

View File

@@ -1,5 +1,9 @@
// src/services/db/user.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
// Un-mock the module we are testing to ensure we use the real implementation.
vi.unmock('./user.db');
import {
findUserByEmail,
createUser,
@@ -21,11 +25,7 @@ import {
getUserFeed,
logSearchQuery,
} from './user.db';
import { Pool } from 'pg';
const mockQuery = vi.fn();
const mockRelease = vi.fn();
const mockConnect = vi.fn().mockResolvedValue({ query: mockQuery, release: mockRelease });
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
import type { Profile } from '../../types';
// Mock other db services that are used by functions in user.db.ts
@@ -38,33 +38,17 @@ vi.mock('./personalization', () => ({
describe('User DB Service', () => {
beforeEach(() => {
// FIX: Reset mockQuery
mockQuery.mockReset();
vi.mocked(Pool).mockImplementation(function() {
return {
query: mockQuery,
connect: mockConnect,
release: mockRelease,
on: vi.fn(),
end: vi.fn(),
totalCount: 0,
idleCount: 0,
waitingCount: 0,
} as unknown as Pool;
});
vi.clearAllMocks();
});
describe('findUserByEmail', () => {
it('should execute the correct query and return a user', async () => {
const mockUser = { user_id: '123', email: 'test@example.com' };
mockQuery.mockResolvedValue({ rows: [mockUser] });
mockPoolInstance.query.mockResolvedValue({ rows: [mockUser] });
const result = await findUserByEmail('test@example.com');
expect(mockQuery).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);
});
});
@@ -74,7 +58,7 @@ describe('User DB Service', () => {
const mockUser = { user_id: 'new-user-id', email: 'new@example.com' };
const mockProfile = { ...mockUser, role: 'user' };
mockQuery
mockPoolInstance.query
.mockResolvedValueOnce({ rows: [] }) // BEGIN
.mockResolvedValueOnce({ rows: [] }) // set_config
.mockResolvedValueOnce({ rows: [mockUser] }) // INSERT user RETURNING
@@ -83,8 +67,8 @@ describe('User DB Service', () => {
const result = await createUser('new@example.com', 'hashedpass', { full_name: 'New User' });
expect(mockConnect).toHaveBeenCalled();
expect(mockQuery).toHaveBeenCalledWith('BEGIN');
expect(mockPoolInstance.connect).toHaveBeenCalled();
expect(mockPoolInstance.query).toHaveBeenCalledWith('BEGIN');
// The implementation returns the profile, not just the user row
expect(result).toEqual(mockProfile);
});
@@ -92,102 +76,102 @@ describe('User DB Service', () => {
describe('findUserById', () => {
it('should query for a user by their ID', async () => {
mockQuery.mockResolvedValue({ rows: [{ user_id: '123' }] });
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }] });
await findUserById('123');
expect(mockQuery).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']);
});
});
describe('findUserWithPasswordHashById', () => {
it('should query for a user and their password hash by ID', async () => {
mockQuery.mockResolvedValue({ rows: [{ user_id: '123', password_hash: 'hash' }] });
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123', password_hash: 'hash' }] });
await findUserWithPasswordHashById('123');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('SELECT user_id, email, password_hash'), ['123']);
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('SELECT user_id, email, password_hash'), ['123']);
});
});
describe('findUserProfileById', () => {
it('should query for a user profile by user ID', async () => {
mockQuery.mockResolvedValue({ rows: [{ user_id: '123' }] });
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }] });
await findUserProfileById('123');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('FROM public.profiles WHERE user_id = $1'), ['123']);
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('FROM public.profiles WHERE user_id = $1'), ['123']);
});
});
describe('updateUserProfile', () => {
it('should execute an UPDATE query for the user profile', async () => {
const mockProfile: Profile = { user_id: '123', full_name: 'Updated Name', role: 'user', points: 0 };
mockQuery.mockResolvedValue({ rows: [mockProfile] });
mockPoolInstance.query.mockResolvedValue({ rows: [mockProfile] });
await updateUserProfile('123', { full_name: 'Updated Name' });
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.profiles'), expect.any(Array));
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('UPDATE public.profiles'), expect.any(Array));
});
});
describe('updateUserPreferences', () => {
it('should execute an UPDATE query for user preferences', async () => {
mockQuery.mockResolvedValue({ rows: [{}] });
mockPoolInstance.query.mockResolvedValue({ rows: [{}] });
await updateUserPreferences('123', { darkMode: true });
expect(mockQuery).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']);
});
});
describe('updateUserPassword', () => {
it('should execute an UPDATE query for the user password', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await updateUserPassword('123', 'newhash');
expect(mockQuery).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']);
});
});
describe('deleteUserById', () => {
it('should execute a DELETE query for the user', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await deleteUserById('123');
expect(mockQuery).toHaveBeenCalledWith('DELETE FROM public.users WHERE user_id = $1', ['123']);
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.users WHERE user_id = $1', ['123']);
});
});
describe('saveRefreshToken', () => {
it('should execute an UPDATE query to save the refresh token', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await saveRefreshToken('123', 'new-token');
expect(mockQuery).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']);
});
});
describe('findUserByRefreshToken', () => {
it('should query for a user by their refresh token', async () => {
mockQuery.mockResolvedValue({ rows: [{ user_id: '123' }] });
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }] });
await findUserByRefreshToken('a-token');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('WHERE refresh_token = $1'), ['a-token']);
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('WHERE refresh_token = $1'), ['a-token']);
});
});
describe('createPasswordResetToken', () => {
it('should execute DELETE and INSERT queries', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
const expires = new Date();
await createPasswordResetToken('123', 'token-hash', expires);
expect(mockQuery).toHaveBeenCalledWith('DELETE FROM public.password_reset_tokens WHERE user_id = $1', ['123']);
expect(mockQuery).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]);
});
});
describe('getValidResetTokens', () => {
it('should query for tokens where expires_at > NOW()', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await getValidResetTokens();
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('WHERE expires_at > NOW()'));
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('WHERE expires_at > NOW()'));
});
});
describe('deleteResetToken', () => {
it('should execute a DELETE query for the token hash', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await deleteResetToken('token-hash');
expect(mockQuery).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']);
});
});
@@ -197,13 +181,13 @@ describe('User DB Service', () => {
const { getShoppingLists } = await import('./shopping.db');
const { getWatchedItems } = await import('./personalization.db');
mockQuery.mockResolvedValue({ rows: [{ user_id: '123' }] }); // For findUserProfileById
mockPoolInstance.query.mockResolvedValue({ rows: [{ user_id: '123' }] }); // For findUserProfileById
vi.mocked(getWatchedItems).mockResolvedValue([]);
vi.mocked(getShoppingLists).mockResolvedValue([]);
await exportUserData('123');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('FROM public.profiles'), ['123']);
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('FROM public.profiles'), ['123']);
expect(getWatchedItems).toHaveBeenCalledWith('123');
expect(getShoppingLists).toHaveBeenCalledWith('123');
});
@@ -211,9 +195,9 @@ describe('User DB Service', () => {
describe('followUser', () => {
it('should execute an INSERT query to create a follow relationship', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await followUser('user-1', 'user-2');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.user_follows'), ['user-1', 'user-2']);
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.user_follows'), ['user-1', 'user-2']);
});
it('should throw an error if a user tries to follow themselves', async () => {
@@ -223,26 +207,26 @@ describe('User DB Service', () => {
describe('unfollowUser', () => {
it('should execute a DELETE query to remove a follow relationship', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await unfollowUser('user-1', 'user-2');
expect(mockQuery).toHaveBeenCalledWith('DELETE FROM public.user_follows WHERE follower_id = $1 AND following_id = $2', ['user-1', 'user-2']);
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.user_follows WHERE follower_id = $1 AND following_id = $2', ['user-1', 'user-2']);
});
});
describe('getUserFeed', () => {
it('should call the get_user_feed database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
await getUserFeed('user-123', 10, 20);
expect(mockQuery).toHaveBeenCalledWith('SELECT * FROM public.get_user_feed($1, $2, $3)', ['user-123', 10, 20]);
expect(mockPoolInstance.query).toHaveBeenCalledWith('SELECT * FROM public.get_user_feed($1, $2, $3)', ['user-123', 10, 20]);
});
});
describe('logSearchQuery', () => {
it('should insert a search query into the log', async () => {
mockQuery.mockResolvedValue({ rows: [] });
mockPoolInstance.query.mockResolvedValue({ rows: [] });
const queryData = { userId: 'user-123', queryText: 'apples', resultCount: 5, wasSuccessful: true };
await logSearchQuery(queryData);
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.search_queries'), [queryData.userId, queryData.queryText, queryData.resultCount, queryData.wasSuccessful]);
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.search_queries'), [queryData.userId, queryData.queryText, queryData.resultCount, queryData.wasSuccessful]);
});
});
});