// Mock dependencies before requiring the service jest.mock('../../../models', () => ({ sequelize: { query: jest.fn(), }, })); jest.mock('sequelize', () => ({ QueryTypes: { SELECT: 'SELECT', }, })); jest.mock('../../../utils/logger', () => ({ info: jest.fn(), error: jest.fn(), warn: jest.fn(), })); const { sequelize } = require('../../../models'); const locationService = require('../../../services/locationService'); describe('LocationService', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('findUsersInRadius', () => { it('should find users within specified radius', async () => { const mockUsers = [ { id: 'user-1', email: 'user1@test.com', firstName: 'User', lastName: 'One', latitude: '37.7749', longitude: '-122.4194', distance: '1.5' }, { id: 'user-2', email: 'user2@test.com', firstName: 'User', lastName: 'Two', latitude: '37.7849', longitude: '-122.4094', distance: '2.3' }, ]; sequelize.query.mockResolvedValue(mockUsers); const result = await locationService.findUsersInRadius(37.7749, -122.4194, 10); expect(result).toHaveLength(2); expect(result[0]).toMatchObject({ id: 'user-1', email: 'user1@test.com', firstName: 'User', lastName: 'One', }); expect(parseFloat(result[0].distance)).toBeCloseTo(1.5, 1); }); it('should use default radius of 10 miles', async () => { sequelize.query.mockResolvedValue([]); await locationService.findUsersInRadius(37.7749, -122.4194); expect(sequelize.query).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ replacements: expect.objectContaining({ radiusMiles: 10, }), }) ); }); it('should throw error when latitude is missing', async () => { await expect(locationService.findUsersInRadius(null, -122.4194, 10)) .rejects.toThrow('Latitude and longitude are required'); }); it('should throw error when longitude is missing', async () => { await expect(locationService.findUsersInRadius(37.7749, null, 10)) .rejects.toThrow('Latitude and longitude are required'); }); it('should throw error when radius is zero', async () => { await expect(locationService.findUsersInRadius(37.7749, -122.4194, 0)) .rejects.toThrow('Radius must be between 1 and 100 miles'); }); it('should throw error when radius is negative', async () => { await expect(locationService.findUsersInRadius(37.7749, -122.4194, -5)) .rejects.toThrow('Radius must be between 1 and 100 miles'); }); it('should throw error when radius exceeds 100 miles', async () => { await expect(locationService.findUsersInRadius(37.7749, -122.4194, 150)) .rejects.toThrow('Radius must be between 1 and 100 miles'); }); it('should handle database errors', async () => { sequelize.query.mockRejectedValue(new Error('Database error')); await expect(locationService.findUsersInRadius(37.7749, -122.4194, 10)) .rejects.toThrow('Failed to find users in radius'); }); it('should return empty array when no users found', async () => { sequelize.query.mockResolvedValue([]); const result = await locationService.findUsersInRadius(37.7749, -122.4194, 10); expect(result).toEqual([]); }); it('should format distance to 2 decimal places', async () => { const mockUsers = [ { id: 'user-1', email: 'user1@test.com', firstName: 'User', lastName: 'One', latitude: '37.7749', longitude: '-122.4194', distance: '1.23456789' }, ]; sequelize.query.mockResolvedValue(mockUsers); const result = await locationService.findUsersInRadius(37.7749, -122.4194, 10); expect(result[0].distance).toBe('1.23'); }); }); describe('calculateDistance', () => { it('should calculate distance between two points', () => { // San Francisco to Los Angeles: approximately 347 miles const distance = locationService.calculateDistance( 37.7749, -122.4194, // San Francisco 34.0522, -118.2437 // Los Angeles ); expect(distance).toBeGreaterThan(340); expect(distance).toBeLessThan(360); }); it('should return 0 for same coordinates', () => { const distance = locationService.calculateDistance( 37.7749, -122.4194, 37.7749, -122.4194 ); expect(distance).toBe(0); }); it('should calculate short distances accurately', () => { // Two points about 1 mile apart const distance = locationService.calculateDistance( 37.7749, -122.4194, 37.7893, -122.4094 ); expect(distance).toBeGreaterThan(0.5); expect(distance).toBeLessThan(2); }); it('should handle negative coordinates', () => { // Sydney, Australia to Melbourne, Australia const distance = locationService.calculateDistance( -33.8688, 151.2093, // Sydney -37.8136, 144.9631 // Melbourne ); expect(distance).toBeGreaterThan(400); expect(distance).toBeLessThan(500); }); it('should handle crossing the prime meridian', () => { // London to Paris const distance = locationService.calculateDistance( 51.5074, -0.1278, // London 48.8566, 2.3522 // Paris ); expect(distance).toBeGreaterThan(200); expect(distance).toBeLessThan(250); }); }); describe('toRadians', () => { it('should convert degrees to radians', () => { expect(locationService.toRadians(0)).toBe(0); expect(locationService.toRadians(180)).toBeCloseTo(Math.PI, 5); expect(locationService.toRadians(90)).toBeCloseTo(Math.PI / 2, 5); expect(locationService.toRadians(360)).toBeCloseTo(2 * Math.PI, 5); }); it('should handle negative degrees', () => { expect(locationService.toRadians(-90)).toBeCloseTo(-Math.PI / 2, 5); }); }); });