import axios from 'axios';
import { GeocodingService } from '../src/services/geocoding.js';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
describe('GeocodingService', () => {
let geocodingService: GeocodingService;
const mockApiKey = 'test-api-key';
beforeEach(() => {
geocodingService = new GeocodingService(mockApiKey);
jest.clearAllMocks();
});
describe('getCoordinatesByLocationName', () => {
it('should return geocoding results for a valid location', async () => {
const mockResponse = [
{
name: 'New York',
lat: 40.7128,
lon: -74.0060,
country: 'US',
state: 'NY',
},
];
mockedAxios.get.mockResolvedValueOnce({ data: mockResponse });
const result = await geocodingService.getCoordinatesByLocationName(
'New York, NY, US'
);
expect(result).toEqual(mockResponse);
expect(mockedAxios.get).toHaveBeenCalledWith(
'http://api.openweathermap.org/geo/1.0/direct',
{
params: {
q: 'New York, NY, US',
limit: 5,
appid: mockApiKey,
},
}
);
});
it('should handle API errors', async () => {
const errorMessage = 'API Error';
mockedAxios.get.mockRejectedValueOnce(new Error(errorMessage));
await expect(
geocodingService.getCoordinatesByLocationName('Invalid Location')
).rejects.toThrow(`Failed to geocode location "Invalid Location": ${errorMessage}`);
});
});
describe('getCoordinatesByZipCode', () => {
it('should return geocoding result for a valid zip code', async () => {
const mockResponse = {
zip: '10001',
name: 'New York',
lat: 40.7128,
lon: -74.0060,
country: 'US',
};
mockedAxios.get.mockResolvedValueOnce({ data: mockResponse });
const result = await geocodingService.getCoordinatesByZipCode('10001', 'US');
expect(result).toEqual(mockResponse);
expect(mockedAxios.get).toHaveBeenCalledWith(
'http://api.openweathermap.org/geo/1.0/zip',
{
params: {
zip: '10001,US',
appid: mockApiKey,
},
}
);
});
it('should handle zip code without country code', async () => {
const mockResponse = {
zip: '10001',
name: 'New York',
lat: 40.7128,
lon: -74.0060,
country: 'US',
};
mockedAxios.get.mockResolvedValueOnce({ data: mockResponse });
await geocodingService.getCoordinatesByZipCode('10001');
expect(mockedAxios.get).toHaveBeenCalledWith(
'http://api.openweathermap.org/geo/1.0/zip',
{
params: {
zip: '10001,US',
appid: mockApiKey,
},
}
);
});
});
describe('resolveLocation', () => {
it('should return coordinates when lat/lon are provided', async () => {
const input = { lat: 40.7128, lon: -74.0060 };
const result = await geocodingService.resolveLocation(input);
expect(result).toEqual({ lat: 40.7128, lon: -74.0060 });
expect(mockedAxios.get).not.toHaveBeenCalled();
});
it('should resolve zip code to coordinates', async () => {
const mockResponse = {
zip: '10001',
name: 'New York',
lat: 40.7128,
lon: -74.0060,
country: 'US',
};
mockedAxios.get.mockResolvedValueOnce({ data: mockResponse });
const input = { zipCode: '10001', country: 'US' };
const result = await geocodingService.resolveLocation(input);
expect(result).toEqual({ lat: 40.7128, lon: -74.0060 });
});
it('should resolve city name to coordinates', async () => {
const mockResponse = [
{
name: 'New York',
lat: 40.7128,
lon: -74.0060,
country: 'US',
state: 'NY',
},
];
mockedAxios.get.mockResolvedValueOnce({ data: mockResponse });
const input = { city: 'New York', state: 'NY', country: 'US' };
const result = await geocodingService.resolveLocation(input);
expect(result).toEqual({ lat: 40.7128, lon: -74.0060 });
});
it('should throw error for invalid input', async () => {
const input = {};
await expect(geocodingService.resolveLocation(input)).rejects.toThrow(
'Invalid location input. Provide coordinates, zip code, or city name.'
);
});
it('should throw error when no location is found', async () => {
mockedAxios.get.mockResolvedValueOnce({ data: [] });
const input = { city: 'NonexistentCity' };
await expect(geocodingService.resolveLocation(input)).rejects.toThrow(
'No location found for "NonexistentCity"'
);
});
});
});