Add sad path test for ObjectMapper & Add test for betaSearch

pull/16/head
Hammy 3 years ago
parent c5502d8414
commit 0c510e4713

@ -16,13 +16,13 @@ import java.util.List;
*
*/
class APIMapper {
private final ObjectMapper objectMapper;
private ObjectMapper objectMapper;
APIMapper() {
objectMapper = new ObjectMapper();
}
/**
/*\
* Convert any object passed and return as a {@link String}
*
* @param obj {@link Object} to write as {@link String}
@ -153,4 +153,8 @@ class APIMapper {
throw new APIMapperException(exceptionMessage, throwable);
}
void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}

@ -39,7 +39,7 @@ public class APIWrapper {
private static final String host = "https://mywaifulist.moe/api/v1/";
private String apiKey;
private final APIMapper apiMapper;
private APIMapper apiMapper;
private final HttpClient httpClient;
private final Executor executor = Executors.newFixedThreadPool(10);
@ -413,7 +413,7 @@ public class APIWrapper {
this.apiKey = apiKey;
}
String getApiKey() {
return apiKey;
void setApiMapper(APIMapper apiMapper) {
this.apiMapper = apiMapper;
}
}

@ -1,5 +1,9 @@
package me.goudham;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import me.goudham.domain.pagination.PaginationData;
import me.goudham.domain.waifu.FilteredWaifu;
import me.goudham.domain.waifu.Waifu;
@ -11,6 +15,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import javax.net.ssl.SSLSession;
import java.io.BufferedReader;
@ -29,143 +34,211 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import static me.goudham.APIUtils.listOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
class MyWaifuClientTest {
@Mock
private HttpClient httpClient;
private final String apiKey = "ValidAPIKey";
private MyWaifuClient sut;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
sut = MyWaifuClient.createDefault(apiKey);
APIWrapper apiWrapper = new APIWrapper(apiKey, httpClient);
sut.setAPIWrapper(apiWrapper);
}
@Test
void successfullyGetWaifu() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getWaifu.json");
Waifu expectedWaifu = TestEntity.getExpectedWaifu();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<Waifu> actualWaifuResponse = sut.getWaifu(1);
assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualWaifuResponse.getBody(), is(expectedBody));
assertThat(actualWaifuResponse.getModel(), is(expectedWaifu));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
@Test
void successfullyGetBestWaifus() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getBestWaifus.json");
List<FilteredWaifu> expectedBestWaifus = TestEntity.getBestWaifus();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<List<FilteredWaifu>> actualBestWaifusResponse = sut.getBestWaifus();
assertThat(actualBestWaifusResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualBestWaifusResponse.getBody(), is(expectedBody));
assertThat(actualBestWaifusResponse.getModel(), is(expectedBestWaifus));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
@Test
void successfullyGetWaifuImages() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1/images?page=1");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getWaifuImages.json");
PaginationData<WaifuImage> expectedWaifuImages = TestEntity.getWaifuImages();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<PaginationData<WaifuImage>> actualWaifuImagesResponse = sut.getWaifuImages(1, 1);
assertThat(actualWaifuImagesResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualWaifuImagesResponse.getBody(), is(expectedBody));
assertThat(actualWaifuImagesResponse.getModel(), is(expectedWaifuImages));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
private String getJsonAsString(String filename) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename);
return new BufferedReader(new InputStreamReader(Objects.requireNonNull(resourceAsStream), StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
}
private HttpRequest buildHttpGetRequest(String apiKey, String param) {
return HttpRequest.newBuilder()
.uri(URI.create("https://mywaifulist.moe/api/v1/" + param))
.timeout(Duration.ofSeconds(20))
.headers("Content-Type", "application/json", "apikey", apiKey)
.GET()
.build();
}
private HttpResponse<String> buildHttpResponse(int statusCode, String body) {
return new HttpResponse<>() {
@Override
public int statusCode() {
return statusCode;
}
@Override
public HttpRequest request() {
return null;
}
@Override
public Optional<HttpResponse<String>> previousResponse() {
return Optional.empty();
}
@Override
public HttpHeaders headers() {
return null;
}
@Override
public String body() {
return body;
}
@Override
public Optional<SSLSession> sslSession() {
return Optional.empty();
}
@Override
public URI uri() {
return null;
}
@Override
public HttpClient.Version version() {
return null;
}
};
}
@Mock
private HttpClient httpClient;
@Spy
private ObjectMapper objectMapper;
private final String apiKey = "ValidAPIKey";
private MyWaifuClient sut;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
sut = MyWaifuClient.createDefault(apiKey);
APIWrapper apiWrapper = new APIWrapper(apiKey, httpClient);
APIMapper apiMapper = new APIMapper();
apiMapper.setObjectMapper(objectMapper);
apiWrapper.setApiMapper(apiMapper);
sut.setAPIWrapper(apiWrapper);
}
@Test
void successfullyGetWaifu() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getWaifu.json");
Waifu expectedWaifu = TestEntity.getExpectedWaifu();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<Waifu> actualWaifuResponse = sut.getWaifu(1);
assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualWaifuResponse.getBody(), is(expectedBody));
assertThat(actualWaifuResponse.getModel(), is(expectedWaifu));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
@Test
void successfullyGetBestWaifus() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getBestWaifus.json");
List<FilteredWaifu> expectedBestWaifus = TestEntity.getBestWaifus();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<List<FilteredWaifu>> actualBestWaifusResponse = sut.getBestWaifus();
assertThat(actualBestWaifusResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualBestWaifusResponse.getBody(), is(expectedBody));
assertThat(actualBestWaifusResponse.getModel(), is(expectedBestWaifus));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
@Test
void successfullyGetWaifuImages() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1/images?page=1");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getWaifuImages.json");
PaginationData<WaifuImage> expectedWaifuImages = TestEntity.getWaifuImages();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<PaginationData<WaifuImage>> actualWaifuImagesResponse = sut.getWaifuImages(1, 1);
assertThat(actualWaifuImagesResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualWaifuImagesResponse.getBody(), is(expectedBody));
assertThat(actualWaifuImagesResponse.getModel(), is(expectedWaifuImages));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
@Test
void successfullyPostBetaSearch() throws IOException, InterruptedException, APIMapperException, APIResponseException {
HttpRequest expectedHttpRequest = buildHttpPostRequest(apiKey, "search/beta");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("betaSearch.json");
List<FilteredWaifu> expectedBetaSearch = TestEntity.getBetaSearch();
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
Response<List<FilteredWaifu>> actualBetaSearchResponse = sut.betaSearch("Yumeko");
assertThat(actualBetaSearchResponse.getStatusCode(), is(expectedStatusCode));
assertThat(actualBetaSearchResponse.getBody(), is(expectedBody));
assertThat(actualBetaSearchResponse.getModel(), is(expectedBetaSearch));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
@Test
void failToGetBestWaifusWhereDeserializationGoesWrong() throws IOException, InterruptedException {
HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best");
int expectedStatusCode = 200;
String expectedBody = getJsonAsString("getBestWaifus.json");
String expectedData = getData(expectedBody);
HttpResponse<String> expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody);
JavaType expectedModel = listOf(FilteredWaifu.class);
String customExceptionMessage = "If you are seeing this message, " +
"this is more than likely a fault in my logic. " +
"Please raise an issue including the printed stacktrace :D";
String exceptionMessage = "Uh Oh Somebody Did a No No!";
String expectedExceptionMessage = "\n\n" + customExceptionMessage + "\n\n" + exceptionMessage;
doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
doThrow(new JsonProcessingException(exceptionMessage) {}).when(objectMapper).readValue(expectedData, expectedModel);
Throwable actualException = assertThrows(APIMapperException.class, () -> sut.getBestWaifus());
assertThat(actualException.getMessage(), is(expectedExceptionMessage));
verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString());
verifyNoMoreInteractions(httpClient);
}
private String getData(String jsonBody) throws JsonProcessingException {
JsonNode parent = objectMapper.readTree(jsonBody);
return parent.get("data").toString();
}
private HttpRequest buildHttpPostRequest(String apiKey, String param) {
return HttpRequest.newBuilder()
.uri(URI.create("https://mywaifulist.moe/api/v1/" + param))
.timeout(Duration.ofSeconds(20))
.headers("Content-Type", "application/json", "apikey", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(""))
.build();
}
private String getJsonAsString(String filename) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename);
return new BufferedReader(new InputStreamReader(Objects.requireNonNull(resourceAsStream), StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
}
private HttpRequest buildHttpGetRequest(String apiKey, String param) {
return HttpRequest.newBuilder()
.uri(URI.create("https://mywaifulist.moe/api/v1/" + param))
.timeout(Duration.ofSeconds(20))
.headers("Content-Type", "application/json", "apikey", apiKey)
.GET()
.build();
}
private HttpResponse<String> buildHttpResponse(int statusCode, String body) {
return new HttpResponse<>() {
@Override
public int statusCode() {
return statusCode;
}
@Override
public HttpRequest request() {
return null;
}
@Override
public Optional<HttpResponse<String>> previousResponse() {
return Optional.empty();
}
@Override
public HttpHeaders headers() {
return null;
}
@Override
public String body() {
return body;
}
@Override
public Optional<SSLSession> sslSession() {
return Optional.empty();
}
@Override
public URI uri() {
return null;
}
@Override
public HttpClient.Version version() {
return null;
}
};
}
}

@ -101,7 +101,8 @@ public class TestEntity {
"rika-furude-when-they-cry",
42,
"Waifu",
"https://www.mywaifulist.moe/waifu/rika-furude-when-they-cry"
"https://www.mywaifulist.moe/waifu/rika-furude-when-they-cry",
1
)
);
@ -125,7 +126,8 @@ public class TestEntity {
"shion-that-time-i-got-reincarnated-as-a-slime",
70,
"Waifu",
"https://www.mywaifulist.moe/waifu/shion-that-time-i-got-reincarnated-as-a-slime"
"https://www.mywaifulist.moe/waifu/shion-that-time-i-got-reincarnated-as-a-slime",
1
)
);
@ -149,7 +151,8 @@ public class TestEntity {
"milim-nava-that-time-i-got-reincarnated-as-a-slime",
85,
"Waifu",
"https://www.mywaifulist.moe/waifu/milim-nava-that-time-i-got-reincarnated-as-a-slime"
"https://www.mywaifulist.moe/waifu/milim-nava-that-time-i-got-reincarnated-as-a-slime",
1
)
);
@ -173,7 +176,8 @@ public class TestEntity {
"shuna-that-time-i-got-reincarnated-as-a-slime",
78,
"Waifu",
"https://www.mywaifulist.moe/waifu/shuna-that-time-i-got-reincarnated-as-a-slime"
"https://www.mywaifulist.moe/waifu/shuna-that-time-i-got-reincarnated-as-a-slime",
1
)
);
@ -197,7 +201,8 @@ public class TestEntity {
"rimuru-tempest-that-time-i-got-reincarnated-as-a-slime",
55,
"Husbando",
"https://www.mywaifulist.moe/waifu/rimuru-tempest-that-time-i-got-reincarnated-as-a-slime"
"https://www.mywaifulist.moe/waifu/rimuru-tempest-that-time-i-got-reincarnated-as-a-slime",
1
)
);
@ -220,7 +225,8 @@ public class TestEntity {
"shizue-izawa-that-time-i-got-reincarnated-as-a-slime",
81,
"Waifu",
"https://www.mywaifulist.moe/waifu/shizue-izawa-that-time-i-got-reincarnated-as-a-slime"
"https://www.mywaifulist.moe/waifu/shizue-izawa-that-time-i-got-reincarnated-as-a-slime",
1
)
);
@ -241,7 +247,8 @@ public class TestEntity {
"alina-gray",
1,
"Waifu",
"https://www.mywaifulist.moe/waifu/alina-gray"
"https://www.mywaifulist.moe/waifu/alina-gray",
1
)
);
@ -261,7 +268,8 @@ public class TestEntity {
"jahy",
4,
"Waifu",
"https://www.mywaifulist.moe/waifu/jahy"
"https://www.mywaifulist.moe/waifu/jahy",
1
)
);
@ -284,7 +292,8 @@ public class TestEntity {
"treyni-that-time-i-got-reincarnated-as-a-slime",
16,
"Waifu",
"https://www.mywaifulist.moe/waifu/treyni-that-time-i-got-reincarnated-as-a-slime"
"https://www.mywaifulist.moe/waifu/treyni-that-time-i-got-reincarnated-as-a-slime",
1
)
);
@ -304,7 +313,8 @@ public class TestEntity {
"satan-2",
0,
"Husbando",
"https://www.mywaifulist.moe/waifu/satan-2"
"https://www.mywaifulist.moe/waifu/satan-2",
1
)
);
@ -401,6 +411,59 @@ public class TestEntity {
return waifuImagesPaginationData;
}
public static List<FilteredWaifu> getBetaSearch() {
List<FilteredWaifu> betaSearchResults = new ArrayList<>();
List<FilteredSeries> appearances = new ArrayList<>();
appearances.add(createFullFilteredSeries(
"Unlike many schools, attending Hyakkaou Private Academy prepares students f...",
"https://thicc.mywaifulist.moe/series/1680/13c030778b8e7e44ddccb8ca999e18d6ea2b3d547eacc1f63bf5849ade858bb0.jpeg",
1680,
"Kakegurui: Compulsive Gambler",
"Kakegurui",
1,
null,
"kakegurui-compulsive-gambler",
"TV",
"https://www.mywaifulist.moe/series/kakegurui-compulsive-gambler"
));
betaSearchResults.add(createFilteredWaifu(
appearances,
"Yumeko Jabami is the main protagonist of Kakegurui. She's a transfer studen...",
"https://thicc.mywaifulist.moe/waifus/6167/7bf292a1552161de90ff7d0c1eab2c890b2aa374903773d38d39c6c6e8d6f3d2_thumb.jpeg",
6167.0,
1889,
"Yumeko Jabami",
"蛇喰夢子",
"Jabami Yumeko",
"Jabami Yumeko",
"yumeko-jabami-kakegurui-compulsive-gambler",
172,
"Waifu",
"https://www.mywaifulist.moe/waifu/yumeko-jabami-kakegurui-compulsive-gambler",
2
)
);
return betaSearchResults;
}
private static FilteredSeries createFullFilteredSeries(String description, String displayPicture, int id, String name, String originalName, int relevance, String romajiName, String slug, String type, String url) {
FilteredSeries filteredSeries = new FilteredSeries();
filteredSeries.setDescription(description);
filteredSeries.setDisplayPicture(displayPicture);
filteredSeries.setId(id);
filteredSeries.setName(name);
filteredSeries.setOriginalName(originalName);
filteredSeries.setRelevance(relevance);
filteredSeries.setRomajiName(romajiName);
filteredSeries.setSlug(slug);
filteredSeries.setType(type);
filteredSeries.setUrl(url);
return filteredSeries;
}
private static WaifuImage createWaifuImage(Integer id, String image, String nsfw, String thumbnail) {
WaifuImage waifuImage = new WaifuImage();
waifuImage.setId(id);
@ -411,8 +474,8 @@ public class TestEntity {
}
private static FilteredWaifu createFilteredWaifu(List<FilteredSeries> appearances, String description, String displayPicture,
Double id, Integer likes, String name, String originalName, String romaji,
String romaji_name, String slug, Integer trash, String type, String url) {
Double id, Integer likes, String name, String originalName, String romaji,
String romaji_name, String slug, Integer trash, String type, String url, Integer relevance) {
FilteredWaifu filteredWaifu = new FilteredWaifu();
filteredWaifu.setAppearances(appearances);
filteredWaifu.setDescription(description);
@ -427,7 +490,7 @@ public class TestEntity {
filteredWaifu.setTrash(trash);
filteredWaifu.setType(type);
filteredWaifu.setUrl(url);
filteredWaifu.setRelevance(1);
filteredWaifu.setRelevance(relevance);
return filteredWaifu;
}

@ -0,0 +1,34 @@
{
"data": [
{
"appearances": [
{
"description": "Unlike many schools, attending Hyakkaou Private Academy prepares students f...",
"display_picture": "https://thicc.mywaifulist.moe/series/1680/13c030778b8e7e44ddccb8ca999e18d6ea2b3d547eacc1f63bf5849ade858bb0.jpeg",
"id": 1680,
"name": "Kakegurui: Compulsive Gambler",
"original_name": "Kakegurui",
"relevance": 1,
"romaji_name": null,
"slug": "kakegurui-compulsive-gambler",
"type": "TV",
"url": "https://www.mywaifulist.moe/series/kakegurui-compulsive-gambler"
}
],
"description": "Yumeko Jabami is the main protagonist of Kakegurui. She's a transfer studen...",
"display_picture": "https://thicc.mywaifulist.moe/waifus/6167/7bf292a1552161de90ff7d0c1eab2c890b2aa374903773d38d39c6c6e8d6f3d2_thumb.jpeg",
"id": 6167,
"likes": 1889,
"name": "Yumeko Jabami",
"original_name": "蛇喰夢子",
"relevance": 2,
"romaji": "Jabami Yumeko",
"romaji_name": "Jabami Yumeko",
"series": null,
"slug": "yumeko-jabami-kakegurui-compulsive-gambler",
"trash": 172,
"type": "Waifu",
"url": "https://www.mywaifulist.moe/waifu/yumeko-jabami-kakegurui-compulsive-gambler"
}
]
}
Loading…
Cancel
Save