From 0c510e4713987ab8d3f1c19a48b1726ab0cb7c3a Mon Sep 17 00:00:00 2001 From: Hammy Date: Wed, 7 Jul 2021 01:22:42 +0100 Subject: [PATCH] Add sad path test for ObjectMapper & Add test for betaSearch --- src/main/java/me/goudham/APIMapper.java | 8 +- src/main/java/me/goudham/APIWrapper.java | 6 +- .../java/me/goudham/MyWaifuClientTest.java | 337 +++++++++++------- src/test/java/me/goudham/util/TestEntity.java | 89 ++++- src/test/resources/betaSearch.json | 34 ++ 5 files changed, 324 insertions(+), 150 deletions(-) create mode 100644 src/test/resources/betaSearch.json diff --git a/src/main/java/me/goudham/APIMapper.java b/src/main/java/me/goudham/APIMapper.java index 12d0bdb..1b543c3 100644 --- a/src/main/java/me/goudham/APIMapper.java +++ b/src/main/java/me/goudham/APIMapper.java @@ -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; + } } diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index 981e9a5..40fae3a 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -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; } } diff --git a/src/test/java/me/goudham/MyWaifuClientTest.java b/src/test/java/me/goudham/MyWaifuClientTest.java index 6161f98..ccddc89 100644 --- a/src/test/java/me/goudham/MyWaifuClientTest.java +++ b/src/test/java/me/goudham/MyWaifuClientTest.java @@ -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 expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); - - doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - - Response 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 expectedBestWaifus = TestEntity.getBestWaifus(); - HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); - - doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - - Response> 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 expectedWaifuImages = TestEntity.getWaifuImages(); - HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); - - doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - - Response> 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 buildHttpResponse(int statusCode, String body) { - return new HttpResponse<>() { - @Override - public int statusCode() { - return statusCode; - } - - @Override - public HttpRequest request() { - return null; - } - - @Override - public Optional> previousResponse() { - return Optional.empty(); - } - - @Override - public HttpHeaders headers() { - return null; - } - - @Override - public String body() { - return body; - } - - @Override - public Optional 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 expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response 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 expectedBestWaifus = TestEntity.getBestWaifus(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> 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 expectedWaifuImages = TestEntity.getWaifuImages(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> 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 expectedBetaSearch = TestEntity.getBetaSearch(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> 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 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 buildHttpResponse(int statusCode, String body) { + return new HttpResponse<>() { + @Override + public int statusCode() { + return statusCode; + } + + @Override + public HttpRequest request() { + return null; + } + + @Override + public Optional> previousResponse() { + return Optional.empty(); + } + + @Override + public HttpHeaders headers() { + return null; + } + + @Override + public String body() { + return body; + } + + @Override + public Optional sslSession() { + return Optional.empty(); + } + + @Override + public URI uri() { + return null; + } + + @Override + public HttpClient.Version version() { + return null; + } + }; + } } diff --git a/src/test/java/me/goudham/util/TestEntity.java b/src/test/java/me/goudham/util/TestEntity.java index 2a20e74..761a80c 100644 --- a/src/test/java/me/goudham/util/TestEntity.java +++ b/src/test/java/me/goudham/util/TestEntity.java @@ -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 getBetaSearch() { + List betaSearchResults = new ArrayList<>(); + + List 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 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; } diff --git a/src/test/resources/betaSearch.json b/src/test/resources/betaSearch.json new file mode 100644 index 0000000..4555059 --- /dev/null +++ b/src/test/resources/betaSearch.json @@ -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" + } + ] +} \ No newline at end of file