history = listOps.range(HISTORY_KEY, 0, -1);
+ if (history == null) {
+ log.warn("History is empty or failed to retrieve data from Redis");
+ return new ArrayList<>();
+ }
+ log.debug("Successfully retrieved {} entries from history", history.size());
+ return history;
+ } catch (Exception e) {
+ log.error("Failed to retrieve history: {}", e.getMessage(), e);
+ return new ArrayList<>();
+ }
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/service/task/manager/service/impl/SubtaskServiceImpl.java b/service/src/main/java/service/task/manager/service/impl/SubtaskServiceImpl.java
new file mode 100644
index 0000000..ad4df3c
--- /dev/null
+++ b/service/src/main/java/service/task/manager/service/impl/SubtaskServiceImpl.java
@@ -0,0 +1,192 @@
+package service.task.manager.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import service.task.manager.dto.epic.EpicResponseDto;
+import service.task.manager.dto.subtask.SubtaskRequestCreatedDto;
+import service.task.manager.dto.subtask.SubtaskRequestUpdatedDto;
+import service.task.manager.dto.subtask.SubtaskResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.mapper.EpicMapper;
+import service.task.manager.mapper.SubtaskMapper;
+import service.task.manager.model.Epic;
+import service.task.manager.model.Subtask;
+import service.task.manager.model.enums.Status;
+import service.task.manager.repository.SubtaskRepository;
+import service.task.manager.service.EpicService;
+import service.task.manager.service.HistoryService;
+import service.task.manager.service.SubtaskService;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Service implementation for managing subtasks.
+ * Provides methods to create, update, retrieve, and delete subtasks associated with epics.
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SubtaskServiceImpl implements SubtaskService {
+ private final SubtaskRepository repository;
+ private final EpicService epicService;
+ private final SubtaskMapper mapper;
+ private final EpicMapper epicMapper;
+ private final HistoryService history;
+
+ /**
+ * Creates a new subtask based on the provided DTO.
+ *
+ * This method verifies the existence of the associated epic and checks for duplicate subtask names.
+ * If the epic does not exist, a {@link NotFoundException} will be thrown by the EpicService.
+ * If a subtask with the same name already exists, a {@link ConflictException} is thrown.
+ *
+ * @param dto The DTO containing subtask creation data (must include name and epic ID).
+ * @throws ConflictException If a subtask with the same name already exists.
+ * @throws NotFoundException If the associated epic does not exist.
+ */
+ @Transactional
+ @Override
+ public void create(SubtaskRequestCreatedDto dto) {
+ log.info("Attempting to create subtask with name: {}", dto.name());
+
+ EpicResponseDto epicDto = epicService.findById(dto.epicId());
+ Epic epic = epicMapper.toEntity(epicDto);
+
+ if (repository.existsByName(dto.name())) {
+ log.warn("Subtask creation failed: Subtask with name {} already exists", dto.name());
+ throw new ConflictException("Subtask with name " + dto.name() + " already exists");
+ }
+
+ Subtask subtask = addEndTimeSubtaskAndStatus(mapper.toEntity(dto));
+ subtask.setEpic(epic);
+ repository.save(subtask);
+ log.info("Subtask created successfully with name: {}", dto.name());
+ }
+
+ /**
+ * Updates an existing subtask with the provided data.
+ *
+ * This method retrieves the subtask by its ID, updates its fields using the provided DTO,
+ * and saves the changes to the database.
+ *
+ * @param dto The DTO containing updated subtask data (must include subtask ID).
+ * @return The updated subtask as a DTO.
+ * @throws NotFoundException If the subtask with the specified ID does not exist.
+ */
+ @Transactional
+ @Override
+ public SubtaskResponseDto update(SubtaskRequestUpdatedDto dto) {
+ log.info("Attempting to update subtask with ID: {}", dto.id());
+
+ Subtask existingSubtask = mapper.toEntity(findById(dto.id()));
+ mapper.updateSubtaskFromDto(dto, existingSubtask);
+ Subtask updatedSubtask = repository.save(existingSubtask);
+ log.info("Subtask with ID {} updated successfully", updatedSubtask.getId());
+ return mapper.toResponseDto(updatedSubtask);
+ }
+
+ /**
+ * Retrieves a subtask by its ID.
+ * @param id The ID of the subtask to retrieve.
+ * @return The subtask as a DTO.
+ * @throws NotFoundException If the subtask with the specified ID does not exist.
+ */
+ @Transactional(readOnly = true)
+ @Override
+ public SubtaskResponseDto findById(Long id) {
+ log.info("Fetching subtask with ID: {}", id);
+ SubtaskResponseDto subtask = repository.findById(id).stream()
+ .map(mapper::toResponseDto)
+ .findFirst()
+ .orElseThrow(() -> {
+ log.warn("Subtask with ID {} not found", id);
+ return new NotFoundException("Subtask with ID " + id + " not found");
+ });
+ history.addToHistory(subtask.type(),subtask.id());
+ log.info("Subtask with ID {} retrieved successfully", id);
+ return subtask;
+ }
+
+ /**
+ * Retrieves all subtasks.
+ * @return A list of all subtasks as DTOs.
+ */
+ @Transactional(readOnly = true)
+ @Override
+ public List findAll() {
+ log.info("Fetching all subtasks");
+ List subtasks = repository.findAll().stream()
+ .map(mapper::toResponseDto)
+ .toList();
+ log.info("Retrieved {} subtasks", subtasks.size());
+ return subtasks;
+ }
+
+ /**
+ * Deletes a subtask by its ID.
+ * @param id The ID of the subtask to delete.
+ * @throws NotFoundException If the subtask with the specified ID does not exist.
+ */
+ @Transactional
+ @Override
+ public void delete(Long id) {
+ log.info("Attempting to delete subtask with ID: {}", id);
+ findById(id); // Проверяет существование
+ repository.deleteById(id);
+ log.info("Subtask with ID {} deleted successfully", id);
+ }
+
+ /**
+ * Retrieves all subtasks sorted by priority and end time.
+ *
+ * Epics are sorted first by status in the order: IN_PROGRESS, NEW, DONE.
+ * Within each status group, subtasks are sorted by end time (earliest first).
+ *
+ *
+ * @return A list of all subtasks as DTOs, sorted by priority and end time.
+ */
+ @Override
+ public List prioritized() {
+ log.info("Fetching all subtasks sorted by priority and end time");
+ List subtask = repository.findAll()
+ .stream()
+ .map(mapper::toResponseDto)
+ .sorted(Comparator
+ .comparing(this::getStatusPriority)
+ .thenComparing(SubtaskResponseDto::endTime))
+ .toList();
+ log.info("Retrieved {} prioritized subtasks", subtask.size());
+ return subtask;
+ }
+
+ /**
+ * Helper method to assign priority based on status.
+ *
+ * @param dto The subtask DTO.
+ * @return The priority value (lower means higher priority).
+ */
+ private int getStatusPriority(SubtaskResponseDto dto) {
+ return switch (dto.status()) {
+ case IN_PROGRESS -> 1;
+ case NEW -> 2;
+ case DONE -> 3;
+ };
+ }
+
+ /**
+ * Sets the status to NEW and calculates the end time for the given subtask.
+ * @param subtask The subtask to modify.
+ * @return The modified subtask with updated status and end time.
+ */
+ private Subtask addEndTimeSubtaskAndStatus(Subtask subtask) {
+ log.debug("Setting status and calculating end time for subtask");
+ subtask.calculateEndTime();
+ subtask.setStatus(Status.NEW);
+ log.debug("Subtask status set to NEW and end time calculated");
+ return subtask;
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/service/task/manager/service/impl/TaskServiceImpl.java b/service/src/main/java/service/task/manager/service/impl/TaskServiceImpl.java
new file mode 100644
index 0000000..3556d49
--- /dev/null
+++ b/service/src/main/java/service/task/manager/service/impl/TaskServiceImpl.java
@@ -0,0 +1,170 @@
+package service.task.manager.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import service.task.manager.dto.task.TaskRequestCreatedDto;
+import service.task.manager.dto.task.TaskRequestUpdatedDto;
+import service.task.manager.dto.task.TaskResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.mapper.TaskMapper;
+import service.task.manager.model.Task;
+import service.task.manager.model.enums.Status;
+import service.task.manager.repository.TaskRepository;
+import service.task.manager.service.HistoryService;
+import service.task.manager.service.TaskService;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Service implementation for managing tasks.
+ * Provides methods to create, update, retrieve, and delete tasks.
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class TaskServiceImpl implements TaskService {
+ private final TaskRepository repository;
+ private final TaskMapper mapper;
+ private final HistoryService history;
+
+ /**
+ * Creates a new task based on the provided DTO.
+ * @param dto The DTO containing task creation data.
+ * @throws ConflictException If a task with the same name already exists.
+ */
+ @Transactional
+ @Override
+ public void create(TaskRequestCreatedDto dto) {
+ log.info("Attempting to create task with name: {}", dto.name());
+ if (repository.existsByName(dto.name())) {
+ log.warn("Task creation failed: Task with name {} already exists", dto.name());
+ throw new ConflictException("Task with name " + dto.name() + " already exists");
+ }
+
+ Task task = addEndTimeTaskAndStatus(mapper.toEntity(dto));
+ repository.save(task);
+ log.info("Task created successfully with name: {}", dto.name());
+ }
+
+ /**
+ * Updates an existing task with the provided data.
+ * @param dto The DTO containing updated task data.
+ * @return The updated task as a DTO.
+ * @throws NotFoundException If the task with the specified ID does not exist.
+ */
+ @Transactional
+ @Override
+ public TaskResponseDto update(TaskRequestUpdatedDto dto) {
+ log.info("Attempting to update task with ID: {}", dto.id());
+ Task existingTask = mapper.toEntity(findById(dto.id()));
+ mapper.updateTaskFromDto(dto, existingTask);
+ Task updatedTask = repository.save(existingTask);
+ log.info("Task with ID {} updated successfully", updatedTask.getId());
+ return mapper.toResponseDto(updatedTask);
+ }
+
+ /**
+ * Retrieves a task by its ID.
+ * @param id The ID of the task to retrieve.
+ * @return The task as a DTO.
+ * @throws NotFoundException If the task with the specified ID does not exist.
+ */
+ @Transactional(readOnly = true)
+ @Override
+ public TaskResponseDto findById(Long id) {
+ log.info("Fetching task with ID: {}", id);
+ TaskResponseDto task = repository.findById(id).stream()
+ .map(mapper::toResponseDto)
+ .findFirst()
+ .orElseThrow(() -> {
+ log.warn("Task with ID {} not found", id);
+ return new NotFoundException("Task with ID " + id + " not found");
+ });
+ history.addToHistory(task.type(),task.id());
+ log.info("Task with ID {} retrieved successfully", id);
+ return task;
+ }
+
+ /**
+ * Retrieves all tasks.
+ * @return A list of all tasks as DTOs.
+ */
+ @Transactional(readOnly = true)
+ @Override
+ public List findAll() {
+ log.info("Fetching all tasks");
+ List tasks = repository.findAll().stream()
+ .map(mapper::toResponseDto)
+ .toList();
+ log.info("Retrieved {} tasks", tasks.size());
+ return tasks;
+ }
+
+ /**
+ * Deletes a task by its ID.
+ * @param id The ID of the task to delete.
+ * @throws NotFoundException If the task with the specified ID does not exist.
+ */
+ @Transactional
+ @Override
+ public void delete(Long id) {
+ log.info("Attempting to delete task with ID: {}", id);
+ findById(id); // Проверяет существование
+ repository.deleteById(id);
+ log.info("Task with ID {} deleted successfully", id);
+ }
+
+ /**
+ * Retrieves all tasks sorted by priority and end time.
+ *
+ * Epics are sorted first by status in the order: IN_PROGRESS, NEW, DONE.
+ * Within each status group, tasks are sorted by end time (earliest first).
+ *
+ *
+ * @return A list of all tasks as DTOs, sorted by priority and end time.
+ */
+ @Override
+ public List prioritized() {
+ log.info("Fetching all subtasks sorted by priority and end time");
+ List tasks = repository.findAll()
+ .stream()
+ .map(mapper::toResponseDto)
+ .sorted(Comparator
+ .comparing(this::getStatusPriority)
+ .thenComparing(TaskResponseDto::endTime))
+ .toList();
+ log.info("Retrieved {} prioritized subtasks", tasks.size());
+ return tasks;
+ }
+
+ /**
+ * Helper method to assign priority based on status.
+ *
+ * @param dto The task DTO.
+ * @return The priority value (lower means higher priority).
+ */
+ private int getStatusPriority(TaskResponseDto dto) {
+ return switch (dto.status()) {
+ case IN_PROGRESS -> 1;
+ case NEW -> 2;
+ case DONE -> 3;
+ };
+ }
+
+ /**
+ * Sets the status to NEW and calculates the end time for the given task.
+ * @param task The task to modify.
+ * @return The modified task with updated status and end time.
+ */
+ private Task addEndTimeTaskAndStatus(Task task) {
+ log.debug("Setting status and calculating end time for task");
+ task.calculateEndTime();
+ task.setStatus(Status.NEW);
+ log.debug("Task status set to NEW and end time calculated");
+ return task;
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml
new file mode 100644
index 0000000..cd1718f
--- /dev/null
+++ b/service/src/main/resources/application.yml
@@ -0,0 +1,30 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:task-manager
+ username: sa
+ password:
+ driver-class-name: org.h2.Driver
+ sql:
+ init:
+ mode: always
+ schema-locations: classpath:schema.sql
+ jpa:
+ properties:
+ hibernate.dialect: org.hibernate.dialect.H2Dialect
+ data:
+ redis:
+ host: localhost
+ port: 6379
+
+# Путь к Swagger UI
+springdoc:
+ swagger-ui:
+ path: /swagger-ui.html
+ tags-sorter: alpha
+ operations-sorter: alpha
+ info:
+ title: Task Manager API
+ version: 1.0.0
+ api-docs:
+ enabled: true
+ path: /v3/api-docs
\ No newline at end of file
diff --git a/service/src/main/resources/schema.sql b/service/src/main/resources/schema.sql
new file mode 100644
index 0000000..4135a0a
--- /dev/null
+++ b/service/src/main/resources/schema.sql
@@ -0,0 +1,45 @@
+-- Таблица для Epic
+CREATE TABLE epic
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ description TEXT,
+ status VARCHAR(50),
+ start_time TIMESTAMP,
+ duration BIGINT, -- Хранится в секундах
+ end_time TIMESTAMP,
+ type VARCHAR(50) NOT NULL
+);
+
+-- Таблица для Subtask
+CREATE TABLE subtask
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ epic_id BIGINT,
+ name VARCHAR(255) NOT NULL,
+ description TEXT,
+ status VARCHAR(50),
+ start_time TIMESTAMP,
+ duration BIGINT, -- Хранится в секундах
+ end_time TIMESTAMP,
+ type VARCHAR(50) NOT NULL,
+ FOREIGN KEY (epic_id) REFERENCES epic (id) ON DELETE CASCADE
+);
+
+-- Таблица для Task
+CREATE TABLE task
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ description TEXT,
+ status VARCHAR(50),
+ start_time TIMESTAMP,
+ duration BIGINT, -- Хранится в секундах
+ end_time TIMESTAMP,
+ type VARCHAR(50) NOT NULL
+);
+
+-- Опционально: Добавление индексов для оптимизации
+CREATE INDEX idx_epic_status ON epic (status);
+CREATE INDEX idx_subtask_epic_id ON subtask (epic_id);
+CREATE INDEX idx_task_status ON task (status);
\ No newline at end of file
diff --git a/service/src/test/java/service/task/manager/controller/EpicControllerTest.java b/service/src/test/java/service/task/manager/controller/EpicControllerTest.java
new file mode 100644
index 0000000..4fde163
--- /dev/null
+++ b/service/src/test/java/service/task/manager/controller/EpicControllerTest.java
@@ -0,0 +1,253 @@
+package service.task.manager.controller;
+
+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.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import service.task.manager.dto.epic.EpicRequestCreatedDto;
+import service.task.manager.dto.epic.EpicRequestUpdatedDto;
+import service.task.manager.dto.epic.EpicResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.model.enums.Status;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.service.EpicService;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@WebMvcTest(EpicController.class)
+class EpicControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private EpicService epicService;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ private EpicResponseDto epicResponseDto;
+
+ @BeforeEach
+ void setUp() {
+ epicResponseDto = new EpicResponseDto(
+ 1L,
+ Collections.emptyList(), // subtasks
+ "Test Epic",
+ "Test Description",
+ Status.NEW,
+ LocalDateTime.of(2025, 4, 27, 10, 0),
+ Duration.ofHours(24),
+ LocalDateTime.of(2025, 4, 28, 10, 0),
+ TaskType.EPIC
+ );
+ }
+
+ @Test
+ void create_ValidEpic_ReturnsCreated() throws Exception {
+ EpicRequestCreatedDto requestDto = new EpicRequestCreatedDto(
+ "Test Epic",
+ "Test Description",
+ LocalDateTime.of(2025, 4, 27, 10, 0),
+ Duration.ofHours(24)
+ );
+
+ doNothing().when(epicService).create(any(EpicRequestCreatedDto.class));
+
+ mockMvc.perform(post("/epic")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(requestDto)))
+ .andExpect(status().isCreated())
+ .andExpect(content().string(""));
+
+ verify(epicService, times(1)).create(any(EpicRequestCreatedDto.class));
+ }
+
+ @Test
+ void create_DuplicateEpic_ReturnsConflict() throws Exception {
+ EpicRequestCreatedDto requestDto = new EpicRequestCreatedDto(
+ "Duplicate Epic",
+ "Test Description",
+ LocalDateTime.of(2025, 4, 27, 10, 0),
+ Duration.ofHours(24)
+ );
+
+ doThrow(new ConflictException("Epic with the same name already exists"))
+ .when(epicService).create(any(EpicRequestCreatedDto.class));
+
+ mockMvc.perform(post("/epic")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(requestDto)))
+ .andExpect(status().isConflict())
+ .andExpect(jsonPath("$.message").value("Epic with the same name already exists"));
+
+ verify(epicService, times(1)).create(any(EpicRequestCreatedDto.class));
+ }
+
+ @Test
+ void create_InvalidEpic_ReturnsBadRequest() throws Exception {
+ EpicRequestCreatedDto requestDto = new EpicRequestCreatedDto(
+ "", // Пустое имя, что нарушает валидацию
+ "Test Description",
+ LocalDateTime.of(2025, 4, 27, 10, 0),
+ Duration.ofHours(24)
+ );
+
+ mockMvc.perform(post("/epic")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(requestDto)))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.name").value("blank name"));
+
+ verify(epicService, never()).create(any(EpicRequestCreatedDto.class));
+ }
+
+ @Test
+ void update_ValidEpic_ReturnsOk() throws Exception {
+ EpicRequestUpdatedDto requestDto = new EpicRequestUpdatedDto(
+ 1L,
+ "Updated Epic",
+ "Updated Description",
+ Status.IN_PROGRESS,
+ Duration.ofHours(48)
+ );
+
+ when(epicService.update(any(EpicRequestUpdatedDto.class))).thenReturn(epicResponseDto);
+
+ mockMvc.perform(put("/epic")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(requestDto)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.name").value("Test Epic"))
+ .andExpect(jsonPath("$.status").value("NEW"));
+
+ verify(epicService, times(1)).update(any(EpicRequestUpdatedDto.class));
+ }
+
+ @Test
+ void update_EpicNotFound_ReturnsNotFound() throws Exception {
+ EpicRequestUpdatedDto requestDto = new EpicRequestUpdatedDto(
+ 999L,
+ "Updated Epic",
+ "Updated Description",
+ Status.IN_PROGRESS,
+ Duration.ofHours(48)
+ );
+
+ when(epicService.update(any(EpicRequestUpdatedDto.class)))
+ .thenThrow(new NotFoundException("Epic not found"));
+
+ mockMvc.perform(put("/epic")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(requestDto)))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value("Epic not found"));
+
+ verify(epicService, times(1)).update(any(EpicRequestUpdatedDto.class));
+ }
+
+ @Test
+ void findById_ValidId_ReturnsOk() throws Exception {
+ when(epicService.findById(1L)).thenReturn(epicResponseDto);
+
+ mockMvc.perform(get("/epic/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.name").value("Test Epic"))
+ .andExpect(jsonPath("$.status").value("NEW"))
+ .andExpect(jsonPath("$.type").value("EPIC"));
+
+ verify(epicService, times(1)).findById(1L);
+ }
+
+ @Test
+ void findById_EpicNotFound_ReturnsNotFound() throws Exception {
+ when(epicService.findById(999L)).thenThrow(new NotFoundException("Epic not found"));
+
+ mockMvc.perform(get("/epic/999"))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value("Epic not found"));
+
+ verify(epicService, times(1)).findById(999L);
+ }
+
+ @Test
+ void findById_InvalidId_ReturnsBadRequest() throws Exception {
+ mockMvc.perform(get("/epic/-1"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.error").value("id must be positive"));
+
+ verify(epicService, never()).findById(anyLong());
+ }
+
+ @Test
+ void findAll_ReturnsOk() throws Exception {
+ List epics = List.of(epicResponseDto);
+ when(epicService.findAll()).thenReturn(epics);
+
+ mockMvc.perform(get("/epic"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(1))
+ .andExpect(jsonPath("$[0].name").value("Test Epic"))
+ .andExpect(jsonPath("$[0].status").value("NEW"))
+ .andExpect(jsonPath("$[0].type").value("EPIC"));
+
+ verify(epicService, times(1)).findAll();
+ }
+
+ @Test
+ void findAll_EmptyList_ReturnsOk() throws Exception {
+ when(epicService.findAll()).thenReturn(Collections.emptyList());
+
+ mockMvc.perform(get("/epic"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$").isArray())
+ .andExpect(jsonPath("$").isEmpty());
+
+ verify(epicService, times(1)).findAll();
+ }
+
+ @Test
+ void delete_ValidId_ReturnsNoContent() throws Exception {
+ doNothing().when(epicService).delete(1L);
+
+ mockMvc.perform(delete("/epic/1"))
+ .andExpect(status().isNoContent());
+
+ verify(epicService, times(1)).delete(1L);
+ }
+
+ @Test
+ void delete_EpicNotFound_ReturnsNotFound() throws Exception {
+ doThrow(new NotFoundException("Epic not found")).when(epicService).delete(999L);
+
+ mockMvc.perform(delete("/epic/999"))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value("Epic not found"));
+
+ verify(epicService, times(1)).delete(999L);
+ }
+
+ @Test
+ void delete_InvalidId_ReturnsBadRequest() throws Exception {
+ mockMvc.perform(delete("/epic/-1"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.error").value("id must be positive"));
+
+ verify(epicService, never()).delete(anyLong());
+ }
+}
\ No newline at end of file
diff --git a/service/src/test/java/service/task/manager/controller/SubtaskControllerTest.java b/service/src/test/java/service/task/manager/controller/SubtaskControllerTest.java
new file mode 100644
index 0000000..40d03a4
--- /dev/null
+++ b/service/src/test/java/service/task/manager/controller/SubtaskControllerTest.java
@@ -0,0 +1,256 @@
+package service.task.manager.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import service.task.manager.dto.subtask.SubtaskRequestCreatedDto;
+import service.task.manager.dto.subtask.SubtaskRequestUpdatedDto;
+import service.task.manager.dto.subtask.SubtaskResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.ErrorHandler;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.model.enums.Status;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.service.SubtaskService;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@WebMvcTest(controllers = {SubtaskController.class, ErrorHandler.class})
+class SubtaskControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockBean
+ private SubtaskService subtaskService;
+
+ private final LocalDateTime testTime = LocalDateTime.of(2025, 4, 27, 10, 0);
+ private final Duration testDuration = Duration.ofHours(24);
+
+ // CREATE tests
+ @Test
+ void createSubtask_ValidRequest_ReturnsCreated() throws Exception {
+ SubtaskRequestCreatedDto request = new SubtaskRequestCreatedDto(
+ 1L,
+ "Valid Subtask",
+ "Valid Description",
+ testTime,
+ testDuration
+ );
+
+ mockMvc.perform(post("/subtask")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isCreated())
+ .andExpect(content().string(""));
+
+ verify(subtaskService).create(request);
+ }
+
+ @Test
+ void createSubtask_ConflictName_ReturnsConflict() throws Exception {
+ SubtaskRequestCreatedDto request = new SubtaskRequestCreatedDto(
+ 1L,
+ "Duplicate Subtask",
+ "Description",
+ testTime,
+ testDuration
+ );
+
+ doThrow(new ConflictException("Subtask with name Duplicate Subtask already exists"))
+ .when(subtaskService).create(request); // Используем конкретный request
+
+ mockMvc.perform(post("/subtask")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isConflict())
+ .andExpect(jsonPath("$.message").value("Subtask with name Duplicate Subtask already exists"));
+ }
+
+ // UPDATE tests
+ @Test
+ void updateSubtask_ValidRequest_ReturnsOk() throws Exception {
+ SubtaskRequestUpdatedDto request = new SubtaskRequestUpdatedDto(
+ 1L,
+ "Updated Subtask",
+ "Updated Description",
+ Status.IN_PROGRESS,
+ Duration.ofHours(48)
+ );
+
+ SubtaskResponseDto responseDto = new SubtaskResponseDto(
+ 1L,
+ 1L,
+ "Updated Subtask",
+ "Updated Description",
+ Status.IN_PROGRESS,
+ testTime,
+ testTime.plus(Duration.ofHours(48)),
+ Duration.ofHours(48),
+ TaskType.SUBTASK
+ );
+
+ when(subtaskService.update(any())).thenReturn(responseDto);
+
+ mockMvc.perform(put("/subtask")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1L))
+ .andExpect(jsonPath("$.name").value("Updated Subtask"))
+ .andDo(result -> System.out.println("Response: " + result.getResponse().getContentAsString()));
+
+ verify(subtaskService).update(request); // Проверяем вызов сервиса
+ }
+
+ @Test
+ void updateSubtask_NotFound_ReturnsNotFound() throws Exception {
+ SubtaskRequestUpdatedDto request = new SubtaskRequestUpdatedDto(
+ 999L,
+ "Non-existent Subtask",
+ "Description",
+ Status.NEW,
+ testDuration
+ );
+
+ when(subtaskService.update(any()))
+ .thenThrow(new NotFoundException("Subtask with ID 999 not found"));
+
+ mockMvc.perform(put("/subtask")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value("Subtask with ID 999 not found"));
+ }
+
+ // GET tests
+ @Test
+ void getSubtask_ValidId_ReturnsOk() throws Exception {
+ SubtaskResponseDto responseDto = new SubtaskResponseDto(
+ 1L,
+ 1L,
+ "Test Subtask",
+ "Test Description",
+ Status.NEW,
+ testTime,
+ testTime.plus(testDuration),
+ testDuration,
+ TaskType.SUBTASK
+ );
+
+ when(subtaskService.findById(1L)).thenReturn(responseDto);
+
+ mockMvc.perform(get("/subtask/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1L))
+ .andExpect(jsonPath("$.epicId").value(1L))
+ .andExpect(jsonPath("$.name").value("Test Subtask"))
+ .andExpect(jsonPath("$.description").value("Test Description"))
+ .andExpect(jsonPath("$.status").value("NEW"))
+ .andExpect(jsonPath("$.startTime").value("2025-04-27T10:00:00"))
+ .andExpect(jsonPath("$.endTime").value("2025-04-28T10:00:00"))
+ .andExpect(jsonPath("$.duration").value("PT24H"))
+ .andExpect(jsonPath("$.type").value("SUBTASK"))
+ .andDo(result -> System.out.println("Response: " + result.getResponse().getContentAsString()));
+ }
+
+ @Test
+ void getSubtask_InvalidId_ReturnsBadRequest() throws Exception {
+ mockMvc.perform(get("/subtask/-1"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.error").value("id must be positive"))
+ .andDo(result -> System.out.println("Response: " + result.getResponse().getContentAsString()));
+ }
+
+ @Test
+ void getSubtask_NotFound_ReturnsNotFound() throws Exception {
+ when(subtaskService.findById(999L))
+ .thenThrow(new NotFoundException("Subtask with ID 999 not found"));
+
+ mockMvc.perform(get("/subtask/999"))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value("Subtask with ID 999 not found"));
+ }
+
+ // GET ALL tests
+ @Test
+ void getAllSubtasks_ReturnsOk() throws Exception {
+ List subtasks = Collections.singletonList(
+ new SubtaskResponseDto(
+ 1L,
+ 1L,
+ "Subtask",
+ "Description",
+ Status.NEW,
+ testTime,
+ testTime.plus(testDuration),
+ testDuration,
+ TaskType.SUBTASK
+ )
+ );
+
+ when(subtaskService.findAll()).thenReturn(subtasks);
+
+ mockMvc.perform(get("/subtask"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(1L))
+ .andDo(result -> System.out.println("Response: " + result.getResponse().getContentAsString()));
+ }
+
+ // DELETE tests
+ @Test
+ void deleteSubtask_ValidId_ReturnsNoContent() throws Exception {
+ mockMvc.perform(delete("/subtask/1"))
+ .andExpect(status().isNoContent())
+ .andExpect(content().string("")); // Проверяем, что тело ответа пустое
+
+ verify(subtaskService).delete(1L);
+ }
+
+ @Test
+ void deleteSubtask_NotFound_ReturnsNotFound() throws Exception {
+ doThrow(new NotFoundException("Subtask with ID 999 not found"))
+ .when(subtaskService).delete(999L);
+
+ mockMvc.perform(delete("/subtask/999"))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value("Subtask with ID 999 not found"));
+ }
+
+ // Validation tests
+ @Test
+ void createSubtask_InvalidRequest_ReturnsBadRequest() throws Exception {
+ SubtaskRequestCreatedDto invalidRequest = new SubtaskRequestCreatedDto(
+ null, // Missing epicId
+ "", // Blank name
+ "", // Blank description
+ null, // Missing startTime
+ null // Missing duration
+ );
+
+ mockMvc.perform(post("/subtask")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(invalidRequest)))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.epicId").value("null epic ID"))
+ .andExpect(jsonPath("$.name").value("blank name"))
+ .andExpect(jsonPath("$.description").value("blank description"))
+ .andExpect(jsonPath("$.startTime").value("null start time"))
+ .andExpect(jsonPath("$.duration").value("null duration"));
+ }
+}
\ No newline at end of file
diff --git a/service/src/test/java/service/task/manager/controller/TaskControllerTest.java b/service/src/test/java/service/task/manager/controller/TaskControllerTest.java
new file mode 100644
index 0000000..bfff349
--- /dev/null
+++ b/service/src/test/java/service/task/manager/controller/TaskControllerTest.java
@@ -0,0 +1,223 @@
+package service.task.manager.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import service.task.manager.dto.task.TaskRequestCreatedDto;
+import service.task.manager.dto.task.TaskRequestUpdatedDto;
+import service.task.manager.dto.task.TaskResponseDto;
+import service.task.manager.exception.ErrorHandler;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.model.enums.Status;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.service.TaskService;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@WebMvcTest(controllers = {TaskController.class, ErrorHandler.class})
+class TaskControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockBean
+ private TaskService taskService;
+
+ private final LocalDateTime testTime = LocalDateTime.of(2025, 4, 27, 10, 0);
+ private final Duration testDuration = Duration.ofHours(24);
+
+ // CREATE tests
+ @Test
+ void createTask_ValidRequest_ReturnsCreated() throws Exception {
+ TaskRequestCreatedDto request = new TaskRequestCreatedDto(
+ "Valid Task",
+ "Valid Description",
+ testTime,
+ testDuration
+ );
+
+ mockMvc.perform(post("/task")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isCreated());
+
+ verify(taskService).create(request);
+ }
+
+ @Test
+ void createTask_InvalidRequest_ReturnsBadRequest() throws Exception {
+ TaskRequestCreatedDto invalidRequest = new TaskRequestCreatedDto(
+ "", // Invalid blank name
+ "", // Invalid blank description
+ null, // Missing start time
+ null // Missing duration
+ );
+
+ mockMvc.perform(post("/task")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(invalidRequest)))
+ .andExpect(status().isBadRequest());
+ }
+ @Test
+ void updateTask_ValidRequest_ReturnsOk() throws Exception {
+ TaskRequestUpdatedDto request = new TaskRequestUpdatedDto(
+ 1L,
+ "Updated Task",
+ "Updated Description",
+ Status.IN_PROGRESS,
+ Duration.ofHours(48)
+ );
+
+ TaskResponseDto responseDto = new TaskResponseDto(
+ 1L,
+ "Updated Task",
+ "Updated Description",
+ Status.IN_PROGRESS,
+ testTime,
+ testTime.plus(Duration.ofHours(48)),
+ Duration.ofHours(48),
+ TaskType.TASK
+ );
+
+ when(taskService.update(any(TaskRequestUpdatedDto.class))).thenReturn(responseDto);
+
+ mockMvc.perform(put("/task")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1L))
+ .andExpect(jsonPath("$.name").value("Updated Task"));
+ }
+
+ @Test
+ void updateTask_InvalidId_ReturnsBadRequest() throws Exception {
+ TaskRequestUpdatedDto invalidRequest = new TaskRequestUpdatedDto(
+ -1L, // Invalid ID
+ "Task",
+ "Description",
+ Status.NEW,
+ testDuration
+ );
+
+ mockMvc.perform(put("/task")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(invalidRequest)))
+ .andExpect(status().isBadRequest());
+ }
+
+ // GET tests
+ @Test
+ void getTask_ValidId_ReturnsOk() throws Exception {
+ TaskResponseDto responseDto = new TaskResponseDto(
+ 1L,
+ "Test Task",
+ "Test Description",
+ Status.NEW,
+ testTime,
+ testTime.plus(testDuration),
+ testDuration,
+ TaskType.TASK
+ );
+
+ when(taskService.findById(1L)).thenReturn(responseDto);
+
+ mockMvc.perform(get("/task/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1L));
+ }
+
+ @Test
+ void getTask_InvalidId_ReturnsBadRequest() throws Exception {
+ mockMvc.perform(get("/task/-1"))
+ .andExpect(status().isBadRequest());
+ }
+
+ // GET ALL tests
+ @Test
+ void getAllTasks_ReturnsOk() throws Exception {
+ List tasks = Collections.singletonList(
+ new TaskResponseDto(
+ 1L,
+ "Task",
+ "Description",
+ Status.NEW,
+ testTime,
+ testTime.plus(testDuration),
+ testDuration,
+ TaskType.TASK
+ )
+ );
+
+ when(taskService.findAll()).thenReturn(tasks);
+
+ mockMvc.perform(get("/task"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(1L));
+ }
+
+ // DELETE tests
+ @Test
+ void deleteTask_ValidId_ReturnsNoContent() throws Exception {
+ mockMvc.perform(delete("/task/1"))
+ .andExpect(status().isNoContent());
+
+ verify(taskService).delete(1L);
+ }
+
+ @Test
+ void deleteTask_InvalidId_ReturnsBadRequest() throws Exception {
+ mockMvc.perform(delete("/task/-1"))
+ .andExpect(status().isBadRequest());
+ }
+
+ // Exception handling tests
+ @Test
+ void updateTask_NotFound_ReturnsNotFound() throws Exception {
+ // Arrange
+ Long nonExistentTaskId = 999L;
+ TaskRequestUpdatedDto request = new TaskRequestUpdatedDto(
+ nonExistentTaskId,
+ "Non-existent Task",
+ "Description",
+ Status.NEW,
+ testDuration
+ );
+
+ String expectedErrorMessage = "Task with ID " + nonExistentTaskId + " not found";
+
+ when(taskService.update(any(TaskRequestUpdatedDto.class)))
+ .thenThrow(new NotFoundException(expectedErrorMessage));
+ mockMvc.perform(put("/task")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isNotFound())
+ .andExpect(jsonPath("$.message").value(expectedErrorMessage));
+
+ verify(taskService).update(any(TaskRequestUpdatedDto.class));
+ }
+
+ @Test
+ void getTask_NotFound_ReturnsNotFound() throws Exception {
+ when(taskService.findById(999L)).thenThrow(new NotFoundException("Task not found"));
+
+ mockMvc.perform(get("/task/999"))
+ .andExpect(status().isNotFound());
+ }
+}
\ No newline at end of file
diff --git a/service/src/test/java/service/task/manager/service/EpicServiceImplTest.java b/service/src/test/java/service/task/manager/service/EpicServiceImplTest.java
new file mode 100644
index 0000000..4be2639
--- /dev/null
+++ b/service/src/test/java/service/task/manager/service/EpicServiceImplTest.java
@@ -0,0 +1,235 @@
+package service.task.manager.service;
+
+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 service.task.manager.dto.epic.EpicRequestCreatedDto;
+import service.task.manager.dto.epic.EpicRequestUpdatedDto;
+import service.task.manager.dto.epic.EpicResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.mapper.EpicMapper;
+import service.task.manager.model.Epic;
+import service.task.manager.model.enums.Status;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.repository.EpicRepository;
+import service.task.manager.service.impl.EpicServiceImpl;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class EpicServiceImplTest {
+
+ @Mock
+ private EpicRepository repository;
+
+ @Mock
+ private EpicMapper mapper;
+
+ @Mock
+ private HistoryService history;
+
+ @InjectMocks
+ private EpicServiceImpl service;
+
+ // Sample data for tests
+ private final LocalDateTime now = LocalDateTime.now();
+ private final Duration duration = Duration.ofHours(24);
+ private final LocalDateTime endTime = now.plus(duration);
+
+ // --- Tests for create method ---
+
+ @Test
+ void create_ShouldCreateEpic_WhenNameIsUnique() {
+ // Arrange
+ EpicRequestCreatedDto dto = new EpicRequestCreatedDto("New Epic", "Test epic created", now, duration);
+ Epic epic = new Epic();
+ epic.setName(dto.name());
+ epic.setDescription(dto.description());
+ epic.setStartTime(dto.startTime());
+ epic.setDuration(dto.duration());
+ epic.setStatus(Status.NEW);
+ epic.setSubtasks(new ArrayList<>());
+
+ when(repository.existsByName(dto.name())).thenReturn(false);
+ when(mapper.toEntity(dto)).thenReturn(epic);
+ when(repository.save(any(Epic.class))).thenReturn(epic);
+
+ // Act
+ try {
+ service.create(dto);
+ } catch (Exception e) {
+ System.out.println("Exception in create: " + e.getMessage());
+ e.printStackTrace();
+ throw e;
+ }
+
+ // Assert
+ verify(repository, times(1)).existsByName(dto.name());
+ verify(mapper, times(1)).toEntity(dto);
+ verify(repository, times(1)).save(epic);
+ assertEquals(Status.NEW, epic.getStatus());
+ assertEquals(endTime, epic.getEndTime());
+ assertEquals(TaskType.EPIC, epic.getType());
+ }
+
+ @Test
+ void create_ShouldThrowConflictException_WhenNameExists() {
+ // Arrange
+ EpicRequestCreatedDto dto = new EpicRequestCreatedDto("Existing Epic", "Description", now, duration);
+ when(repository.existsByName(dto.name())).thenReturn(true);
+
+ // Act & Assert
+ assertThrows(ConflictException.class, () -> service.create(dto));
+ verify(repository, times(1)).existsByName(dto.name());
+ verify(mapper, never()).toEntity((EpicRequestCreatedDto) any());
+ verify(repository, never()).save(any());
+ }
+
+ // --- Tests for update method ---
+
+ @Test
+ void update_ShouldUpdateEpic_WhenEpicExists() {
+ // Arrange
+ Long epicId = 1L;
+ EpicRequestUpdatedDto dto = new EpicRequestUpdatedDto(epicId, "Updated Epic", "Updated Description", Status.IN_PROGRESS, duration);
+ Epic existingEpic = new Epic();
+ existingEpic.setId(epicId);
+ existingEpic.setName("Old Epic");
+ existingEpic.setDescription("Old Description");
+ existingEpic.setStartTime(now.minusDays(1));
+ existingEpic.setDuration(Duration.ofHours(12));
+ existingEpic.setStatus(Status.NEW);
+ existingEpic.setSubtasks(new ArrayList<>());
+
+ EpicResponseDto responseDto = new EpicResponseDto(epicId, new ArrayList<>(), "Updated Epic", "Updated Description", Status.IN_PROGRESS, now.minusDays(1), duration, now.minusDays(1).plus(duration), TaskType.EPIC);
+
+ when(repository.findById(epicId)).thenReturn(Optional.of(existingEpic));
+ when(mapper.toResponseDto(existingEpic)).thenReturn(responseDto);
+ when(mapper.toEntity(responseDto)).thenReturn(existingEpic);
+ when(repository.save(existingEpic)).thenReturn(existingEpic);
+
+ // Act
+ EpicResponseDto result = service.update(dto);
+
+ // Assert
+ verify(repository, times(1)).findById(epicId);
+ verify(mapper, times(1)).updateTaskFromDto(dto, existingEpic);
+ verify(repository, times(1)).save(existingEpic);
+ assertEquals("Updated Epic", result.name());
+ assertEquals(now.minusDays(1).plus(duration), result.endTime());
+ assertEquals(TaskType.EPIC, result.type());
+ }
+
+ @Test
+ void update_ShouldThrowNotFoundException_WhenEpicDoesNotExist() {
+ // Arrange
+ Long epicId = 1L;
+ EpicRequestUpdatedDto dto = new EpicRequestUpdatedDto(epicId, "Updated Epic", "Updated Description", Status.IN_PROGRESS, duration);
+ when(repository.findById(epicId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> service.update(dto));
+ verify(repository, times(1)).findById(epicId);
+ verify(mapper, never()).updateTaskFromDto(any(), any());
+ verify(repository, never()).save(any());
+ }
+
+ // --- Tests for findById method ---
+
+ @Test
+ void findById_ShouldReturnEpic_WhenEpicExists() {
+ // Arrange
+ Long epicId = 1L;
+ Epic epic = new Epic();
+ epic.setId(epicId);
+ epic.setName("Epic");
+ epic.setDescription("Description");
+ epic.setStartTime(now);
+ epic.setDuration(duration);
+ epic.setEndTime(endTime);
+ epic.setStatus(Status.NEW);
+ epic.setSubtasks(new ArrayList<>());
+
+ EpicResponseDto dto = new EpicResponseDto(epicId, new ArrayList<>(), "Epic", "Description", Status.NEW, now, duration, endTime, TaskType.EPIC);
+ when(repository.findById(epicId)).thenReturn(Optional.of(epic));
+ when(mapper.toResponseDto(epic)).thenReturn(dto);
+
+ // Act
+ EpicResponseDto result = service.findById(epicId);
+
+ // Assert
+ verify(repository, times(1)).findById(epicId);
+ verify(mapper, times(1)).toResponseDto(epic);
+ verify(history, times(1)).addToHistory(TaskType.EPIC, epicId);
+ assertEquals(dto, result);
+ assertEquals(endTime, result.endTime());
+ assertEquals(TaskType.EPIC, result.type());
+ }
+
+ @Test
+ void findById_ShouldThrowNotFoundException_WhenEpicDoesNotExist() {
+ // Arrange
+ Long epicId = 1L;
+ when(repository.findById(epicId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> service.findById(epicId));
+ verify(repository, times(1)).findById(epicId);
+ verify(mapper, never()).toResponseDto(any());
+ verify(history, never()).addToHistory(any(), any());
+ }
+
+ // --- Tests for findAll method ---
+
+ @Test
+ void findAll_ShouldReturnAllEpics() {
+ // Arrange
+ Epic epic1 = new Epic();
+ epic1.setId(1L);
+ epic1.setName("Epic1");
+ epic1.setDescription("Desc1");
+ epic1.setStartTime(now);
+ epic1.setDuration(duration);
+ epic1.setEndTime(endTime);
+ epic1.setStatus(Status.NEW);
+ epic1.setSubtasks(new ArrayList<>());
+
+ Epic epic2 = new Epic();
+ epic2.setId(2L);
+ epic2.setName("Epic2");
+ epic2.setDescription("Desc2");
+ epic2.setStartTime(now);
+ epic2.setDuration(duration);
+ epic2.setEndTime(endTime);
+ epic2.setStatus(Status.NEW);
+ epic2.setSubtasks(new ArrayList<>());
+
+ List epics = List.of(epic1, epic2);
+ EpicResponseDto dto1 = new EpicResponseDto(1L, new ArrayList<>(), "Epic1", "Desc1", Status.NEW, now, duration, endTime, TaskType.EPIC);
+ EpicResponseDto dto2 = new EpicResponseDto(2L, new ArrayList<>(), "Epic2", "Desc2", Status.NEW, now, duration, endTime, TaskType.EPIC);
+ when(repository.findAll()).thenReturn(epics);
+ when(mapper.toResponseDto(epic1)).thenReturn(dto1);
+ when(mapper.toResponseDto(epic2)).thenReturn(dto2);
+
+ // Act
+ List result = service.findAll();
+
+ // Assert
+ verify(repository, times(1)).findAll();
+ verify(mapper, times(2)).toResponseDto(any(Epic.class));
+ assertEquals(2, result.size());
+ assertTrue(result.contains(dto1));
+ assertTrue(result.contains(dto2));
+ }
+}
\ No newline at end of file
diff --git a/service/src/test/java/service/task/manager/service/HistoryServiceImplTest.java b/service/src/test/java/service/task/manager/service/HistoryServiceImplTest.java
new file mode 100644
index 0000000..5c80b96
--- /dev/null
+++ b/service/src/test/java/service/task/manager/service/HistoryServiceImplTest.java
@@ -0,0 +1,140 @@
+package service.task.manager.service;
+
+import org.junit.jupiter.api.BeforeEach;
+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 org.springframework.data.redis.core.ListOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import service.task.manager.model.HistoryEntry;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.service.impl.HistoryServiceImpl;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class HistoryServiceImplTest {
+
+ private static final String HISTORY_KEY = "history";
+ private static final int HISTORY_SIZE = 10;
+
+ @Mock
+ private RedisTemplate redisTemplate;
+
+ @Mock
+ private ListOperations listOps;
+
+ @InjectMocks
+ private HistoryServiceImpl historyService;
+
+ @BeforeEach
+ void setUp() throws NoSuchFieldException, IllegalAccessException {
+ // Use reflection to inject listOps into historyService
+ Field listOpsField = HistoryServiceImpl.class.getDeclaredField("listOps");
+ listOpsField.setAccessible(true);
+ listOpsField.set(historyService, listOps);
+ }
+
+ @Test
+ void addToHistory_ShouldAddEntrySuccessfully() {
+ // Arrange
+ TaskType taskType = TaskType.TASK;
+ Long id = 1L;
+ when(listOps.rightPush(eq(HISTORY_KEY), any(HistoryEntry.class))).thenReturn(1L);
+ when(listOps.size(HISTORY_KEY)).thenReturn(1L);
+
+ // Act
+ historyService.addToHistory(taskType, id);
+
+ // Assert
+ verify(listOps, times(1)).rightPush(eq(HISTORY_KEY), any(HistoryEntry.class));
+ verify(listOps, never()).leftPop(HISTORY_KEY);
+ }
+
+ @Test
+ void addToHistory_ShouldRemoveOldestEntryWhenSizeExceedsLimit() {
+ // Arrange
+ TaskType taskType = TaskType.EPIC;
+ Long id = 2L;
+ when(listOps.rightPush(eq(HISTORY_KEY), any(HistoryEntry.class))).thenReturn(11L);
+ when(listOps.size(HISTORY_KEY)).thenReturn(11L);
+ when(listOps.leftPop(HISTORY_KEY)).thenReturn(new HistoryEntry(TaskType.TASK, 1L));
+
+ // Act
+ historyService.addToHistory(taskType, id);
+
+ // Assert
+ verify(listOps, times(1)).rightPush(eq(HISTORY_KEY), any(HistoryEntry.class));
+ verify(listOps, times(1)).leftPop(HISTORY_KEY);
+ }
+
+ @Test
+ void addToHistory_ShouldHandleExceptionGracefully() {
+ // Arrange
+ TaskType taskType = TaskType.SUBTASK;
+ Long id = 3L;
+ when(listOps.rightPush(eq(HISTORY_KEY), any(HistoryEntry.class)))
+ .thenThrow(new RuntimeException("Redis exception"));
+
+ // Act
+ historyService.addToHistory(taskType, id);
+
+ // Assert
+ verify(listOps, times(1)).rightPush(eq(HISTORY_KEY), any(HistoryEntry.class));
+ verify(listOps, never()).size(HISTORY_KEY);
+ verify(listOps, never()).leftPop(HISTORY_KEY);
+ }
+
+ @Test
+ void getHistory_ShouldReturnHistoryEntries() {
+ // Arrange
+ List expectedHistory = List.of(
+ new HistoryEntry(TaskType.TASK, 1L),
+ new HistoryEntry(TaskType.EPIC, 2L)
+ );
+ when(listOps.range(HISTORY_KEY, 0, -1)).thenReturn(expectedHistory);
+
+ // Act
+ List actualHistory = historyService.getHistory();
+
+ // Assert
+ assertEquals(expectedHistory, actualHistory);
+ verify(listOps, times(1)).range(HISTORY_KEY, 0, -1);
+ }
+
+ @Test
+ void getHistory_ShouldReturnEmptyListWhenHistoryIsNull() {
+ // Arrange
+ when(listOps.range(HISTORY_KEY, 0, -1)).thenReturn(null);
+
+ // Act
+ List actualHistory = historyService.getHistory();
+
+ // Assert
+ assertNotNull(actualHistory);
+ assertTrue(actualHistory.isEmpty());
+ verify(listOps, times(1)).range(HISTORY_KEY, 0, -1);
+ }
+
+ @Test
+ void getHistory_ShouldReturnEmptyListOnException() {
+ // Arrange
+ when(listOps.range(HISTORY_KEY, 0, -1)).thenThrow(new RuntimeException("Redis exception"));
+
+ // Act
+ List actualHistory = historyService.getHistory();
+
+ // Assert
+ assertNotNull(actualHistory);
+ assertTrue(actualHistory.isEmpty());
+ verify(listOps, times(1)).range(HISTORY_KEY, 0, -1);
+ }
+}
\ No newline at end of file
diff --git a/service/src/test/java/service/task/manager/service/SubtaskServiceImplTest.java b/service/src/test/java/service/task/manager/service/SubtaskServiceImplTest.java
new file mode 100644
index 0000000..2e05b80
--- /dev/null
+++ b/service/src/test/java/service/task/manager/service/SubtaskServiceImplTest.java
@@ -0,0 +1,311 @@
+package service.task.manager.service;
+
+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 service.task.manager.dto.epic.EpicResponseDto;
+import service.task.manager.dto.subtask.SubtaskRequestCreatedDto;
+import service.task.manager.dto.subtask.SubtaskRequestUpdatedDto;
+import service.task.manager.dto.subtask.SubtaskResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.mapper.EpicMapper;
+import service.task.manager.mapper.SubtaskMapper;
+import service.task.manager.model.Epic;
+import service.task.manager.model.Subtask;
+import service.task.manager.model.enums.Status;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.repository.SubtaskRepository;
+import service.task.manager.service.impl.SubtaskServiceImpl;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+/**
+ * Unit tests for SubtaskServiceImpl.
+ */
+@ExtendWith(MockitoExtension.class)
+class SubtaskServiceImplTest {
+
+ @Mock
+ private SubtaskRepository repository;
+
+ @Mock
+ private EpicService epicService;
+
+ @Mock
+ private SubtaskMapper mapper;
+
+ @Mock
+ private EpicMapper epicMapper;
+
+ @Mock
+ private HistoryService history;
+
+ @InjectMocks
+ private SubtaskServiceImpl service;
+
+ // Sample data for tests
+ private final LocalDateTime now = LocalDateTime.now();
+ private final Duration duration = Duration.ofHours(24);
+ private final LocalDateTime endTime = now.plus(duration);
+ private final Long epicId = 1L;
+ private final Long subtaskId = 1L;
+
+ // --- Tests for create method ---
+
+ @Test
+ void create_ShouldCreateSubtask_WhenNameIsUniqueAndEpicExists() {
+ // Arrange
+ SubtaskRequestCreatedDto dto = new SubtaskRequestCreatedDto(epicId, "New Subtask", "Description", now, duration);
+ EpicResponseDto epicDto = new EpicResponseDto(epicId, new ArrayList<>(), "Epic", "Epic Description", Status.NEW, now, duration, endTime, TaskType.EPIC);
+ Epic epic = new Epic();
+ epic.setId(epicId);
+ epic.setName("Epic");
+ epic.setDescription("Epic Description");
+ epic.setSubtasks(new ArrayList<>());
+
+ Subtask subtask = new Subtask();
+ subtask.setName(dto.name());
+ subtask.setDescription(dto.description());
+ subtask.setStartTime(dto.startTime());
+ subtask.setDuration(dto.duration());
+ subtask.setEpic(epic);
+
+ when(epicService.findById(epicId)).thenReturn(epicDto);
+ when(epicMapper.toEntity(epicDto)).thenReturn(epic);
+ when(repository.existsByName(dto.name())).thenReturn(false);
+ when(mapper.toEntity(dto)).thenReturn(subtask);
+ when(repository.save(any(Subtask.class))).thenReturn(subtask);
+
+ // Act
+ service.create(dto);
+
+ // Assert
+ verify(epicService, times(1)).findById(epicId);
+ verify(epicMapper, times(1)).toEntity(epicDto);
+ verify(repository, times(1)).existsByName(dto.name());
+ verify(mapper, times(1)).toEntity(dto);
+ verify(repository, times(1)).save(subtask);
+ assertEquals(Status.NEW, subtask.getStatus());
+ assertEquals(endTime, subtask.getEndTime());
+ assertEquals(TaskType.SUBTASK, subtask.getType());
+ assertEquals(epic, subtask.getEpic());
+ }
+
+ @Test
+ void create_ShouldThrowConflictException_WhenNameExists() {
+ // Arrange
+ SubtaskRequestCreatedDto dto = new SubtaskRequestCreatedDto(epicId, "Existing Subtask", "Description", now, duration);
+ EpicResponseDto epicDto = new EpicResponseDto(epicId, new ArrayList<>(), "Epic", "Epic Description", Status.NEW, now, duration, endTime, TaskType.EPIC);
+
+ when(epicService.findById(epicId)).thenReturn(epicDto);
+ when(repository.existsByName(dto.name())).thenReturn(true);
+
+ // Act & Assert
+ assertThrows(ConflictException.class, () -> service.create(dto));
+ verify(epicService, times(1)).findById(epicId);
+ verify(repository, times(1)).existsByName(dto.name());
+ verify(mapper, never()).toEntity((SubtaskRequestCreatedDto) any());
+ verify(repository, never()).save(any());
+ }
+
+ @Test
+ void create_ShouldThrowNotFoundException_WhenEpicDoesNotExist() {
+ // Arrange
+ SubtaskRequestCreatedDto dto = new SubtaskRequestCreatedDto(epicId, "New Subtask", "Description", now, duration);
+ when(epicService.findById(epicId)).thenThrow(new NotFoundException("Epic with ID " + epicId + " not found"));
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> service.create(dto));
+ verify(epicService, times(1)).findById(epicId);
+ verify(repository, never()).existsByName(any());
+ verify(mapper, never()).toEntity((SubtaskRequestCreatedDto) any());
+ verify(repository, never()).save(any());
+ }
+
+ // --- Tests for update method ---
+
+ @Test
+ void update_ShouldUpdateSubtask_WhenSubtaskExists() {
+ // Arrange
+ SubtaskRequestUpdatedDto dto = new SubtaskRequestUpdatedDto(subtaskId, "Updated Subtask", "Updated Description", Status.IN_PROGRESS, duration);
+ Subtask existingSubtask = new Subtask();
+ existingSubtask.setId(subtaskId);
+ existingSubtask.setName("Old Subtask");
+ existingSubtask.setDescription("Old Description");
+ existingSubtask.setStartTime(now.minusDays(1));
+ existingSubtask.setDuration(Duration.ofHours(12));
+ existingSubtask.setStatus(Status.NEW);
+
+ SubtaskResponseDto responseDto = new SubtaskResponseDto(subtaskId, epicId, "Updated Subtask", "Updated Description", Status.IN_PROGRESS, now.minusDays(1), now.minusDays(1).plus(duration), duration, TaskType.SUBTASK);
+
+ when(repository.findById(subtaskId)).thenReturn(Optional.of(existingSubtask));
+ when(mapper.toResponseDto(existingSubtask)).thenReturn(responseDto);
+ when(mapper.toEntity(responseDto)).thenReturn(existingSubtask);
+ when(repository.save(existingSubtask)).thenReturn(existingSubtask);
+
+ // Act
+ SubtaskResponseDto result = service.update(dto);
+
+ // Assert
+ verify(repository, times(1)).findById(subtaskId);
+ verify(mapper, times(1)).updateSubtaskFromDto(dto, existingSubtask);
+ verify(repository, times(1)).save(existingSubtask);
+ assertEquals("Updated Subtask", result.name());
+ assertEquals(now.minusDays(1).plus(duration), result.endTime());
+ assertEquals(TaskType.SUBTASK, result.type());
+ }
+
+ @Test
+ void update_ShouldThrowNotFoundException_WhenSubtaskDoesNotExist() {
+ // Arrange
+ SubtaskRequestUpdatedDto dto = new SubtaskRequestUpdatedDto(subtaskId, "Updated Subtask", "Updated Description", Status.IN_PROGRESS, duration);
+ when(repository.findById(subtaskId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> service.update(dto));
+ verify(repository, times(1)).findById(subtaskId);
+ verify(mapper, never()).updateSubtaskFromDto(any(), any());
+ verify(repository, never()).save(any());
+ }
+
+ // --- Tests for findById method ---
+
+ @Test
+ void findById_ShouldReturnSubtask_WhenSubtaskExists() {
+ // Arrange
+ Subtask subtask = new Subtask();
+ subtask.setId(subtaskId);
+ subtask.setName("Subtask");
+ subtask.setDescription("Description");
+ subtask.setStartTime(now);
+ subtask.setDuration(duration);
+ subtask.setEndTime(endTime);
+ subtask.setStatus(Status.NEW);
+ Epic epic = new Epic();
+ epic.setId(epicId);
+ subtask.setEpic(epic);
+
+ SubtaskResponseDto dto = new SubtaskResponseDto(subtaskId, epicId, "Subtask", "Description", Status.NEW, now, endTime, duration, TaskType.SUBTASK);
+ when(repository.findById(subtaskId)).thenReturn(Optional.of(subtask));
+ when(mapper.toResponseDto(subtask)).thenReturn(dto);
+
+ // Act
+ SubtaskResponseDto result = service.findById(subtaskId);
+
+ // Assert
+ verify(repository, times(1)).findById(subtaskId);
+ verify(mapper, times(1)).toResponseDto(subtask);
+ verify(history, times(1)).addToHistory(TaskType.SUBTASK, subtaskId);
+ assertEquals(dto, result);
+ assertEquals(endTime, result.endTime());
+ assertEquals(TaskType.SUBTASK, result.type());
+ }
+
+ @Test
+ void findById_ShouldThrowNotFoundException_WhenSubtaskDoesNotExist() {
+ // Arrange
+ when(repository.findById(subtaskId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> service.findById(subtaskId));
+ verify(repository, times(1)).findById(subtaskId);
+ verify(mapper, never()).toResponseDto(any());
+ verify(history, never()).addToHistory(any(), any());
+ }
+
+ // --- Tests for findAll method ---
+
+ @Test
+ void findAll_ShouldReturnAllSubtasks() {
+ // Arrange
+ Subtask subtask1 = new Subtask();
+ subtask1.setId(1L);
+ subtask1.setName("Subtask1");
+ subtask1.setDescription("Desc1");
+ subtask1.setStartTime(now);
+ subtask1.setDuration(duration);
+ subtask1.setEndTime(endTime);
+ subtask1.setStatus(Status.NEW);
+ Epic epic1 = new Epic();
+ epic1.setId(epicId);
+ subtask1.setEpic(epic1);
+
+ Subtask subtask2 = new Subtask();
+ subtask2.setId(2L);
+ subtask2.setName("Subtask2");
+ subtask2.setDescription("Desc2");
+ subtask2.setStartTime(now);
+ subtask2.setDuration(duration);
+ subtask2.setEndTime(endTime);
+ subtask2.setStatus(Status.NEW);
+ subtask2.setEpic(epic1);
+
+ List subtasks = List.of(subtask1, subtask2);
+ SubtaskResponseDto dto1 = new SubtaskResponseDto(1L, epicId, "Subtask1", "Desc1", Status.NEW, now, endTime, duration, TaskType.SUBTASK);
+ SubtaskResponseDto dto2 = new SubtaskResponseDto(2L, epicId, "Subtask2", "Desc2", Status.NEW, now, endTime, duration, TaskType.SUBTASK);
+ when(repository.findAll()).thenReturn(subtasks);
+ when(mapper.toResponseDto(subtask1)).thenReturn(dto1);
+ when(mapper.toResponseDto(subtask2)).thenReturn(dto2);
+
+ // Act
+ List result = service.findAll();
+
+ // Assert
+ verify(repository, times(1)).findAll();
+ verify(mapper, times(2)).toResponseDto(any(Subtask.class));
+ assertEquals(2, result.size());
+ assertTrue(result.contains(dto1));
+ assertTrue(result.contains(dto2));
+ }
+
+ // --- Tests for delete method ---
+
+ @Test
+ void delete_ShouldDeleteSubtask_WhenSubtaskExists() {
+ // Arrange
+ Subtask subtask = new Subtask();
+ subtask.setId(subtaskId);
+ subtask.setName("Subtask");
+ subtask.setDescription("Description");
+ subtask.setStartTime(now);
+ subtask.setDuration(duration);
+ subtask.setEndTime(endTime);
+ subtask.setStatus(Status.NEW);
+ Epic epic = new Epic();
+ epic.setId(epicId);
+ subtask.setEpic(epic);
+
+ SubtaskResponseDto dto = new SubtaskResponseDto(subtaskId, epicId, "Subtask", "Description", Status.NEW, now, endTime, duration, TaskType.SUBTASK);
+ when(repository.findById(subtaskId)).thenReturn(Optional.of(subtask));
+ when(mapper.toResponseDto(subtask)).thenReturn(dto);
+
+ // Act
+ service.delete(subtaskId);
+
+ // Assert
+ verify(repository, times(1)).findById(subtaskId);
+ verify(repository, times(1)).deleteById(subtaskId);
+ }
+
+ @Test
+ void delete_ShouldThrowNotFoundException_WhenSubtaskDoesNotExist() {
+ // Arrange
+ when(repository.findById(subtaskId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> service.delete(subtaskId));
+ verify(repository, times(1)).findById(subtaskId);
+ verify(repository, never()).deleteById(any());
+ }
+}
diff --git a/service/src/test/java/service/task/manager/service/TaskServiceImplTest.java b/service/src/test/java/service/task/manager/service/TaskServiceImplTest.java
new file mode 100644
index 0000000..ec998e1
--- /dev/null
+++ b/service/src/test/java/service/task/manager/service/TaskServiceImplTest.java
@@ -0,0 +1,265 @@
+package service.task.manager.service;
+
+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 service.task.manager.dto.task.TaskRequestCreatedDto;
+import service.task.manager.dto.task.TaskRequestUpdatedDto;
+import service.task.manager.dto.task.TaskResponseDto;
+import service.task.manager.exception.ConflictException;
+import service.task.manager.exception.NotFoundException;
+import service.task.manager.mapper.TaskMapper;
+import service.task.manager.model.Task;
+import service.task.manager.model.enums.Status;
+import service.task.manager.model.enums.TaskType;
+import service.task.manager.repository.TaskRepository;
+import service.task.manager.service.impl.TaskServiceImpl;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+/**
+ * Unit tests for TaskServiceImpl, aligned with updated DTOs and Task model.
+ */
+@ExtendWith(MockitoExtension.class)
+class TaskServiceImplTest {
+
+ @Mock
+ private TaskRepository repository;
+
+ @Mock
+ private TaskMapper mapper;
+
+ @Mock
+ private HistoryService history;
+
+ @InjectMocks
+ private TaskServiceImpl taskService;
+
+ // Sample data for tests
+ private final LocalDateTime now = LocalDateTime.now();
+ private final Duration duration = Duration.ofHours(24);
+ private final LocalDateTime endTime = now.plus(duration);
+
+ // --- Tests for create method ---
+
+ @Test
+ void create_ShouldCreateTask_WhenNameIsUnique() {
+ // Arrange
+ TaskRequestCreatedDto dto = new TaskRequestCreatedDto("New Task", "Description", now, duration);
+ Task task = new Task();
+ task.setName(dto.name());
+ task.setDescription(dto.description());
+ task.setStartTime(dto.startTime());
+ task.setDuration(dto.duration());
+
+ when(repository.existsByName(dto.name())).thenReturn(false);
+ when(mapper.toEntity(dto)).thenReturn(task);
+ when(repository.save(any(Task.class))).thenReturn(task);
+
+ // Act
+ taskService.create(dto);
+
+ // Assert
+ verify(repository, times(1)).existsByName(dto.name());
+ verify(mapper, times(1)).toEntity(dto);
+ verify(repository, times(1)).save(task);
+ assertEquals(Status.NEW, task.getStatus()); // Status set by addEndTimeTaskAndStatus
+ assertEquals(endTime, task.getEndTime()); // End time calculated
+ assertEquals(TaskType.TASK, task.getType()); // Default type
+ }
+
+ @Test
+ void create_ShouldThrowConflictException_WhenNameExists() {
+ // Arrange
+ TaskRequestCreatedDto dto = new TaskRequestCreatedDto("Existing Task", "Description", now, duration);
+ when(repository.existsByName(dto.name())).thenReturn(true);
+
+ // Act & Assert
+ assertThrows(ConflictException.class, () -> taskService.create(dto));
+ verify(repository, times(1)).existsByName(dto.name());
+ verify(mapper, never()).toEntity((TaskRequestCreatedDto) any());
+ verify(repository, never()).save(any());
+ }
+
+ // --- Tests for update method ---
+
+ @Test
+ void update_ShouldUpdateTask_WhenTaskExists() {
+ // Arrange
+ Long taskId = 1L;
+ TaskRequestUpdatedDto dto = new TaskRequestUpdatedDto(taskId, "Updated Task", "Updated Description", Status.IN_PROGRESS, duration);
+ Task existingTask = new Task();
+ existingTask.setId(taskId);
+ existingTask.setName("Old Task");
+ existingTask.setDescription("Old Description");
+ existingTask.setStartTime(now.minusDays(1));
+ existingTask.setDuration(Duration.ofHours(12));
+ existingTask.setStatus(Status.NEW);
+
+ TaskResponseDto responseDto = new TaskResponseDto(taskId, "Updated Task", "Updated Description", Status.IN_PROGRESS, now.minusDays(1), now.minusDays(1).plus(duration), duration, TaskType.TASK);
+
+ when(repository.findById(taskId)).thenReturn(Optional.of(existingTask));
+ when(mapper.toResponseDto(existingTask)).thenReturn(responseDto);
+ when(mapper.toEntity(responseDto)).thenReturn(existingTask);
+ when(repository.save(existingTask)).thenReturn(existingTask);
+
+ // Act
+ TaskResponseDto result = taskService.update(dto);
+
+ // Assert
+ verify(repository, times(1)).findById(taskId); // Called by findById internally
+ verify(mapper, times(1)).updateTaskFromDto(dto, existingTask);
+ verify(repository, times(1)).save(existingTask);
+ assertEquals("Updated Task", result.name());
+ assertEquals(now.minusDays(1).plus(duration), result.endTime()); // End time recalculated
+ assertEquals(TaskType.TASK, result.type());
+ }
+
+ @Test
+ void update_ShouldThrowNotFoundException_WhenTaskDoesNotExist() {
+ // Arrange
+ Long taskId = 1L;
+ TaskRequestUpdatedDto dto = new TaskRequestUpdatedDto(taskId, "Updated Task", "Updated Description", Status.IN_PROGRESS, duration);
+ when(repository.findById(taskId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> taskService.update(dto));
+ verify(repository, times(1)).findById(taskId);
+ verify(mapper, never()).updateTaskFromDto(any(), any());
+ verify(repository, never()).save(any());
+ }
+
+ // --- Tests for findById method ---
+
+ @Test
+ void findById_ShouldReturnTask_WhenTaskExists() {
+ // Arrange
+ Long taskId = 1L;
+ Task task = new Task();
+ task.setId(taskId);
+ task.setName("Task");
+ task.setDescription("Description");
+ task.setStartTime(now);
+ task.setDuration(duration);
+ task.setEndTime(endTime);
+ task.setStatus(Status.NEW);
+
+ TaskResponseDto dto = new TaskResponseDto(taskId, "Task", "Description", Status.NEW, now, endTime, duration, TaskType.TASK);
+ when(repository.findById(taskId)).thenReturn(Optional.of(task));
+ when(mapper.toResponseDto(task)).thenReturn(dto);
+
+ // Act
+ TaskResponseDto result = taskService.findById(taskId);
+
+ // Assert
+ verify(repository, times(1)).findById(taskId);
+ verify(mapper, times(1)).toResponseDto(task);
+ verify(history, times(1)).addToHistory(TaskType.TASK, taskId);
+ assertEquals(dto, result);
+ assertEquals(endTime, result.endTime());
+ assertEquals(TaskType.TASK, result.type());
+ }
+
+ @Test
+ void findById_ShouldThrowNotFoundException_WhenTaskDoesNotExist() {
+ // Arrange
+ Long taskId = 1L;
+ when(repository.findById(taskId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> taskService.findById(taskId));
+ verify(repository, times(1)).findById(taskId);
+ verify(mapper, never()).toResponseDto(any());
+ verify(history, never()).addToHistory(any(), any());
+ }
+
+ // --- Tests for findAll method ---
+
+ @Test
+ void findAll_ShouldReturnAllTasks() {
+ // Arrange
+ Task task1 = new Task();
+ task1.setId(1L);
+ task1.setName("Task1");
+ task1.setDescription("Desc1");
+ task1.setStartTime(now);
+ task1.setDuration(duration);
+ task1.setEndTime(endTime);
+ task1.setStatus(Status.NEW);
+
+ Task task2 = new Task();
+ task2.setId(2L);
+ task2.setName("Task2");
+ task2.setDescription("Desc2");
+ task2.setStartTime(now);
+ task2.setDuration(duration);
+ task2.setEndTime(endTime);
+ task2.setStatus(Status.NEW);
+
+ List tasks = List.of(task1, task2);
+ TaskResponseDto dto1 = new TaskResponseDto(1L, "Task1", "Desc1", Status.NEW, now, endTime, duration, TaskType.TASK);
+ TaskResponseDto dto2 = new TaskResponseDto(2L, "Task2", "Desc2", Status.NEW, now, endTime, duration, TaskType.TASK);
+ when(repository.findAll()).thenReturn(tasks);
+ when(mapper.toResponseDto(task1)).thenReturn(dto1);
+ when(mapper.toResponseDto(task2)).thenReturn(dto2);
+
+ // Act
+ List result = taskService.findAll();
+
+ // Assert
+ verify(repository, times(1)).findAll();
+ verify(mapper, times(2)).toResponseDto(any(Task.class));
+ assertEquals(2, result.size());
+ assertTrue(result.contains(dto1));
+ assertTrue(result.contains(dto2));
+ }
+
+ // --- Tests for delete method ---
+
+ @Test
+ void delete_ShouldDeleteTask_WhenTaskExists() {
+ // Arrange
+ Long taskId = 1L;
+ Task task = new Task();
+ task.setId(taskId);
+ task.setName("Task");
+ task.setDescription("Description");
+ task.setStartTime(now);
+ task.setDuration(duration);
+ task.setEndTime(endTime);
+ task.setStatus(Status.NEW);
+
+ TaskResponseDto dto = new TaskResponseDto(taskId, "Task", "Description",
+ Status.NEW, now, endTime, duration, TaskType.TASK);
+ when(repository.findById(taskId)).thenReturn(Optional.of(task));
+ when(mapper.toResponseDto(task)).thenReturn(dto);
+
+ // Act
+ taskService.delete(taskId);
+
+ // Assert
+ verify(repository, times(1)).findById(taskId); // Called by findById internally
+ verify(repository, times(1)).deleteById(taskId);
+ }
+
+ @Test
+ void delete_ShouldThrowNotFoundException_WhenTaskDoesNotExist() {
+ // Arrange
+ Long taskId = 1L;
+ when(repository.findById(taskId)).thenReturn(Optional.empty());
+
+ // Act & Assert
+ assertThrows(NotFoundException.class, () -> taskService.delete(taskId));
+ verify(repository, times(1)).findById(taskId);
+ verify(repository, never()).deleteById(any());
+ }
+}
\ No newline at end of file
diff --git a/src/task/manager/schedule/Main.java b/src/task/manager/schedule/Main.java
deleted file mode 100644
index 68e87c4..0000000
--- a/src/task/manager/schedule/Main.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package task.manager.schedule;
-
-public class Main {
- public static void main(String[] args) {
- }
-}
\ No newline at end of file
diff --git a/src/task/manager/schedule/exception/ManagerSaveException.java b/src/task/manager/schedule/exception/ManagerSaveException.java
deleted file mode 100644
index 338f552..0000000
--- a/src/task/manager/schedule/exception/ManagerSaveException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package task.manager.schedule.exception;
-
-public class ManagerSaveException extends RuntimeException {
- public ManagerSaveException() {
- }
-
- public ManagerSaveException(String message) {
- super(message);
- }
-
- public ManagerSaveException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public ManagerSaveException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/src/task/manager/schedule/exception/NotFoundException.java b/src/task/manager/schedule/exception/NotFoundException.java
deleted file mode 100644
index ff8d235..0000000
--- a/src/task/manager/schedule/exception/NotFoundException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package task.manager.schedule.exception;
-
-public class NotFoundException extends RuntimeException {
- public NotFoundException() {
- }
-
- public NotFoundException(String message) {
- super(message);
- }
-
- public NotFoundException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public NotFoundException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/src/task/manager/schedule/model/Epic.java b/src/task/manager/schedule/model/Epic.java
deleted file mode 100644
index 12233b1..0000000
--- a/src/task/manager/schedule/model/Epic.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package task.manager.schedule.model;
-
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-
-public class Epic extends Task {
-
- private final List subtaskIds = new ArrayList<>();
- private LocalDateTime endTime;
-
- public Epic(String name, Status status, String description, LocalDateTime startTime, long duration) {
- super(name, status, description, startTime, duration);
- }
-
- public Epic(int id, String name, String description, Status status, LocalDateTime startTime, long duration) {
- super(name, status, description, startTime, duration);
- setId(id);
- }
-
- public void addSubtaskId(int id) {
- subtaskIds.add(id);
- }
-
- public void cleanSubtask() {
- subtaskIds.clear();
- }
-
- public void removeSubtask(int id) {
- subtaskIds.remove(id);
- }
-
- public List getSubtaskIds() {
- return subtaskIds;
- }
-
- public TaskType getType() {
- return TaskType.EPIC;
- }
-
- public void setEndTime(LocalDateTime endTime) {
- this.endTime = endTime;
- }
-
- @Override
- public String toString() {
- return "Epic{" +
- "subtaskIds=" + subtaskIds +
- ", endTime=" + endTime +
- '}';
- }
-}
diff --git a/src/task/manager/schedule/model/Subtask.java b/src/task/manager/schedule/model/Subtask.java
deleted file mode 100644
index 0ff7bf7..0000000
--- a/src/task/manager/schedule/model/Subtask.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package task.manager.schedule.model;
-
-import java.time.LocalDateTime;
-
-public class Subtask extends Task {
-
- private Integer epicId;
-
- public Subtask(String name, Status status, String description, LocalDateTime startTime, long duration, int epicId) {
-
- super(name, status, description, startTime, duration);
- setEpicId(epicId);
- }
-
- public Subtask(int id, String name, String description, Status status, LocalDateTime startTime, long duration, Integer epicId) {
- super(name, status, description, startTime, duration);
- setId(id);
- setEpicId(epicId);
- }
-
- @Override
- public Integer getEpicId() {
- return epicId;
- }
-
- @Override
- public TaskType getType() {
- return TaskType.SUBTASK;
- }
-
- public void setEpicId(Integer epicId) {
- this.epicId = epicId;
- }
-
- @Override
- public String toString() {
- return "Subtask{" +
- "epicId=" + epicId +
- '}';
- }
-}
diff --git a/src/task/manager/schedule/model/Task.java b/src/task/manager/schedule/model/Task.java
deleted file mode 100644
index bcc4e8c..0000000
--- a/src/task/manager/schedule/model/Task.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package task.manager.schedule.model;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
-import java.util.Objects;
-
-public class Task {
- private int id;
- private String name;
- private String description;
- private Status status;
-
- private LocalDateTime startTime; // LocalDateTime
- private Duration duration; // минуты или Duration
-
- public Task(String name, Status status, String description, LocalDateTime startTime, long duration) {
- this.name = name;
- this.status = status;
- this.description = description;
- this.startTime = LocalDateTime.from(startTime);
- this.duration = Duration.ofMinutes(duration);
- }
-
- public Task(int id, String name, String description, Status status, LocalDateTime startTime, long duration) {
- setId(id);
- this.name = name;
- this.status = status;
- this.description = description;
- this.startTime = startTime;
- this.duration = Duration.ofMinutes(duration);
- }
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public Integer getEpicId() {
- return null;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDescription() {
- return description;
- }
-
- public TaskType getType() {
- return TaskType.TASK;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public Status getStatus() {
- return status;
- }
-
- public void setStatus(Status status) {
- this.status = status;
- }
-
- public LocalDateTime getStartTime() {
- return startTime;
- }
-
- public void setStartTime(LocalDateTime startTime) {
- this.startTime = startTime;
- }
-
- public long getDuration() {
- return duration.toMinutesPart();
- }
-
- public void setDuration(long duration) {
- this.duration = Duration.ofMinutes(duration);
- }
-
- public LocalDateTime getEndTime() {
- return startTime.plus(duration);
- }
-
-
- @Override
- public String toString() {
- return "Task{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", description='" + description + '\'' +
- ", status=" + status +
- ", startTime=" + startTime +
- ", endTime=" + getEndTime() +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Task task = (Task) o;
- return id == task.id && Objects.equals(name, task.name) && Objects.equals(description, task.description) && status == task.status && Objects.equals(startTime, task.startTime) && Objects.equals(duration, task.duration);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, name, description, status, startTime, duration);
- }
-}
diff --git a/src/task/manager/schedule/server/Endpoint.java b/src/task/manager/schedule/server/Endpoint.java
deleted file mode 100644
index dfce98e..0000000
--- a/src/task/manager/schedule/server/Endpoint.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package task.manager.schedule.server;
-
-public enum Endpoint {
- GET_TASKS,
- GET_BY_ID,
- GET_EPICS_ID_SUBTASKS,
- GET_SUBTASKS,
- GET_HISTORY,
- GET_PRIORITIZED,
-
-
- POST,
-
-
- DELETE_BY_ID,
-
-
- UNKNOWN
-}
diff --git a/src/task/manager/schedule/server/EndpointHandler.java b/src/task/manager/schedule/server/EndpointHandler.java
deleted file mode 100644
index fc6ce12..0000000
--- a/src/task/manager/schedule/server/EndpointHandler.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package task.manager.schedule.server;
-
-public class EndpointHandler {
- public Endpoint getEndpoint(String requestPath, String requestMethod) {
- if (requestPath.equals("/tasks") && requestMethod.equals("GET")) {
- return Endpoint.GET_TASKS;
- } else if (requestPath.equals("/tasks") && requestMethod.equals("POST")) {
- return Endpoint.POST;
- } else if (requestPath.equals("/tasks/" + requestPath.split("/")[2]) && requestMethod.equals("GET")) {
- return Endpoint.GET_BY_ID;
- } else if (requestPath.equals("/tasks/" + requestPath.split("/")[2]) && requestMethod.equals("DELETE")) {
- return Endpoint.DELETE_BY_ID;
- } else if (requestPath.equals("/epics") && requestMethod.equals("GET")) {
- return Endpoint.GET_TASKS;
- } else if (requestPath.equals("/epics") && requestMethod.equals("POST")) {
- return Endpoint.POST;
- } else if (requestPath.equals("epics/" + requestPath.split("/")[2]) && requestMethod.equals("GET")) {
- return Endpoint.GET_BY_ID;
- } else if (requestPath.equals("/epics/" + requestPath.split("/")[2]) && requestMethod.equals("DELETE")) {
- return Endpoint.DELETE_BY_ID;
- } else if (requestPath.equals("/epics/" + requestPath.split("/")[2] + "/subtasks") && requestMethod.equals("GET")) {
- return Endpoint.GET_EPICS_ID_SUBTASKS;
- } else if (requestPath.equals("/subtasks") && requestMethod.equals("GET")) {
- return Endpoint.GET_SUBTASKS;
- } else if (requestPath.equals("/subtasks") && requestMethod.equals("POST")) {
- return Endpoint.POST;
- } else if (requestPath.equals("/subtasks/" + requestPath.split("/")[2]) && requestMethod.equals("GET")) {
- return Endpoint.GET_BY_ID;
- } else if (requestPath.equals("subtasks/" + requestPath.split("/")[2]) && requestMethod.equals("DELETE")) {
- return Endpoint.DELETE_BY_ID;
- } else if (requestPath.endsWith("history") && requestMethod.equals("GET")) {
- return Endpoint.GET_HISTORY;
- } else if (requestPath.endsWith("prioritized") && requestMethod.equals("GET")) {
- return Endpoint.GET_PRIORITIZED;
- } else {
- return Endpoint.UNKNOWN;
- }
- }
-}
diff --git a/src/task/manager/schedule/server/HttpHandler.java b/src/task/manager/schedule/server/HttpHandler.java
deleted file mode 100644
index b46831d..0000000
--- a/src/task/manager/schedule/server/HttpHandler.java
+++ /dev/null
@@ -1,203 +0,0 @@
-package task.manager.schedule.server;
-
-import com.google.gson.*;
-import com.sun.net.httpserver.HttpExchange;
-import task.manager.schedule.exception.ManagerSaveException;
-import task.manager.schedule.exception.NotFoundException;
-import task.manager.schedule.model.Epic;
-import task.manager.schedule.model.Subtask;
-import task.manager.schedule.model.Task;
-import task.manager.schedule.service.TaskManager;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-public class HttpHandler implements com.sun.net.httpserver.HttpHandler {
- TaskManager manager;
- TaskHandler taskHandler;
- EndpointHandler endpointHandler;
- public HttpHandler(TaskManager manager) {
- this.manager = manager;
- this.taskHandler = new TaskHandler();
- this.endpointHandler = new EndpointHandler();
- }
-
- @Override
- public void handle(HttpExchange h) throws IOException {
- GsonBuilder gsonBuilder = new GsonBuilder();
- gsonBuilder.setPrettyPrinting();
- Gson gson = gsonBuilder.create();
- Endpoint endpoint = endpointHandler.getEndpoint(h.getRequestURI().getPath(), h.getRequestMethod());
- final String path = h.getRequestURI().getPath();
- switch (endpoint) {
- case GET_TASKS:
- if (path.equals("/tasks")) {
- sendText(h, gson.toJson(manager.getTasks().toString()));
- break;
- }else if(path.equals("/epics")) {
- sendText(h, gson.toJson(manager.getEpics().toString()));
- break;
- }
- case GET_BY_ID:
- try {
- int id = Integer.parseInt(h.getRequestURI().getPath().split("/")[2]);
- if(path.equals("/tasks/"+ id)){
- sendText(h, gson.toJson(manager.getTask(id).toString()));
- break;
- }else if(path.equals("/epics/"+ id)){
- sendText(h, gson.toJson(manager.getEpic(id).toString()));
- break;
- }else if(path.equals("/subtasks/"+ id)){
- sendText(h, gson.toJson(manager.getSubtask(id).toString()));
- break;
- }
- } catch (NotFoundException e) {
- sendNotFound(h, "Данной задачи не существует по id: " + h.getRequestURI().getPath().split("/")[2]);
- break;
- }
- case GET_EPICS_ID_SUBTASKS:
- try {
- sendText(h, gson.toJson(manager.getEpicSubtasks(Integer.parseInt(h.getRequestURI().getPath().split("/")[2])).toString()));
- break;
- } catch (NotFoundException e) {
- sendNotFound(h, "Данного эпика не существует по id: " + e.getMessage());
- break;
- }
- case GET_HISTORY:
- sendText(h, gson.toJson(manager.getHistory().toString()));
- break;
- case GET_PRIORITIZED:
- sendText(h, gson.toJson(manager.getPrioritizedTasks().toString()));
- break;
- case POST:
- convertTask(h);
- break;
- case DELETE_BY_ID:
- try {
- int id = Integer.parseInt(h.getRequestURI().getPath().split("/")[2]);
- if(path.equals("/tasks/"+id)) {
- manager.deleteTask(id);
- sendText(h,"");
- break;
- } else if(path.equals("/epics/"+id)) {
- manager.deleteEpic(id);
- sendText(h,"");
- break;
- }else if(path.equals("/subtasks/"+id)) {
- manager.deleteSubtask(id);
- sendText(h,"");
- break;
- }
- } catch (NotFoundException e) {
- sendNotFound(h, "Данной задачи не существует по id: " + e.getMessage());
- break;
- }
- default:
- errorServer(h);
- break;
- }
- }
-
- protected void sendText(HttpExchange h, String text) throws IOException {
- try (h) {
- byte[] resp = text.getBytes(StandardCharsets.UTF_8);
- h.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8");
- h.sendResponseHeaders(200, resp.length);
- h.getResponseBody().write(resp);
- }
- }
-
- protected void sendNotFound(HttpExchange h, String text) throws IOException {
- try (h) {
- byte[] resp = text.getBytes(StandardCharsets.UTF_8);
- h.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8");
- h.sendResponseHeaders(404, resp.length);
- h.getResponseBody().write(resp);
- }
- }
-
- protected void updateText(HttpExchange h) throws IOException {
- try (h) {
- h.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8");
- h.sendResponseHeaders(201, 0);
- }
- }
- protected void errorServer(HttpExchange h) throws IOException {
- try (h) {
- h.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8");
- h.sendResponseHeaders(500, 0);
- }
- }
-
- protected void sendHasInteractions(HttpExchange h, String text) throws IOException {
- try (h) {
- byte[] resp = text.getBytes(StandardCharsets.UTF_8);
- h.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8");
- h.sendResponseHeaders(406, resp.length);
- h.getResponseBody().write(resp);
- }
- }
-
-
- private void convertTask(HttpExchange h) throws IOException {
- InputStream inputStream = h.getRequestBody();
- final String path = h.getRequestURI().getPath();
- final String[] body = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8).split(",");
- Task task = taskHandler.taskFromString(body);
- if (path.equals("/tasks")) {
- if (body[0].split("=")[0].equals("id")) {
- try {
- manager.updateTask(task);
- updateText(h);
- } catch (ManagerSaveException e) {
- sendHasInteractions(h, e.getMessage());
- }
- } else {
- try {
- manager.createTask(task);
- updateText(h);
- } catch (ManagerSaveException e) {
- sendHasInteractions(h, e.getMessage());
- }
- }
- }
- if (path.equals("/epics")) {
- if (body[0].split("=")[0].equals("id")) {
- int taskId = Integer.parseInt(body[0].split("=")[1]);
- try {
- manager.updateEpic(new Epic(task.getId(), task.getName(), task.getDescription(), task.getStatus(), task.getStartTime(), task.getDuration()));
- updateText(h);
- } catch (ManagerSaveException e) {
- sendHasInteractions(h, e.getMessage());
- }
- } else {
- try {
- manager.createEpic(new Epic(task.getName(), task.getStatus(), task.getDescription(), task.getStartTime(), task.getDuration()));
- updateText(h);
- } catch (ManagerSaveException e) {
- sendHasInteractions(h, e.getMessage());
- }
- }
- }
- if (path.equals("/subtasks")) {
- final int epicId = Integer.parseInt(body[6].split("=")[1]);
- if (body[0].split("=")[0].equals("id")) {
- int taskId = Integer.parseInt(body[0].split("=")[1]);
- try {
- manager.updateSubtask(new Subtask(task.getId(), task.getName(), task.getDescription(), task.getStatus(), task.getStartTime(), task.getDuration(), epicId));
- updateText(h);
- } catch (ManagerSaveException e) {
- sendHasInteractions(h, e.getMessage());
- }
- } else {
- try {
- manager.createSubtask(new Subtask(task.getName(), task.getStatus(), task.getDescription(), task.getStartTime(), task.getDuration(), epicId));
- updateText(h);
- } catch (ManagerSaveException e) {
- sendHasInteractions(h, e.getMessage());
- }
- }
- }
- }
-}
diff --git a/src/task/manager/schedule/server/HttpTaskServer.java b/src/task/manager/schedule/server/HttpTaskServer.java
deleted file mode 100644
index 3dd0383..0000000
--- a/src/task/manager/schedule/server/HttpTaskServer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package task.manager.schedule.server;
-import com.sun.net.httpserver.HttpServer;
-import task.manager.schedule.model.Status;
-import task.manager.schedule.model.Task;
-import task.manager.schedule.service.Manager;
-import task.manager.schedule.service.TaskManager;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.time.LocalDateTime;
-
-public class HttpTaskServer {
- private static final int PORT = 8080;
- public static void main(String[] args) throws IOException {
- TaskManager taskManager = Manager.getDefault();
-
- taskManager.createTask(new Task("Дом", Status.NEW, "Убраться в кухни и ванной", LocalDateTime.now(),40));
- taskManager.createTask(new Task("Работа", Status.IN_PROGRESS, "Сделать куча рутины и пойти домой:)",LocalDateTime.now().plusDays(1),50));
-
-
-
- HttpHandler httpHandler = new HttpHandler(taskManager);
-
- HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
- server.createContext("/tasks",httpHandler);
- server.createContext("/epics",httpHandler);
- server.createContext("/subtasks", httpHandler);
- server.createContext("/history", httpHandler);
- server.createContext("/prioritized", httpHandler);
- server.start();
- System.out.println("HTTP-сервер запущен на " + PORT + " порту!");
- }
-}
diff --git a/src/task/manager/schedule/server/TaskHandler.java b/src/task/manager/schedule/server/TaskHandler.java
deleted file mode 100644
index afc2f6d..0000000
--- a/src/task/manager/schedule/server/TaskHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package task.manager.schedule.server;
-
-import task.manager.schedule.model.Status;
-import task.manager.schedule.model.Task;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
-
-public class TaskHandler {
- public Task taskFromString(String[] body){
- if (body[0].split("=")[0].equals("id")) {
- int taskId = Integer.parseInt(body[0].split("=")[1]);
- final String name = body[1].split("=")[1];
- final String description = body[2].split("=")[1];
- final Status status = Status.valueOf(body[3].split("=")[1]);
- final LocalDateTime startTime = LocalDateTime.parse(body[4].split("=")[1]);
- final LocalDateTime endTime = LocalDateTime.parse(body[5].split("=")[1].replaceAll("}\"", "").trim());
- final Duration duration = Duration.between(startTime, endTime);
- return new Task(taskId, name, description, status, startTime, duration.toMinutesPart());
- } else {
- final String name = body[0].split("=")[1];
- final String description = body[1].split("=")[1];
- final Status status = Status.valueOf(body[2].split("=")[1]);
- final LocalDateTime startTime = LocalDateTime.parse(body[3].split("=")[1]);
- final LocalDateTime endTime = LocalDateTime.parse(body[4].split("=")[1].replaceAll("}\"", "").trim());
- final Duration duration = Duration.between(startTime, endTime);
- return new Task(name, status, description, startTime, duration.toMinutesPart());
- }
- }
-
-}
diff --git a/src/task/manager/schedule/service/FileBackedTaskManager.java b/src/task/manager/schedule/service/FileBackedTaskManager.java
deleted file mode 100644
index 861022e..0000000
--- a/src/task/manager/schedule/service/FileBackedTaskManager.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package task.manager.schedule.service;
-
-import java.io.*;
-
-import java.nio.file.Files;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
-import java.util.Map;
-
-import task.manager.schedule.exception.ManagerSaveException;
-import ru.yandex.javacource.golotin.schedule.model.*;
-import task.manager.schedule.service.inMemory.InMemoryTaskManager;
-import task.manager.schedule.model.*;
-
-public class FileBackedTaskManager extends InMemoryTaskManager {
-
- private static final String HEADER = "id,type,name,status,description,epic";
- private final File file;
-
- public FileBackedTaskManager(File file) {
- super(Manager.getDefaultHistory());
- this.file = file;
- }
-
- public static FileBackedTaskManager loadFromFile(File file) {
- final FileBackedTaskManager taskManager = new FileBackedTaskManager(file);
- try {
- final String csv = Files.readString(file.toPath());
- final String[] lines = csv.split(System.lineSeparator());
- int generatorId = 0;
- for (int i = 1; i < lines.length; i++) {
- String line = lines[i];
- if (line.isEmpty()) {
- break;
- }
- final Task task = taskFromString(line);
- final int id = task.getId();
- if (id > generatorId) {
- generatorId = id;
- }
- if (task.getType() == TaskType.TASK) {
- taskManager.createTask(task);
- } else if (task.getType() == TaskType.SUBTASK) {
- taskManager.createSubtask(new Subtask(task.getId(), task.getName(), task.getDescription(),
- task.getStatus(), task.getStartTime(), task.getDuration(), task.getEpicId()));
- } else if (task.getType() == TaskType.EPIC) {
- taskManager.createEpic(new Epic(task.getId(), task.getName(), task.getDescription(),
- task.getStatus(), task.getStartTime(), task.getDuration()));
- for (Subtask subtask : taskManager.subtasks.values()) {// Поиск подзадач эпика
- if (subtask.getEpicId() == task.getId()) {
- Epic epic = taskManager.epics.get(task.getId());
- epic.addSubtaskId(subtask.getId());
- }
- }
- }
- }
- for (Map.Entry e : taskManager.subtasks.entrySet()) {
- final Subtask subtask = e.getValue();
- final Epic epic = taskManager.epics.get(subtask.getEpicId());
- epic.addSubtaskId(subtask.getId());
- }
- taskManager.counterId = generatorId;
- } catch (IOException e) {
- throw new ManagerSaveException("Невозможно прочитать файл: " + file.getName(), e);
- }
- return taskManager;
- }
-
- @Override
- public Task createTask(Task task) {
- Task newTask = super.createTask(task);
- saveToFile();
- return newTask;
- }
-
- @Override
- public Epic createEpic(Epic epic) {
- Epic newEpic = super.createEpic(epic);
- saveToFile();
- return newEpic;
- }
-
- @Override
- public Subtask createSubtask(Subtask subtask) {
- Subtask newSubtask = super.createSubtask(subtask);
- saveToFile();
- return newSubtask;
- }
-
- @Override
- public void updateTask(Task task) {
- super.updateTask(task);
- saveToFile();
- }
-
- @Override
- public void updateEpic(Epic epic) {
- super.updateEpic(epic);
- saveToFile();
- }
-
- @Override
- public void updateSubtask(Subtask subTask) {
- super.updateSubtask(subTask);
- saveToFile();
- }
-
- @Override
- public void deleteTask(int id) {
- super.deleteTask(id);
- saveToFile();
- }
-
- @Override
- public void deleteEpic(int id) {
- super.deleteEpic(id);
- saveToFile();
- }
-
- @Override
- public void deleteSubtask(int id) {
- super.deleteSubtask(id);
- saveToFile();
- }
-
- public static String toString(Task task) {
- return task.getId() + "," + task.getType() + "," + task.getName() + "," + task.getStatus() + "," +
- task.getDescription() + "," + (task.getType().equals(TaskType.SUBTASK) ? task.getEpicId() : ""+task.getStartTime()+","+task.getEndTime());
- }
-
-
- public static Task taskFromString(String value) {
- final String[] values = value.split(",");
- final int id = Integer.parseInt(values[0]);
- final TaskType type = TaskType.valueOf(values[1]);
- final String name = values[2];
- final Status status = Status.valueOf(values[3]);
- final String description = values[4];
- final LocalDateTime startTime = LocalDateTime.parse(values[5]);
- final Duration duration = Duration.between(LocalDateTime.parse(values[5]), LocalDateTime.parse(values[6]));
- if (type == TaskType.TASK) {
- return new Task(id, name, description, status, startTime, duration.toMinutesPart());
- }
- if (type == TaskType.SUBTASK) {
- final int epicId = Integer.parseInt(values[7]);
- return new Subtask(id, name, description, status, startTime, duration.toMinutesPart(), epicId);
- }
-
- return new Epic(id, name, description, status, startTime, duration.toMinutesPart());
- }
-
- protected void saveToFile() {
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
- writer.write(HEADER);
- writer.newLine();
-
- for (Map.Entry entry : tasks.entrySet()) {
- final Task task = entry.getValue();
- writer.write(toString(task));
- writer.newLine();
- }
-
- for (Map.Entry entry : subtasks.entrySet()) {
- final Task task = entry.getValue();
- writer.write(toString(task));
- writer.newLine();
- }
-
- for (Map.Entry entry : epics.entrySet()) {
- final Task task = entry.getValue();
- writer.write(toString(task));
- writer.newLine();
- }
-
- writer.newLine();
- } catch (IOException e) {
- throw new ManagerSaveException("Ошибка сохранения файла: " + file.getName(), e);
- }
- }
-}
diff --git a/src/task/manager/schedule/service/HistoryManager.java b/src/task/manager/schedule/service/HistoryManager.java
deleted file mode 100644
index 9b81442..0000000
--- a/src/task/manager/schedule/service/HistoryManager.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package task.manager.schedule.service;
-
-import java.util.List;
-
-import task.manager.schedule.model.Task;
-
-public interface HistoryManager {
- void add(Task task);
-
- List getAll();
-
- void remove(int id);
-
-
-}
\ No newline at end of file
diff --git a/src/task/manager/schedule/service/Manager.java b/src/task/manager/schedule/service/Manager.java
deleted file mode 100644
index a14dbc8..0000000
--- a/src/task/manager/schedule/service/Manager.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package task.manager.schedule.service;
-
-import task.manager.schedule.service.inMemory.InMemoryHistoryManager;
-
-import java.io.File;
-
-public class Manager {
- public static TaskManager getDefault() {
- return new FileBackedTaskManager(new File("resources/task.csv"));
- }
-
- public static InMemoryHistoryManager getDefaultHistory() {
- return new InMemoryHistoryManager();
- }
-}
diff --git a/src/task/manager/schedule/service/TaskManager.java b/src/task/manager/schedule/service/TaskManager.java
deleted file mode 100644
index 2b6fc36..0000000
--- a/src/task/manager/schedule/service/TaskManager.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package task.manager.schedule.service;
-
-import task.manager.schedule.model.Epic;
-import task.manager.schedule.model.Subtask;
-import task.manager.schedule.model.Task;
-
-import java.util.List;
-
-public interface TaskManager {
- Task createTask(Task task);
-
- Task createEpic(Epic epic);
-
- Subtask createSubtask(Subtask subtask);
-
- void updateTask(Task task);
-
- void updateEpic(Epic epic);
-
- void updateSubtask(Subtask subtask);
-
- void cleanTasks();
-
- void cleanSubtasks();
-
- void cleanEpics();
-
- void deleteTask(int id);
-
- void deleteSubtask(int id);
-
- void deleteEpic(int id);
-
- List getTasks();
-
- List getEpics();
-
- List getEpicSubtasks(int epicId);
-
- Task getTask(int id);
-
- Epic getEpic(int id);
-
- Subtask getSubtask(int id);
-
- List getHistory();
-
- List getPrioritizedTasks();
-}
diff --git a/src/task/manager/schedule/service/inMemory/InMemoryHistoryManager.java b/src/task/manager/schedule/service/inMemory/InMemoryHistoryManager.java
deleted file mode 100644
index 6db73c0..0000000
--- a/src/task/manager/schedule/service/inMemory/InMemoryHistoryManager.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package task.manager.schedule.service.inMemory;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import task.manager.schedule.model.Task;
-import task.manager.schedule.service.HistoryManager;
-
-public class InMemoryHistoryManager implements HistoryManager {
-
- private static class Node {
- Task item;
- Node next;
- Node prev;
-
- Node(Node prev, Task element, Node next) {// присвоение prev, element, next
- this.item = element;
- this.next = next;
- this.prev = prev;
- }
- }
-
- private static final Map history = new HashMap<>();
- private Node first;
- private Node last;
-
- @Override
- public void add(Task task) {// добавление Task
-
- if (task == null) {
- return;
- }else {
- remove(task.getId());
- linkLast(task);
- history.put(task.getId(), last);
-
- }
- }
-
- @Override
- public List getAll() {// вывод списка истории
- List list = new ArrayList<>();
- Node current = first;
- while (current != null) {
-
- current = current.next;
- }
- return list;
- }
-
-
- @Override
- public void remove(int id) {// удаление по id
- final Node node = history.remove(id);
- if (node == null) {
- return;
- }
- removeNode(node);
- }
-
- private void removeNode(Node node) {
-
- if (node.prev != null) {
- node.prev.next = node.next;
- if (node.next == null) {
- last = node.prev;
- } else {
- node.next.prev = node.prev;
- }
- } else {
- first = node.next;
- if (first == null) {
- last = null;
- } else {
- first.prev = null;
- }
- }
- }
-
- private void linkLast(Task task) {// двигаем историю
- final Node lastLink = last;
- final Node newNode = new Node(lastLink, task, null);
- last = newNode;
- if (lastLink == null) {
- first = newNode;
- } else {
- lastLink.next = newNode;
- }
- }
-}
\ No newline at end of file
diff --git a/src/task/manager/schedule/service/inMemory/InMemoryTaskManager.java b/src/task/manager/schedule/service/inMemory/InMemoryTaskManager.java
deleted file mode 100644
index 4b6b71b..0000000
--- a/src/task/manager/schedule/service/inMemory/InMemoryTaskManager.java
+++ /dev/null
@@ -1,295 +0,0 @@
-package task.manager.schedule.service.inMemory;
-
-import task.manager.schedule.exception.ManagerSaveException;
-import task.manager.schedule.exception.NotFoundException;
-import task.manager.schedule.model.Epic;
-import task.manager.schedule.model.Status;
-import task.manager.schedule.model.Subtask;
-import task.manager.schedule.model.Task;
-import task.manager.schedule.service.HistoryManager;
-import task.manager.schedule.service.TaskManager;
-
-import java.time.LocalDateTime;
-import java.util.*;
-import java.util.stream.Collectors;
-
-
-public class InMemoryTaskManager implements TaskManager {
- protected int counterId = 0;
- protected final Map tasks;
- protected final Map epics;
- protected final Map subtasks;
- protected final HistoryManager historyManager;
- protected final Set prioritizedTasks;
-
- public InMemoryTaskManager(HistoryManager historyManager) {
- this.historyManager = historyManager; // 3
- this.tasks = new HashMap<>();
- this.epics = new HashMap<>();
- this.subtasks = new HashMap<>();
- this.prioritizedTasks = new TreeSet<>(Comparator.comparing(Task::getStartTime));
- }
-
- @Override
- public Task createTask(Task task) {// создание Task
- final int id = ++counterId;
- task.setId(id);
- addPriorityTask(task);
- tasks.put(id, task);
- return task;
- }
-
- @Override
- public Epic createEpic(Epic epic) {// создание Epic
- final int id = ++counterId;
- epic.setId(id);
- addPriorityTask(epic);
- epics.put(id, epic);
- return epic;
- }
-
-
- @Override
- public Subtask createSubtask(Subtask subtask) {// создание Subtask
- final int epicId = subtask.getEpicId();
- Epic epic = epics.get(epicId);
- if (epic == null) {
- return null;
- }
- updateEpicDuration(epic);
- final int id = ++counterId;
- subtask.setId(id);
- addPriorityTask(subtask);
- subtasks.put(id, subtask);
- epic.addSubtaskId(subtask.getId());
- updateEpicStatus(epicId);
- return subtask;
- }
-
- @Override
- public void updateTask(Task task) {// обновление Task
- final Task savedTask = tasks.get(task.getId());
- if (savedTask == null) {
- return;
- }
- addPriorityTask(task);
- tasks.put(task.getId(), task);
- }
-
- @Override
- public void updateEpic(Epic epic) {// обновление Epic
- final Epic savedEpic = epics.get(epic.getId());
- if (savedEpic == null) {
- return;
- }
- savedEpic.setName(epic.getName());
- savedEpic.setDescription(epic.getDescription());
- addPriorityTask(savedEpic);
- epics.put(epic.getId(), epic);
- }
-
- @Override
- public void updateSubtask(Subtask subtask) {// обновление Subtask
- final int epicId = subtask.getEpicId();
- final Subtask savedSubtask = subtasks.get(subtask.getId());
- if (savedSubtask == null) {
- return;
- }
- final Epic epic = epics.get(epicId);
- if (epic == null) {
- return;
- }
- addPriorityTask(savedSubtask);
- subtasks.put(subtask.getId(), subtask);
- updateEpic(epicId);// обновление статуса у Epic
- }
-
- @Override
- public void cleanTasks() {
- tasks.clear();
- }// очистка списка Tasks
-
- public void cleanSubtasks() {// очистка списка Subtasks
- for (Epic epic : epics.values()) {
- epic.cleanSubtask();
- updateEpicStatus(epic.getId());
- }
- subtasks.clear();
- }
-
- @Override
- public void cleanEpics() {// очистка списка Epics и Subtasks
- epics.clear();
- subtasks.clear();
-
- }
-
- @Override
- public void deleteTask(int id) {
- tasks.remove(id);
- }// удаление по id Task
-
- @Override
- public void deleteSubtask(int id) {// удаление по id Subtask
- Subtask subtask = subtasks.remove(id);
- if (subtask == null) {
- return;
- }
- Epic epic = epics.get(subtask.getEpicId());
- epic.removeSubtask(id);
- updateEpicStatus(epic.getId());
- }
-
- @Override
- public void deleteEpic(int id) {// удаление по id Epic
- Epic epic = epics.remove(id);
- if (epic == null) {
- return;
- }
- for (Integer subtaskId : epic.getSubtaskIds()) {
- subtasks.remove(subtaskId);
- }
- }
-
- @Override
- public List getTasks() {
- return new ArrayList<>(tasks.values());
- }// получаем список Tasks
-
- @Override
- public List getEpics() {
- return new ArrayList<>(epics.values());
- }// получаем список Epics
-
- @Override
- public List getEpicSubtasks(int epicId) {// получаем список Epic с Subtasks
- final Epic epic = epics.get(epicId);
- if (epic == null) {
- throw new NotFoundException("Задача с ид=" + epicId);
- }
- updateEpic(epicId);
- return epic.getSubtaskIds().stream().map(subtasks::get).collect(Collectors.toList());
- }
-
- @Override
- public Task getTask(int id) {// получаем Task по id
- final Task task = tasks.get(id);
- if (task == null) {
- throw new NotFoundException("Задача с ид=" + id);
- }
- historyManager.add(task);
- return task;
-
- }
-
- @Override
- public Epic getEpic(int id) {// получаем Epic по id
- final Epic epic = epics.get(id);
- if (epic == null) {
- throw new NotFoundException("Эпик с ид=" + id);
- }
- historyManager.add(epic);
- return epic;
- }
-
- @Override
- public Subtask getSubtask(int id) {// получаем Subtask по id
- final Subtask subtask = subtasks.get(id);
- if (subtask == null) {
- throw new NotFoundException("Подзадача с ид=" + id);
- }
- historyManager.add(subtask);
- return subtask;
- }
-
- @Override
- public List getHistory() {// получаем список истории
- return historyManager.getAll();
- }
-
- private void updateEpicStatus(int epicId) {// обновление статуса Epic
- Epic epic = epics.get(epicId);
- List subtasks = epic.getSubtaskIds().stream()
- .filter(this.subtasks::containsKey)
- .map(this.subtasks::get)
- .toList();
- for (Subtask statusSubtask : subtasks) {
- short subtaskNew = 0;
- short subtaskDone = 0;
- if (statusSubtask.getStatus() == Status.IN_PROGRESS) {
- epic.setStatus(Status.IN_PROGRESS);
- break;
- } else if (statusSubtask.getStatus() == Status.NEW) {
- subtaskNew++;
- } else if (statusSubtask.getStatus() == Status.DONE) {
- subtaskDone++;
- }
- if (subtaskDone == subtasks.size()) {
- epic.setStatus(Status.DONE);
- break;
- }
- if (subtaskNew == subtasks.size()) {
- epic.setStatus(Status.NEW);
- } else {
- epic.setStatus(Status.IN_PROGRESS);
- }
- break;
- }
- }
-
- private void updateEpicDuration(Epic epic) {
- List subs = epic.getSubtaskIds();
- if (subs.isEmpty()) {
- epic.setDuration(0L);
- return;
- }
- LocalDateTime start = LocalDateTime.MAX;
- LocalDateTime end = LocalDateTime.MIN;
- long duration = 0L;
- for (int id : subs) {
- final Subtask subtask = subtasks.get(id);
- final LocalDateTime startTime = subtask.getStartTime();
- final LocalDateTime endTime = subtask.getEndTime();
- if (startTime.isBefore(start)) {
- start = startTime;
- }
- if (endTime.isAfter(end)) {
- end = endTime;
- }
- duration += subtask.getDuration();
- }
- epic.setDuration(duration);
- epic.setStartTime(start);
- epic.setEndTime(end);
- }
-
- protected void updateEpic(int epicId) {
- Epic epic = epics.get(epicId);
- updateEpicStatus(epicId);
- updateEpicDuration(epic);
- }
-
- private void addPriorityTask(Task task) {
- final LocalDateTime startTime = task.getStartTime();
- final LocalDateTime endTime = task.getEndTime();
- for (Task t : prioritizedTasks) {
- final LocalDateTime existStart = t.getStartTime();
- final LocalDateTime existEnd = t.getEndTime();
- if (!endTime.isAfter(existStart)) {
- continue;
- }
- if (!existEnd.isAfter(startTime)) {
- continue;
- }
-
- throw new ManagerSaveException("Задача пересекаются с id=" + t.getId() + " c " + existStart + " по " + existEnd);
- }
-
- prioritizedTasks.add(task);
- }
-
- @Override
- public List getPrioritizedTasks() {
- return new ArrayList<>(prioritizedTasks);
- }
-}
diff --git a/test/task/manager/schedule/module/EpicTest.java b/test/task/manager/schedule/module/EpicTest.java
deleted file mode 100644
index 4a159da..0000000
--- a/test/task/manager/schedule/module/EpicTest.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package ru.yandex.javacource.golotin.schedule.test.model;
-
-import org.junit.jupiter.api.Test;
-import ru.yandex.javacource.golotin.schedule.model.Epic;
-import ru.yandex.javacource.golotin.schedule.model.Status;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class EpicTest {
-
- @Test
- void shouldEqualsWithCopy() {
- Epic epic = new Epic("name", Status.NEW, "desc");
- Epic epicExpected = new Epic("name1", Status.NEW, "desc");
- assertEquals(epicExpected, epic);
-
- }
-
-}
\ No newline at end of file
diff --git a/test/task/manager/schedule/service/FileBackedTaskManagerTest.java b/test/task/manager/schedule/service/FileBackedTaskManagerTest.java
deleted file mode 100644
index b691320..0000000
--- a/test/task/manager/schedule/service/FileBackedTaskManagerTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package ru.yandex.javacource.golotin.schedule.service;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import ru.yandex.javacource.golotin.schedule.model.Status;
-import ru.yandex.javacource.golotin.schedule.model.Task;
-
-import java.io.File;
-import java.time.LocalDateTime;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class FileBackedTaskManagerTest {
- TaskManager taskManager;
- @BeforeEach
- void beforeEach() {
- taskManager = Manager.getDefault();
-
- }
-
- @Test
- void createTask() {
- LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
- Task task = new Task("test", Status.NEW, "testing", localDateTime,30);
- Task task2 = new Task("test2", Status.NEW, "testing2",LocalDateTime.now(),45);
- taskManager.createTask(task);
- taskManager.createTask(task2);
- assertEquals(taskManager.getTasks(),FileBackedTaskManager.loadFromFile(new File("resources/task.csv")).getTasks());
- }
-
- @Test
- void updateTask() {
- Task task = new Task("test", Status.NEW, "testing", LocalDateTime.now(),30);
- taskManager.createTask(task);
- Task task2 = new Task(0,"test2","testing2", Status.NEW, LocalDateTime.now(),45);
- taskManager.updateTask(task2);
- assertEquals(task, FileBackedTaskManager.loadFromFile(new File("resources/task.csv")).getTask(1));
- }
-
- @Test
- void deleteTask() {
- Task task = new Task("test", Status.NEW, "testing", LocalDateTime.now(),30);
- taskManager.createTask(task);
- TaskManager taskManager1 = taskManager;
- taskManager.deleteTask(task.getId());
- assertNotEquals(taskManager, taskManager1);
- }
-
-}
\ No newline at end of file
diff --git a/test/task/manager/schedule/service/InMemoryTaskManagerTest.java b/test/task/manager/schedule/service/InMemoryTaskManagerTest.java
deleted file mode 100644
index 245f262..0000000
--- a/test/task/manager/schedule/service/InMemoryTaskManagerTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package ru.yandex.javacource.golotin.schedule.test.service;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import ru.yandex.javacource.golotin.schedule.exception.ManagerSaveException;
-import ru.yandex.javacource.golotin.schedule.model.Status;
-import ru.yandex.javacource.golotin.schedule.model.Task;
-import ru.yandex.javacource.golotin.schedule.service.InMemoryTaskManager;
-import ru.yandex.javacource.golotin.schedule.service.Manager;
-import ru.yandex.javacource.golotin.schedule.service.TaskManager;
-
-
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class InMemoryTaskManagerTest {
- TaskManager taskManager;
-
- @BeforeEach
- void beforeEach() {
- taskManager = Manager.getDefault();
- }
-
-
- @Test
- void shouldCreateTask() {
- LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
- Task task = new Task("test", Status.NEW, "testing", localDateTime,30);
- Task task2 = new Task("test2", Status.NEW, "testing2",LocalDateTime.now(),45);
- Task task3 = task;
- assertEquals(task, task2);
- assertSame(task, task3);
-
- Task result = taskManager.createTask(task);
-
- assertNotNull(result);
- Task clone = taskManager.getTask(result.getId());
- assertEquals(clone.getId(), result.getId());
- assertEquals(clone.getName(), result.getName());
- assertTrue(taskManager.getTasks().contains(task));
-
- }
-
- @Test
- void shouldUpdateTask() {
- LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
- Task task = new Task("test", Status.NEW, "testing", localDateTime,30);
- taskManager.createTask(task);
- Task task2 = new Task(0,"test2", "testing2", Status.NEW, LocalDateTime.now(),45);
- taskManager.updateTask(task2);
- assertEquals(task, taskManager.getTask(1));
- }
-
- @Test
- void shouldDeleteTask() {
- LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
- Task task = new Task("test", Status.NEW, "testing", localDateTime,30);
- Task task2 = new Task("test2", Status.NEW, "testing2", LocalDateTime.now(),45);
- taskManager.createTask(task);
- taskManager.createTask(task2);
- taskManager.deleteTask(1);
- Exception exception = assertThrows(ManagerSaveException.class, ()->taskManager.getTask(1));
- String expectedMessage = "Задача с ид=" + 1;
- String actualMessage = exception.getMessage();
- assertTrue(actualMessage.contains(expectedMessage));
-
- }
-
- @Test
- void shouldCleanTask() {
- LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
- Task task = new Task("test", Status.NEW, "testing", LocalDateTime.now(),30);
- Task task2 = new Task("test2", Status.NEW, "testing2", localDateTime,45);
- taskManager.createTask(task);
- taskManager.createTask(task2);
- taskManager.cleanTasks();
- assertEquals(taskManager.getTasks(), taskManager.getEpics());
- }
-
- @Test
- void shouldGetTasks() {
- LocalDateTime localDateTime = LocalDateTime.now().plusDays(1);
- Task task = new Task("test", Status.NEW, "testing", localDateTime,30);
- Task task2 = new Task("test2", Status.NEW, "testing2", LocalDateTime.now(),45);
- taskManager.createTask(task);
- taskManager.createTask(task2);
-
- List tasks = new ArrayList<>();
- tasks.add(task);
- tasks.add(task2);
- assertEquals(tasks, taskManager.getTasks());
- }
-
- @Test
- void shouldaddPriorityTask(){
- LocalDateTime localDateTime = LocalDateTime.now();
- Task task = new Task("test", Status.NEW, "testing", localDateTime,30);
- Task task2 = new Task("test2", Status.NEW, "testing2", localDateTime,45);
- taskManager.createTask(task);
- Exception exception = assertThrows(ManagerSaveException.class, () -> taskManager.createTask(task2));
-
- String expectedMessage = "Задача пересекаются с id=" + task.getId() + " c " + task.getStartTime() + " по " + task.getEndTime();
- String actualMessage = exception.getMessage();
- assertTrue(actualMessage.contains(expectedMessage));
-
- }
-}
\ No newline at end of file