From 894a61334c266fb593fbad831c3b819caaa45218 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 25 Dec 2024 18:40:03 +0300 Subject: [PATCH] Add new tests (#123) * test: added tests * style: Refactoring test code to improve readability * test: updated tests * test: fixed errors * test: degug in ci * test: debug in ci * test: fixed errors --- .../DictionaryServiceWebClientTest.java | 7 +- .../GoogleTranslatorWebClientTest.java | 148 +++++++++++++++++ .../service/state/StateServiceSqliteTest.java | 152 ++++++++++++++++++ 3 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 src/test/java/ru/dankoy/korvotoanki/core/service/googletrans/GoogleTranslatorWebClientTest.java create mode 100644 src/test/java/ru/dankoy/korvotoanki/core/service/state/StateServiceSqliteTest.java diff --git a/src/test/java/ru/dankoy/korvotoanki/core/service/dictionaryapi/DictionaryServiceWebClientTest.java b/src/test/java/ru/dankoy/korvotoanki/core/service/dictionaryapi/DictionaryServiceWebClientTest.java index b5e99b0..a2ef51d 100644 --- a/src/test/java/ru/dankoy/korvotoanki/core/service/dictionaryapi/DictionaryServiceWebClientTest.java +++ b/src/test/java/ru/dankoy/korvotoanki/core/service/dictionaryapi/DictionaryServiceWebClientTest.java @@ -20,7 +20,6 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.web.reactive.function.client.WebClient; import ru.dankoy.korvotoanki.config.WebClientConfig; @@ -39,11 +38,11 @@ ObjectMapper.class, DictionaryServiceWebClient.class }) -@TestPropertySource(properties = "korvo-to-anki.http-client=web-client") +// @TestPropertySource(properties = "korvo-to-anki.http-client=web-client") class DictionaryServiceWebClientTest { private static MockWebServer server; - private String mockUrl = ""; + private String mockUrl = "http://127.0.0.1:%s/"; @BeforeAll static void setUp() throws IOException { @@ -64,7 +63,7 @@ static void tearDown() throws IOException { @BeforeEach void initialize() { - mockUrl = String.format("http://localhost:%s/", server.getPort()); + mockUrl = String.format(mockUrl, server.getPort()); } @DisplayName("correct translation") diff --git a/src/test/java/ru/dankoy/korvotoanki/core/service/googletrans/GoogleTranslatorWebClientTest.java b/src/test/java/ru/dankoy/korvotoanki/core/service/googletrans/GoogleTranslatorWebClientTest.java new file mode 100644 index 0000000..d9820db --- /dev/null +++ b/src/test/java/ru/dankoy/korvotoanki/core/service/googletrans/GoogleTranslatorWebClientTest.java @@ -0,0 +1,148 @@ +package ru.dankoy.korvotoanki.core.service.googletrans; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; + +import java.io.IOException; +import java.util.List; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.web.reactive.function.client.WebClient; +import ru.dankoy.korvotoanki.config.WebClientConfig; +import ru.dankoy.korvotoanki.config.appprops.AppProperties; +import ru.dankoy.korvotoanki.config.appprops.GoogleParamsProperties; +import ru.dankoy.korvotoanki.config.appprops.GoogleTranslatorProperties; +import ru.dankoy.korvotoanki.core.domain.googletranslation.GoogleTranslation; +import ru.dankoy.korvotoanki.core.exceptions.GoogleTranslatorException; +import ru.dankoy.korvotoanki.core.service.googletrans.parser.GoogleTranslatorParser; + +@DisplayName("Test GoogleTranslatorWebClient ") +@SpringBootTest( + classes = { + GoogleTranslatorWebClient.class, + GoogleTranslatorProperties.class, + GoogleParamsProperties.class, + AppProperties.class, + WebClient.class, + WebClientConfig.class + }) +// @TestPropertySource(properties = "korvo-to-anki.http-client=web-client") +class GoogleTranslatorWebClientTest { + + private MockWebServer server; + private String mockUrl = "http://127.0.0.1:%s/"; + + @MockitoBean private GoogleTranslatorParser googleTranslatorParser; + + @MockitoBean private GoogleTranslatorProperties googleTranslatorProperties; + + @MockitoBean private GoogleParamsProperties properties; + + @Autowired private GoogleTranslatorWebClient googleTranslatorWebClient; + + @BeforeEach + void setUpBeans() throws IOException { + + server = new MockWebServer(); + server.start(); + + mockUrl = String.format(mockUrl, server.getPort()); + + given(googleTranslatorProperties.getGoogleTranslatorUrl()).willReturn(mockUrl); + given(googleTranslatorProperties.getGoogleParamsProperties()).willReturn(properties); + given(properties.getClient()).willReturn("client"); + given(properties.getIe()).willReturn("UTF-8"); + given(properties.getOe()).willReturn("UTF-8"); + given(properties.getHl()).willReturn("en"); + given(properties.getOtf()).willReturn(0); + given(properties.getSsel()).willReturn(0); + given(properties.getTsel()).willReturn(0); + + given(googleTranslatorParser.parse(anyString())).willReturn(createGoogleTranslation()); + } + + @AfterEach + void tearDownMockWebServer() throws IOException { + server.shutdown(); + } + + @Test + void testTranslationSuccess() throws InterruptedException { + + server.enqueue( + new MockResponse().setBody("Some response").addHeader("Content-Type", "application/json")); + + GoogleTranslation translation = + googleTranslatorWebClient.translate("text", "en", "ru", List.of("option1")); + + RecordedRequest recordedRequest = server.takeRequest(); + + // do not assert hostname and port because localhost changes to 127.0.0.1 and 127.0.0.1 changes + // to localhost. Always fails assertion. + assertEquals( + "/?client=client&sl=ru&tl=en&ie=UTF-8&oe=UTF-8&hl=en&otf=0&ssel=0&tsel=0&dt=option1&q=text", + recordedRequest.getPath()); + + assertThat(translation).isEqualTo(createGoogleTranslation()); + } + + @Test + void testTranslationError_ExpectsGoogleTranslatorException() throws InterruptedException { + + server.enqueue( + new MockResponse() + .setResponseCode(400) + .setBody("Some error") + .addHeader("Content-Type", "application/json")); + + var list = List.of("option1"); + + assertThatThrownBy(() -> googleTranslatorWebClient.translate("text", "en", "ru", list)) + .isInstanceOf(GoogleTranslatorException.class); + + RecordedRequest recordedRequest = server.takeRequest(); + + // do not assert hostname and port because localhost changes to 127.0.0.1 and 127.0.0.1 changes + // to localhost. Always fails assertion. + assertEquals( + "/?client=client&sl=ru&tl=en&ie=UTF-8&oe=UTF-8&hl=en&otf=0&ssel=0&tsel=0&dt=option1&q=text", + recordedRequest.getPath()); + } + + @Test + void testTranslationError_ExpectsParsingException() throws InterruptedException { + + server.enqueue(new MockResponse().setResponseCode(200).setBody("Some body")); + + given(googleTranslatorParser.parse(anyString())) + .willThrow(new GoogleTranslatorException("some error", new RuntimeException())); + + var list = List.of("option1"); + + assertThatThrownBy(() -> googleTranslatorWebClient.translate("text", "en", "ru", list)) + .isInstanceOf(GoogleTranslatorException.class); + + RecordedRequest recordedRequest = server.takeRequest(); + + // do not assert hostname and port because localhost changes to 127.0.0.1 and 127.0.0.1 changes + // to localhost. Always fails assertion. + assertEquals( + "/?client=client&sl=ru&tl=en&ie=UTF-8&oe=UTF-8&hl=en&otf=0&ssel=0&tsel=0&dt=option1&q=text", + recordedRequest.getPath()); + } + + private GoogleTranslation createGoogleTranslation() { + return new GoogleTranslation("Hello, world!"); + } +} diff --git a/src/test/java/ru/dankoy/korvotoanki/core/service/state/StateServiceSqliteTest.java b/src/test/java/ru/dankoy/korvotoanki/core/service/state/StateServiceSqliteTest.java new file mode 100644 index 0000000..9244595 --- /dev/null +++ b/src/test/java/ru/dankoy/korvotoanki/core/service/state/StateServiceSqliteTest.java @@ -0,0 +1,152 @@ +package ru.dankoy.korvotoanki.core.service.state; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import ru.dankoy.korvotoanki.core.dao.state.StateDao; +import ru.dankoy.korvotoanki.core.domain.Title; +import ru.dankoy.korvotoanki.core.domain.Vocabulary; +import ru.dankoy.korvotoanki.core.domain.state.State; + +@DisplayName("Testing of state service") +@ExtendWith(MockitoExtension.class) +class StateServiceSqliteTest { + + @Mock private StateDao stateDao; + + @InjectMocks private StateServiceSqlite stateService; + + /** Check states returned by service. */ + @Test + @DisplayName("Check states returned by service") + void testCheckState() { + // Arrange + List states = + Stream.of( + new State(1L, "word1", LocalDateTime.now()), + new State(2L, "word2", LocalDateTime.now())) + .toList(); + when(stateDao.getAll()).thenReturn(states); + + // Act + List result = stateService.checkState(); + + // Assert + assertThat(result).isEqualTo(states); + } + + /** Filter State with empty final state list returns all vocabularies. */ + @Test + @DisplayName("Filter State with empty final state list returns all vocabularies") + void testFilterState_EmptyFinalStateList_ReturnsAllVocabularies() { + // Arrange + List vocabularies = createVocabularyList(); + when(stateDao.getAll()).thenReturn(List.of()); + + // Act + List result = stateService.filterState(vocabularies); + + // Assert + assertThat(result).isEqualTo(vocabularies); + } + + /** Filter State with final state list containing vocabulary returns only missing vocabularies. */ + @Test + @DisplayName( + "Filter State with final state list containing vocabulary returns only missing vocabularies") + void testFilterState_FinalStateListContainsVocabulary_ReturnsOnlyMissingVocabularies() { + // Arrange + List vocabularies = createVocabularyList(); + + State state = new State(1L, "word1", LocalDateTime.now()); + when(stateDao.getAll()).thenReturn(List.of(state)); + + // Act + List result = stateService.filterState(vocabularies); + + // Assert + assertThat(result).isEqualTo(Stream.of(getVocabularyByWord("word2")).toList()); + } + + /** Save States. */ + @Test + @DisplayName("Save States") + void testSaveState() { + // Arrange + List vocabularies = new ArrayList<>(createVocabularyList()); + + when(stateDao.batchInsert(any())).thenReturn(new int[vocabularies.size()]); + + // Act + stateService.saveState(vocabularies); + + // Assert + assertThat(stateDao.getAll()).isNotNull(); + } + + /** Save State with transactional save operation. */ + @Test + @DisplayName("Save State with transactional save operation") + void testSaveState_Transactional() { + // Arrange + List vocabularies = createVocabularyList(); + + when(stateDao.batchInsert(any())) + .thenThrow(new RuntimeException("Error occurred in transaction")); + + // Act and Assert (Expecting exception) + assertThatThrownBy(() -> stateService.saveState(vocabularies)) + .isInstanceOf(RuntimeException.class); + } + + /** + * Creates a list of vocabulary objects. + * + * @return A list of vocabulary objects + */ + private List createVocabularyList() { + + var title = new Title(1L, "Title1", 1L); + + return Stream.of( + new Vocabulary( + "word1", + title, + 1695239837, + 1695239837, + 1695240137, + 0, + "combined forces.” He hoped to the gods it didn’t come to that.\n" + + "She fell silent, ", + " a gratifying slaughter. Maybe even the final battle that would confirm her" + + " mastery. Most of all", + 0), + new Vocabulary("word2", title, 1695239837, 1695239837, 1695240137, 0, null, null, 0)) + .toList(); + } + + /** + * Retrieves a vocabulary object by its word. + * + * @param word The word to retrieve the vocabulary for + * @return The vocabulary object or null if not found + */ + private Vocabulary getVocabularyByWord(String word) { + return createVocabularyList().stream() + .filter(v -> v.word().equals(word)) + .findFirst() + .orElse(null); + } +}