diff --git a/src/test/java/com/example/schoolproject/controller/TaskControllerTest.java b/src/test/java/com/example/schoolproject/controller/TaskControllerTest.java new file mode 100644 index 0000000..fddbe7b --- /dev/null +++ b/src/test/java/com/example/schoolproject/controller/TaskControllerTest.java @@ -0,0 +1,104 @@ +package com.example.schoolproject.controller; + +import com.example.schoolproject.dto.TaskDTO; +import com.example.schoolproject.entity.Task; +import com.example.schoolproject.entity.TaskStatus; +import com.example.schoolproject.integration.PostgresContainer; +import com.example.schoolproject.repository.TaskRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +public class TaskControllerTest extends PostgresContainer { + + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + @Autowired + private MockMvc mockMvc; + + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private TaskRepository repository; + + private Long savedTaskId; + + @BeforeEach + void setUp() { + repository.deleteAll(); + Task saved = repository.save(getTask()); + savedTaskId = saved.getId(); + } + + private Task getTask() { + return new Task(null, "title", "description", 10L, TaskStatus.TODO); + } + + private TaskDTO getTaskDTO() { + return new TaskDTO("title", "description", 10L, TaskStatus.TODO); + } + + @Test + void shouldReturnAllTasks() throws Exception { + mockMvc.perform(get("/tasks")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].title").value("title")) + .andExpect(jsonPath("$[0].description").value("description")) + .andExpect(jsonPath("$[0].userId").value(10L)); + } + + @Test + void shouldReturnTaskById() throws Exception { + mockMvc.perform(get("/tasks/" + savedTaskId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("title")) + .andExpect(jsonPath("$.description").value("description")) + .andExpect(jsonPath("$.userId").value(10L)); + } + + @Test + void shouldCreateTask() throws Exception { + mockMvc.perform(post("/tasks") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(getTaskDTO()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("title")) + .andExpect(jsonPath("$.description").value("description")) + .andExpect(jsonPath("$.userId").value(10L)); + } + + @Test + void shouldUpdateTask() throws Exception { + mockMvc.perform(put("/tasks/" + savedTaskId) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(getTaskDTO()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("title")) + .andExpect(jsonPath("$.description").value("description")) + .andExpect(jsonPath("$.userId").value(10L)); + } + + @Test + void shouldDeleteTask() throws Exception { + mockMvc.perform(delete("/tasks/" + savedTaskId)) + .andExpect(status().isNoContent()); + } + + @Test + void shouldReturnNotFoundWhenTaskDoesNotExist() throws Exception { + mockMvc.perform(get("/tasks/99999")) + .andExpect(status().isNotFound()) + .andExpect(content().string("Task with id 99999 is not found")); + } +} diff --git a/src/test/java/com/example/schoolproject/integration/PostgresContainer.java b/src/test/java/com/example/schoolproject/integration/PostgresContainer.java new file mode 100644 index 0000000..8b38a1f --- /dev/null +++ b/src/test/java/com/example/schoolproject/integration/PostgresContainer.java @@ -0,0 +1,21 @@ +package com.example.schoolproject.integration; + +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class PostgresContainer { + + @Container + public static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16"); + + @DynamicPropertySource + public static void properties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } +} diff --git a/src/test/java/com/example/schoolproject/mapper/MainMapperTest.java b/src/test/java/com/example/schoolproject/mapper/MainMapperTest.java new file mode 100644 index 0000000..a77e219 --- /dev/null +++ b/src/test/java/com/example/schoolproject/mapper/MainMapperTest.java @@ -0,0 +1,47 @@ +package com.example.schoolproject.mapper; + +import com.example.schoolproject.dto.TaskDTO; +import com.example.schoolproject.dto.TaskStatusUpdateDTO; +import com.example.schoolproject.entity.Task; +import com.example.schoolproject.entity.TaskStatus; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class MainMapperTest { + + private MainMapper mapper = Mappers.getMapper(MainMapper.class); + + private Task getCreatedTask() { + return new Task(1L, "title", "description", 10L, TaskStatus.TODO); + } + + private TaskDTO getCreatedTaskDTO() { + return new TaskDTO("title", "description", 10L, TaskStatus.TODO); + } + + private TaskStatusUpdateDTO getTaskStatusUpdateDTO() { + return new TaskStatusUpdateDTO(1L, TaskStatus.TODO); + } + + @Test + void shouldReturnTaskDTO() { + TaskDTO result = mapper.toDTO(getCreatedTask()); + Assertions.assertEquals(result, getCreatedTaskDTO()); + } + + @Test + void shouldReturnTaskEntity() { + Task result = mapper.toEntity(getCreatedTaskDTO()); + Assertions.assertEquals(result, getCreatedTask()); + } + + @Test + void shouldReturnTaskStatusUpdateDTO() { + TaskStatusUpdateDTO result = mapper.toStatusUpdateDTO(getCreatedTask()); + Assertions.assertEquals(result, getTaskStatusUpdateDTO()); + } +} diff --git a/src/test/java/com/example/schoolproject/service/TaskServiceTest.java b/src/test/java/com/example/schoolproject/service/TaskServiceTest.java new file mode 100644 index 0000000..9d46905 --- /dev/null +++ b/src/test/java/com/example/schoolproject/service/TaskServiceTest.java @@ -0,0 +1,162 @@ +package com.example.schoolproject.service; + +import com.example.schoolproject.dto.TaskDTO; +import com.example.schoolproject.dto.TaskStatusUpdateDTO; +import com.example.schoolproject.entity.Task; +import com.example.schoolproject.entity.TaskStatus; +import com.example.schoolproject.exception.TaskNotFoundException; +import com.example.schoolproject.kafka.KafkaTaskProducer; +import com.example.schoolproject.mapper.MainMapper; +import com.example.schoolproject.repository.TaskRepository; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class TaskServiceTest { + + @Mock + private TaskRepository taskRepository; + + @Mock + private KafkaTaskProducer kafkaTaskProducer; + + @Mock + private MainMapper mapper; + + private static final Long ID = 1L; + private static final String TASKS_NOT_FOUND = "Tasks are not found"; + private static final String TASK_NOT_FOUND = "Task with id " + ID + " is not found"; + + @BeforeEach + void setUp() { + } + + private Task getCreatedTask() { + return new Task(ID, "title", "description", 10L, TaskStatus.TODO); + } + + private TaskDTO getCreatedTaskDTO() { + return new TaskDTO("title", "description", 10L, TaskStatus.TODO); + } + + private Task getUpdatedTask() { + return new Task(ID, "newTitle", "newDescription", 11L, TaskStatus.DONE); + } + + private TaskDTO getUpdatedTaskDTO() { + return new TaskDTO("newTitle", "newDescription", 11L, TaskStatus.DONE); + } + + private TaskStatusUpdateDTO getTaskStatusUpdateDTO() { + return new TaskStatusUpdateDTO(ID, TaskStatus.DONE); + } + + private TaskService getTaskService() { + return new TaskServiceImpl(taskRepository, mapper, kafkaTaskProducer); + } + + @Test + void shouldReturnTaskByIdWhenExist() { + Task task = getCreatedTask(); + TaskDTO taskDTO = getCreatedTaskDTO(); + TaskService service = getTaskService(); + when(taskRepository.findById(ID)).thenReturn(Optional.of(task)); + when(mapper.toDTO(task)).thenReturn(taskDTO); + TaskDTO result = service.getTaskById(ID); + Assertions.assertNotNull(result); + assertEquals(taskDTO, result); + verify(taskRepository, times(1)).findById(ID); + } + + @Test + void shouldTrowExceptionWhenTaskNotFoundById() { + TaskService service = getTaskService(); + when(taskRepository.findById(ID)).thenReturn(Optional.empty()); + TaskNotFoundException exception = Assertions.assertThrows(TaskNotFoundException.class, () -> service.getTaskById(ID)); + verify(taskRepository).findById(any()); + assertEquals(TASK_NOT_FOUND, exception.getMessage()); + } + + @Test + void shouldReturnAllTasksWhenExist() { + Task task = getCreatedTask(); + TaskDTO taskDTO = getCreatedTaskDTO(); + TaskService service = getTaskService(); + when(taskRepository.findAll()).thenReturn(List.of(task)); + when(mapper.toDTO(task)).thenReturn(taskDTO); + List result = service.getAllTasks(); + Assertions.assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(taskDTO, result.get(0)); + verify(taskRepository, times(1)).findAll(); + } + + @Test + void shouldThrowExceptionWhenTasksNotFound() { + TaskService service = getTaskService(); + when(taskRepository.findAll()).thenReturn(Collections.emptyList()); + TaskNotFoundException exception = Assertions.assertThrows(TaskNotFoundException.class, () -> service.getAllTasks()); + verify(taskRepository).findAll(); + assertEquals(TASKS_NOT_FOUND, exception.getMessage()); + } + + @Test + void shouldDeleteTaskWhenExist() { + TaskService service = getTaskService(); + when(taskRepository.existsById(ID)).thenReturn(true); + service.deleteTask(ID); + verify(taskRepository, times(1)).existsById(ID); + verify(taskRepository, times(1)).deleteById(ID); + } + + @Test + void shouldThrowExceptionInsteadDeleting() { + TaskService service = getTaskService(); + when(taskRepository.existsById(ID)).thenReturn(false); + TaskNotFoundException exception = Assertions.assertThrows(TaskNotFoundException.class, () -> service.deleteTask(ID)); + verify(taskRepository).existsById(ID); + verify(taskRepository, never()).deleteById(any()); + assertEquals(TASK_NOT_FOUND, exception.getMessage()); + } + + @Test + void shouldCreateTask() { + Task task = getCreatedTask(); + TaskDTO taskDTO = getCreatedTaskDTO(); + TaskService service = getTaskService(); + when(mapper.toEntity(taskDTO)).thenReturn(task); + when(taskRepository.save(task)).thenReturn(task); + when(mapper.toDTO(task)).thenReturn(taskDTO); + TaskDTO result = service.createTask(taskDTO); + assertEquals(taskDTO, result); + verify(taskRepository, times(1)).save(task); + } + + @Test + void shouldUpdateTask() { + Task task = getCreatedTask(); + TaskService service = getTaskService(); + Task updatedTask = getUpdatedTask(); + TaskDTO updatedTaskDTO = getUpdatedTaskDTO(); + TaskStatusUpdateDTO taskStatusUpdateDTO = getTaskStatusUpdateDTO(); + when(taskRepository.findById(ID)).thenReturn(Optional.of(task)); + when(taskRepository.save(any(Task.class))).thenReturn(updatedTask); + when(mapper.toDTO(updatedTask)).thenReturn(updatedTaskDTO); + when(mapper.toStatusUpdateDTO(updatedTask)).thenReturn(taskStatusUpdateDTO); + TaskDTO result = service.updateTask(task.getId(), updatedTaskDTO); + assertEquals(updatedTaskDTO, result); + verify(taskRepository, times(1)).save(updatedTask); + verify(kafkaTaskProducer).sendTo(any(), eq(taskStatusUpdateDTO)); + } +}