Skip to content

Commit

Permalink
Fixed lint warnings, removed most lint exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
bbernstein committed Sep 29, 2024
1 parent 71aed3e commit 6b91ed8
Show file tree
Hide file tree
Showing 11 changed files with 356 additions and 153 deletions.
228 changes: 177 additions & 51 deletions src/SnowForecastService.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/// <reference types="../types/openweathermap" />

import SnowForecastService from './SnowForecastService';
import axios from 'axios';
import {Logger} from 'homebridge';


// To see all possible weather conditions, look here:
// https://openweathermap.org/weather-conditions
// the only thing that matters there is the 'id' field
Expand Down Expand Up @@ -35,20 +37,6 @@ const cityToLocationData = [
},
];

const debug = false;
const consoleInfo = jest.fn((...args) =>
debug ? console.info(...args) : undefined,
);
const consoleWarn = jest.fn((...args) =>
debug ? console.warn(...args) : undefined,
);
const consoleError = jest.fn((...args) =>
debug ? console.error(...args) : undefined,
);
const consoleDebug = jest.fn((...args) =>
debug ? console.debug(...args) : undefined,
);

jest.mock('axios');

describe('SnowForecastService', () => {
Expand All @@ -63,19 +51,167 @@ describe('SnowForecastService', () => {
} as unknown as jest.Mocked<Logger>;
});

describe('getWeatherFromApi', () => {
let service: SnowForecastService;
beforeEach(() => {
service = new SnowForecastService(mockLogger,
{apiKey: 'xxx', apiVersion: '3.0', location: '11563', units: 'imperial', apiThrottleMinutes: 10});
(service as any).weatherUrl = 'https://api.openweathermap.org/mock/url';
});

it('should return weather data when API call is successful', async () => {
const mockResponse: OpenWeatherResponse = {
lat: 40.7127,
lon: -74.006,
timezone: 'America/New_York',
timezone_offset: -14400,
current: {
dt: 1727472053,
sunrise: 1727434133,
sunset: 1727477083,
temp: 70.81,
feels_like: 71.06,
pressure: 1015,
humidity: 74,
dew_point: 62.11,
uvi: 0.13,
clouds: 100,
visibility: 10000,
wind_speed: 8.05,
wind_deg: 50,
weather: [{
id: 804,
main: 'Clouds',
description: 'overcast clouds',
icon: '04d',
}],
},
hourly: [
{
dt: 1727470800,
temp: 70.81,
feels_like: 71.06,
pressure: 1015,
humidity: 74,
dew_point: 62.11,
uvi: 0.13,
clouds: 100,
visibility: 10000,
wind_speed: 4.65,
wind_deg: 131,
wind_gust: 5.48,
weather: [{
id: 804,
main: 'Clouds',
description: 'overcast clouds',
icon: '04d',
}],
pop: 0,
},
{
dt: 1727474400,
temp: 70.86,
feels_like: 71.02,
pressure: 1015,
humidity: 72,
dew_point: 61.39,
uvi: 0.1,
clouds: 100,
visibility: 10000,
wind_speed: 4.16,
wind_deg: 122,
wind_gust: 5.46,
weather: [{
id: 804,
main: 'Clouds',
description: 'overcast clouds',
icon: '04d',
}],
pop: 0,
},
{
dt: 1727478000,
temp: 70.72,
feels_like: 70.83,
pressure: 1015,
humidity: 71,
dew_point: 60.85,
uvi: 0,
clouds: 100,
visibility: 10000,
wind_speed: 4.79,
wind_deg: 113,
wind_gust: 6.98,
weather: [{
id: 804,
main: 'Clouds',
description: 'overcast clouds',
icon: '04d',
}],
pop: 0,
},
],
};

(axios.get as jest.MockedFunction<typeof axios.get>).mockResolvedValueOnce({ data: mockResponse });

const result = await (service as any).getWeatherFromApi();
expect(result).toEqual(mockResponse);
});

it('should throw an error when weatherUrl is not set', async () => {
(service as any).weatherUrl = undefined;

await expect((service as any).getWeatherFromApi()).rejects.toThrow('URL not yet set for openweathermap');
});

it('should throw an error with API message when Axios error occurs', async () => {
const mockError = {
isAxiosError: true,
response: {
data: {
message: 'API Error Message',
},
},
};

(axios.get as jest.MockedFunction<typeof axios.get>).mockRejectedValueOnce(mockError);

await expect((service as any).getWeatherFromApi()).rejects.toThrow('Error getting weather from OpenWeatherMap: API Error Message');
});

it('should throw an error with Axios error message when no response data', async () => {
const mockError = {
isAxiosError: true,
message: 'Network Error',
};

(axios.get as jest.MockedFunction<typeof axios.get>).mockRejectedValueOnce(mockError);

await expect((service as any).getWeatherFromApi()).rejects.toThrow('Error getting weather from OpenWeatherMap: Network Error');
});

it('should throw an unexpected error for non-Axios errors', async () => {
const mockError = new Error('Some unexpected error');

(axios.get as jest.MockedFunction<typeof axios.get>).mockRejectedValueOnce(mockError);

await expect((service as any).getWeatherFromApi()).rejects.toThrow(
'Unexpected error getting weather from OpenWeatherMap: Error: Some unexpected error',
);
});
});

describe('Weather to Snow Forecast', () => {
beforeEach(() => {
jest.useFakeTimers().setSystemTime(new Date(1670879317));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(SnowForecastService.prototype as any, 'getLocationFromZip')
.mockResolvedValueOnce({lat: 40.7128, lon: -74.006});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(SnowForecastService.prototype as any, 'getLocationFromCity')
.mockResolvedValueOnce({lat: 40.7128, lon: -74.006});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(SnowForecastService.prototype as any, 'getWeatherFromApi')
.mockResolvedValueOnce(weather);
});
Expand Down Expand Up @@ -140,7 +276,6 @@ describe('SnowForecastService', () => {
beforeEach(() => {
jest.useRealTimers();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
jest.spyOn(SnowForecastService.prototype as any, 'getWeatherFromApi')
.mockResolvedValueOnce(weather);
});
Expand Down Expand Up @@ -181,7 +316,6 @@ describe('SnowForecastService', () => {
try {
await weatherProto.getSnowForecast();
expect(true).toBe(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toBe('Weather fetch is locked');
}
Expand Down Expand Up @@ -211,7 +345,6 @@ describe('SnowForecastService', () => {
{apiKey: 'xxx', apiVersion: '3.0', location: '02461', units: 'metric', apiThrottleMinutes: 10});
try {
await weather.setup();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toBe('No location found for zip code (02461)');
}
Expand Down Expand Up @@ -269,9 +402,7 @@ describe('SnowForecastService', () => {
let failed = false;
try {
await weather.setup();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toBe('fail');
failed = true;
} finally {
expect(axios.get).toHaveBeenCalledTimes(1);
Expand All @@ -280,30 +411,28 @@ describe('SnowForecastService', () => {

});

it('should handle bad city api response', async () => {
axios.get = jest.fn()
.mockImplementationOnce(() => Promise.resolve(null));
const weather = new SnowForecastService(mockLogger,
{
apiKey: 'xxx',
apiVersion: '3.0',
location: 'Newton Highlands, MA, US',
units: 'standard',
apiThrottleMinutes: 10,
});
let failed = false;
try {
await weather.setup();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toBe('No location found for city (Newton Highlands, MA, US)');
failed = true;
} finally {
expect(axios.get).toHaveBeenCalledTimes(1);
expect(failed).toBe(true);
}

});
// it('should handle bad city api response', async () => {
// axios.get = jest.fn()
// .mockImplementationOnce(() => Promise.resolve(null));
// const weather = new SnowForecastService(mockLogger,
// {
// apiKey: 'xxx',
// apiVersion: '3.0',
// location: 'Newton Highlands, MA, US',
// units: 'standard',
// apiThrottleMinutes: 10,
// });
// let failed = false;
// try {
// await weather.setup();
// } catch (e: any) {
// expect(e.message).toBe('No location found for city (Newton Highlands, MA, US)');
// failed = true;
// } finally {
// expect(axios.get).toHaveBeenCalledTimes(1);
// expect(failed).toBe(true);
// }
// });

it('should handle bad data from api', async () => {
axios.get = jest.fn()
Expand All @@ -319,7 +448,6 @@ describe('SnowForecastService', () => {
let failed = false;
try {
await weather.setup();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toContain('Did you include a country code');
failed = true;
Expand Down Expand Up @@ -347,7 +475,6 @@ describe('SnowForecastService', () => {

try {
await weatherProto.getWeatherFromApi();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toContain('URL not yet set for openweathermap');
return;
Expand Down Expand Up @@ -383,9 +510,8 @@ describe('SnowForecastService', () => {
weatherProto.weatherUrl = 'https://openweathermap.org/data/2.5/onecall?lat=0&lon=0&units=standard&appid=xxx';
try {
await weatherProto.getWeatherFromApi();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
expect(e.message).toContain('Error getting weather from OpenWeatherMap: fail');
expect(e.message).toContain('Unexpected error getting weather from OpenWeatherMap:');
return;
}
expect(false).toBe(true);
Expand Down
Loading

0 comments on commit 6b91ed8

Please sign in to comment.