Skip to content

Commit

Permalink
Raise test coverage significantly
Browse files Browse the repository at this point in the history
  • Loading branch information
victorquinn committed Oct 9, 2024
1 parent 3db0362 commit a756b16
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "weather-plus",
"version": "0.0.20",
"version": "0.1.0",
"description": "Weather Plus is a powerful wrapper around various Weather APIs that simplifies adding weather data to your application",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand Down
9 changes: 9 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import WeatherPlus from './index';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { IWeatherData } from './interfaces';
import { InvalidProviderLocationError } from './errors';

jest.mock('redis', () => {
const mGet = jest.fn();
Expand Down Expand Up @@ -120,4 +121,12 @@ describe('WeatherPlus Library', () => {
}
expect(response).toEqual(expectedResponse);
});

// Add this test case
it('should export InvalidProviderLocationError', () => {
expect(InvalidProviderLocationError).toBeDefined();
const error = new InvalidProviderLocationError('Test error');
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe('Test error');
});
});
85 changes: 85 additions & 0 deletions src/providers/nws/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,89 @@ describe('NWSProvider', () => {
});

// Add additional tests as needed

// Add this test case
it('should throw an error if no observation stations are found', async () => {
// Mock fetchObservationStationUrl
mock
.onGet(`https://api.weather.gov/points/${latInUS},${lngInUS}`)
.reply(200, {
properties: {
observationStations: 'https://api.weather.gov/gridpoints/XYZ/123,456/stations',
},
});

// Mock fetchNearbyStations with empty features array
mock
.onGet('https://api.weather.gov/gridpoints/XYZ/123,456/stations')
.reply(200, {
features: [],
});

await expect(provider.getWeather(latInUS, lngInUS)).rejects.toThrow('No stations found');
});

// Add this test case
it('should throw an error on invalid observation data', async () => {
// Mock fetchObservationStationUrl
mock
.onGet(`https://api.weather.gov/points/${latInUS},${lngInUS}`)
.reply(200, {
properties: {
observationStations: 'https://api.weather.gov/gridpoints/XYZ/123,456/stations',
},
});

// Mock fetchNearbyStations
mock
.onGet('https://api.weather.gov/gridpoints/XYZ/123,456/stations')
.reply(200, {
features: [{ id: 'station123' }],
});

// Mock fetchLatestObservation with invalid data
mock.onGet('station123/observations/latest').reply(200, {});

await expect(provider.getWeather(latInUS, lngInUS)).rejects.toThrow('Invalid observation data');
});

// Add this test case
it('should skip stations if fetching data from a station fails', async () => {
// Mock fetchObservationStationUrl
mock
.onGet(`https://api.weather.gov/points/${latInUS},${lngInUS}`)
.reply(200, {
properties: {
observationStations: 'https://api.weather.gov/gridpoints/XYZ/123,456/stations',
},
});

// Mock fetchNearbyStations with multiple stations
mock
.onGet('https://api.weather.gov/gridpoints/XYZ/123,456/stations')
.reply(200, {
features: [{ id: 'station123' }, { id: 'station456' }],
});

// Mock fetchLatestObservation to fail for the first station
mock.onGet('station123/observations/latest').reply(500);
// Mock fetchLatestObservation to succeed for the second station
mock.onGet('station456/observations/latest').reply(200, {
properties: {
dewpoint: { value: 10, unitCode: 'wmoUnit:degC' },
relativeHumidity: { value: 80 },
temperature: { value: 20, unitCode: 'wmoUnit:degC' },
textDescription: 'Clear',
},
});

const weatherData = await provider.getWeather(latInUS, lngInUS);

expect(weatherData).toEqual({
dewPoint: { value: 10, unit: 'C' },
humidity: { value: 80, unit: 'percent' },
temperature: { value: 20, unit: 'C' },
conditions: { value: 'Clear', unit: 'string' },
});
});
});
8 changes: 8 additions & 0 deletions src/providers/nws/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export class NWSProvider implements IWeatherProvider {
) && stations.length > 0
);

if (weatherData.length === 0) {
throw new Error('Invalid observation data');
}

for (const key of WEATHER_KEYS) {
const value = weatherData.find((data) => data[key]);

Expand All @@ -68,6 +72,10 @@ export class NWSProvider implements IWeatherProvider {
}
}

if (Object.keys(data).length === 0) {
throw new Error('Invalid observation data');

Check warning on line 76 in src/providers/nws/client.ts

View check run for this annotation

Codecov / codecov/patch

src/providers/nws/client.ts#L76

Added line #L76 was not covered by tests
}

return data as IWeatherData;
} catch (error) {
log('Error in getWeather:', error);
Expand Down
16 changes: 16 additions & 0 deletions src/providers/openweather/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,20 @@ describe('OpenWeatherProvider', () => {
conditions: { value: 'clear sky', unit: 'string' },
});
});

// Add this test case
it('should throw an error if no API key is provided', () => {
expect(() => {
new OpenWeatherProvider('');
}).toThrow('OpenWeather provider requires an API key.');
});

// Add this test case
it('should handle errors from the OpenWeather API', async () => {
mock
.onGet('https://api.openweathermap.org/data/3.0/onecall')
.reply(500);

await expect(provider.getWeather(lat, lng)).rejects.toThrow();
});
});
10 changes: 10 additions & 0 deletions src/providers/providerFactory.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ProviderFactory } from './providerFactory';
import { ProviderNotSupportedError } from '../errors';

describe('ProviderFactory', () => {
it('should throw ProviderNotSupportedError for unsupported providers', () => {
expect(() => {
ProviderFactory.createProvider('unsupportedProvider');
}).toThrow(ProviderNotSupportedError);
});
});
12 changes: 12 additions & 0 deletions src/weatherService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,16 @@ describe('WeatherService', () => {
expect(cacheGetMock).toHaveBeenCalled();
expect(cacheSetMock).not.toHaveBeenCalled();
});

// Add this test case
it('should rethrow generic errors from provider.getWeather', async () => {
const genericError = new Error('Generic provider error');
// Mock the provider's getWeather to throw a generic error
jest.spyOn(weatherService['provider'], 'getWeather').mockRejectedValue(genericError);

const lat = 38.8977; // Valid US location
const lng = -77.0365;

await expect(weatherService.getWeather(lat, lng)).rejects.toThrow('Generic provider error');
});
});

0 comments on commit a756b16

Please sign in to comment.