diff --git a/pom.xml b/pom.xml index 2c23e76..60941b5 100644 --- a/pom.xml +++ b/pom.xml @@ -24,30 +24,38 @@ 11 - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-validation - - - org.postgresql - postgresql - runtime - - - org.springframework.boot - spring-boot-starter-test - test - - + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.postgresql + postgresql + runtime + + + com.h2database + h2 + runtime + + + @@ -58,5 +66,4 @@ - diff --git a/src/test/java/com/example/postgresdemo/controller/QuestionControllerTest.java b/src/test/java/com/example/postgresdemo/controller/QuestionControllerTest.java new file mode 100644 index 0000000..15229c4 --- /dev/null +++ b/src/test/java/com/example/postgresdemo/controller/QuestionControllerTest.java @@ -0,0 +1,183 @@ +package com.example.postgresdemo.controller; + +import com.example.postgresdemo.model.Question; +import com.example.postgresdemo.repository.QuestionRepository; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + + +import java.nio.CharBuffer; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest + +@AutoConfigureMockMvc +public class QuestionControllerTest { + @Autowired + private QuestionRepository questionRepository; + + @Autowired + private MockMvc mockMvc; + + @AfterEach + void deleteQuestions() { + questionRepository.deleteAll(); + } + + @Test + void testGetQuestionsWithAmountLessThanPageSize() throws Exception { + int assertionNumber = 10; + int pageSize = 20; + + fillQuestions(assertionNumber); + + mockMvc.perform(get("/questions") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.jsonPath("$.content.length()", Matchers.equalTo(assertionNumber))) + .andExpect(MockMvcResultMatchers.jsonPath("$.totalElements", Matchers.equalTo(assertionNumber))) + .andExpect(MockMvcResultMatchers.jsonPath("$.totalPages", Matchers.equalTo(1))); + } + + @Test + void testGetQuestionsWithAmountMoreThanPageSize() throws Exception { + int assertionNumber = 30; + int pageSize = 20; + int totalPages = (int) Math.ceil(assertionNumber / (double) pageSize); + + fillQuestions(assertionNumber); + + mockMvc.perform(get("/questions") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.jsonPath("$.content.length()", Matchers.equalTo(pageSize))) + .andExpect(MockMvcResultMatchers.jsonPath("$.totalElements", Matchers.equalTo(assertionNumber))) + .andExpect(MockMvcResultMatchers.jsonPath("$.totalPages", Matchers.equalTo(totalPages))); + } + + @Test + void testCreateCorrectQuestion() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/questions") + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"title\": \"Question 1\",\n" + + " \"description\": \"Description 1\"\n" + + "}")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.jsonPath("$.title", Matchers.equalTo("Question 1"))) + .andExpect(MockMvcResultMatchers.jsonPath("$.description", Matchers.equalTo("Description 1"))); + } + + @Test + void testCreateQuestionWithoutTitle() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/questions") + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"description\": \"Description\"\n" + + "}")) + .andExpect(status().is4xxClientError()); + } + + @Test + void testCreateQuestionWithTitleLesThenThreeChars() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/questions") + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"title\": \"Te\",\n" + + " \"description\": \"Description\"\n" + + "}")) + .andExpect(status().is4xxClientError()); + } + + @Test + void testCreateQuestionWithTitleMoreThenHundredChars() throws Exception { + int numberOfChars = 101; + String title = CharBuffer.allocate(numberOfChars).toString().replace('\0', 'T'); + + mockMvc.perform(MockMvcRequestBuilders.post("/questions") + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"title\": \"" + title + "\",\n" + + " \"description\": \"Description\"\n" + + "}")) + .andExpect(status().is4xxClientError()); + } + + @Test + void testCreateQuestionWithoutDescription() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/questions") + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"title\": \"Question 1\"\n" + + "}")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.jsonPath("$.title", Matchers.equalTo("Question 1"))) + .andExpect(MockMvcResultMatchers.jsonPath("$.description", Matchers.equalTo(null))); + } + + @Test + void testUpdateQuestion() throws Exception { + fillQuestions(1); + long questionId = questionRepository.findAll().get(0).getId(); + + mockMvc.perform(MockMvcRequestBuilders.put("/questions/" + questionId) + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"title\": \"Edited Question 1\",\n" + + " \"description\": \"Edited Description 1\"\n" + + "}")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.jsonPath("$.title", Matchers.equalTo("Edited Question 1"))) + .andExpect(MockMvcResultMatchers.jsonPath("$.description", Matchers.equalTo("Edited Description 1"))); + } + + @Test + void testUpdateQuestionWithNonExistingId() throws Exception { + fillQuestions(1); + long questionId = questionRepository.findAll().get(0).getId(); + + mockMvc.perform(MockMvcRequestBuilders.put("/questions/" + (questionId + 1)) + .contentType(MediaType.APPLICATION_JSON) + .content("{\n" + + " \"title\": \"Edited Question 1\",\n" + + " \"description\": \"Edited Description 1\"\n" + + "}")) + .andExpect(status().is4xxClientError()); + } + + @Test + void testDeleteQuestion() throws Exception { + fillQuestions(1); + long questionId = questionRepository.findAll().get(0).getId(); + + mockMvc.perform(MockMvcRequestBuilders.delete("/questions/" + questionId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + private void fillQuestions(Integer number) { + for (int i = 0; i < number; i++) { + Question question = new Question(); + question.setTitle("Question " + i); + question.setDescription("Description " + i); + questionRepository.save(question); + } + } +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..6d94c03 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,14 @@ +spring.datasource.url=jdbc:h2:mem:test;MODE=PostgreSQL; +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=${DB_USER} +spring.datasource.password=${DB_PASSWORD} +# We add the MySQL Dialect so that it understands and generates the query based on MySQL +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect + +spring.h2.console.enabled=true +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.format_sql=true +#spring.jpa.properties.hibernate.show_sql=true + + +spring.sql.init.mode=always