feat: WeatherAPI tests

This commit is contained in:
Martin Eyben 2024-11-23 18:41:40 +01:00
parent 52e63be79c
commit e62635c057
10 changed files with 13425 additions and 43 deletions

View File

@ -19,7 +19,7 @@ import java.util.Locale;
*/ */
public class WeatherAPI implements WeatherDataAPI{ public class WeatherAPI implements WeatherDataAPI{
private final String weatherAPIKey; private final String weatherAPIKey;
private static final JSONFetcherInterface JSONFetcher = new JSONFetcher(); JSONFetcherInterface JSONFetcher = new JSONFetcher();
private static final String forecastBaseURL = "https://api.weatherapi.com/v1/forecast.json"; private static final String forecastBaseURL = "https://api.weatherapi.com/v1/forecast.json";
WeatherAPI(String weatherAPIKey) { WeatherAPI(String weatherAPIKey) {

View File

@ -0,0 +1,43 @@
package eirb.pg203;
import eirb.pg203.utils.FileResourcesUtils;
import eirb.pg203.utils.JSONFetcherInterface;
import eirb.pg203.utils.SplitQueryUrl;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Map;
public class FakeJSONFetcherWeatherAPI implements JSONFetcherInterface {
private final static String baseUrlFormat = "https://api.weatherapi.com/v1/forecast.json?key=%s&q=%s&days=%d";
private final String apiKey = "realKey";
private static final JSONObject wrongKeyRequest = FileResourcesUtils.getFileFromResourceAsJson("WeatherAPI/wrong-apikey.json");
private static ArrayList<JSONObject> bordeauxRequests() {
ArrayList<JSONObject> bordeauxRequest = new ArrayList<>();
bordeauxRequest.add(FileResourcesUtils.getFileFromResourceAsJson("WeatherAPI/Bordeaux-1-partial.json"));
bordeauxRequest.add(FileResourcesUtils.getFileFromResourceAsJson("WeatherAPI/Bordeaux-2-partial-sunny.json"));
bordeauxRequest.add(FileResourcesUtils.getFileFromResourceAsJson("WeatherAPI/Bordeaux-3-partial-sunny-rain.json"));
bordeauxRequest.add(FileResourcesUtils.getFileFromResourceAsJson("WeatherAPI/Bordeaux-4-partial-sunny-rain-cloudy.json"));
return bordeauxRequest;
}
@Override
public JSONObject fetch(URL url) throws IOException {
Map<String, String> params = SplitQueryUrl.splitQuery(url);
int days = Integer.parseInt(params.get("days"));
if (!params.get("key").contentEquals(apiKey))
return wrongKeyRequest;
return bordeauxRequests().get(days - 1);
}
@Override
public JSONArray fetchArray(URL url) throws IOException {
return null;
}
}

View File

@ -2,70 +2,100 @@ package eirb.pg203;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.stream.Stream;
public class WeatherAPITest { public class WeatherAPITest {
private static String APIKey = "cef8e1b6ea364994b5072423240111"; private static final String APIKey = "realKey";
private static final float epsilon = 0.01F;
private WeatherAPI weatherAPI;
@Test @BeforeEach
public void testRightAPIKey() { public void setupWeatherApi() {
WeatherAPI weatherAPI = new WeatherAPI(WeatherAPITest.APIKey); this.weatherAPI = new WeatherAPI(WeatherAPITest.APIKey);
int day = 0; this.weatherAPI.JSONFetcher = new FakeJSONFetcherWeatherAPI();
// int hour = 10; }
int days = 7;
String city = "Bordeaux";
Assertions.assertAll( /**
() -> weatherAPI.getTemperature(day, city), * List of args for Temperature testing
// () -> weatherAPI.getTemperature(day, hour, city), * @return Args for testing
() -> weatherAPI.getTemperatures(days, city) */
private static Stream<Arguments> testGetTemperature(){
return Stream.of(
Arguments.arguments(0, 8.1F,WeatherData.Condition.PARTIAL, 17.45F, 142.08F),
Arguments.arguments(1, 13F, WeatherData.Condition.SUNNY, 23.03F, 142.58F),
Arguments.arguments(2, 12.7F, WeatherData.Condition.RAINY, 13.19F, 222.92F),
Arguments.arguments(3, 8.1F,WeatherData.Condition.CLOUDY, 17.45F, 142.08F)
); );
} }
@Test @ParameterizedTest(name="Temperature fetch at Bordeaux D+{0}")
public void testWrongAPIKey() { @MethodSource
WeatherAPI weatherAPI = new WeatherAPI(""); public void testGetTemperature(int day, float expectedTemp, WeatherData.Condition expectedCond, float expectedWindSpeed, float expectedWindAngle) throws IOException {
int day = 0;
// int hour = 10;
int days = 7;
String city = "Bordeaux"; String city = "Bordeaux";
WeatherData weatherData;
weatherData = weatherAPI.getTemperature(day, city);
Assertions.assertThrows(IOException.class, () -> weatherAPI.getTemperature(day, city)); /* Temperatures */
Assertions.assertThrows(IOException.class, () -> weatherAPI.getTemperatures(days, city)); Assertions.assertEquals(expectedTemp, weatherData.getTemp());
/* Condition */
Assertions.assertEquals(expectedCond, weatherData.getCondition());
/* Wind */
Assertions.assertTrue(expectedWindSpeed - weatherData.getWindSpeed() < epsilon);
Assertions.assertTrue(expectedWindAngle - weatherData.getWindDirectionAngle() < epsilon);
} }
/**
* For coverage (hour not yet implemented)
* @throws IOException never
*/
@Test @Test
public void testWrongDay() { public void testGetTemperatureByHour() throws IOException {
WeatherAPI weatherAPI = new WeatherAPI(WeatherAPITest.APIKey);
String city = "Bordeaux"; String city = "Bordeaux";
Assertions.assertThrows(IOException.class, () -> weatherAPI.getTemperature(-1, city));
Assertions.assertThrows(IOException.class, () -> weatherAPI.getTemperature(15, city));
Assertions.assertThrows(IOException.class, () -> weatherAPI.getTemperatures(15, city));
Assertions.assertThrows(IOException.class, () -> weatherAPI.getTemperatures(-1, city));
}
@Test
public void testRightDay() {
WeatherAPI weatherAPI = new WeatherAPI(WeatherAPITest.APIKey);
String city = "Bordeaux";
Assertions.assertAll( Assertions.assertAll(
() -> weatherAPI.getTemperature(0, city), () -> weatherAPI.getTemperature(0,1, city)
() -> weatherAPI.getTemperature(5, city),
() -> weatherAPI.getTemperature(14, city),
() -> weatherAPI.getTemperatures(0, city),
() -> weatherAPI.getTemperatures(8, city),
() -> weatherAPI.getTemperatures(14, city)
); );
}
@Test
@DisplayName("Multiple day temperature fetch")
public void testGetTemperatures() throws IOException {
String city = "Bordeaux";
int days = 3;
float[] expectedTemperatures = {8.1F, 13F, 12.7F, 8.1F};
WeatherData.Condition[] expectedConditions = {WeatherData.Condition.PARTIAL, WeatherData.Condition.SUNNY, WeatherData.Condition.RAINY, WeatherData.Condition.CLOUDY};
float[] expectedWindSpeed = {17.45F, 23.03F, 13.19F, 17.45F};
float[] expectedWindDirection = {142.08F, 142.58F, 222.92F, 142.08F};
ArrayList<WeatherData> weatherDatas;
WeatherData weatherData;
weatherDatas = weatherAPI.getTemperatures(days, city);
for (int index = 0; index < days; index++) {
weatherData = weatherDatas.get(index);
/* Temperatures */
Assertions.assertEquals(expectedTemperatures[index], weatherData.getTemp());
/* Weather condition */
Assertions.assertEquals(expectedConditions[index], weatherData.getCondition());
/* Wind */
Assertions.assertTrue(expectedWindSpeed[index] - weatherData.getWindSpeed() < epsilon);
Assertions.assertTrue(expectedWindDirection[index] - weatherData.getWindDirectionAngle() < epsilon);
}
} }
@Test @Test
public void testGetAPIName() { public void testGetAPIName() {
WeatherAPI weatherAPI = new WeatherAPI(WeatherAPITest.APIKey); Assertions.assertEquals("WeatherAPI", weatherAPI.getAPIName());
Assertions.assertTrue(weatherAPI.getAPIName().equals("WeatherAPI"));
} }
} }

View File

@ -0,0 +1,53 @@
package eirb.pg203.utils;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class FileResourcesUtils {
/**
* Fetch ressource file
* Code from https://mkyong.com
* @param fileName
* @return
*/
public static InputStream getFileFromResourceAsStream(String fileName) {
// The class loader that loaded the class
ClassLoader classLoader = FileResourcesUtils.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(fileName);
// the stream holding the file content
if (inputStream == null) {
throw new IllegalArgumentException("file not found! " + fileName);
} else {
return inputStream;
}
}
public static JSONObject getFileFromResourceAsJson(String fileName) {
InputStream inputStream = getFileFromResourceAsStream(fileName);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line;
while(true){
try {
if (!bufferedReader.ready()) break;
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
line = bufferedReader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
stringBuilder.append(line);
}
return new JSONObject(stringBuilder.toString());
}
}

View File

@ -0,0 +1,20 @@
package eirb.pg203.utils;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.LinkedHashMap;
import java.util.Map;
public class SplitQueryUrl {
public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
{
"error": {
"code": 2008,
"message": "API key has been disabled."
}
}