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..0a2d47e
--- /dev/null
+++ b/service/src/main/java/service/task/manager/service/impl/SubtaskServiceImpl.java
@@ -0,0 +1,154 @@
+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.error.ConflictException;
+import service.task.manager.error.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.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);
+ }
+
+ /**
+ * 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..1ba09da
--- /dev/null
+++ b/service/src/main/java/service/task/manager/service/impl/TaskServiceImpl.java
@@ -0,0 +1,132 @@
+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.error.ConflictException;
+import service.task.manager.error.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.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);
+ }
+
+ /**
+ * 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..8530f08
--- /dev/null
+++ b/service/src/main/resources/application.yml
@@ -0,0 +1,30 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ 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..582d02c
--- /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.error.ConflictException;
+import service.task.manager.error.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..3f6a0a3
--- /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.error.ConflictException;
+import service.task.manager.error.ErrorHandler;
+import service.task.manager.error.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..efefd7f
--- /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.error.ErrorHandler;
+import service.task.manager.error.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/HistoryServiceImplTest.java b/service/src/test/java/service/task/manager/service/HistoryServiceImplTest.java
new file mode 100644
index 0000000..ad3471b
--- /dev/null
+++ b/service/src/test/java/service/task/manager/service/HistoryServiceImplTest.java
@@ -0,0 +1,91 @@
+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.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class HistoryServiceImplTest {
+
+ @Mock
+ private RedisTemplate redisTemplate;
+
+ @Mock
+ private ListOperations listOps;
+
+ @InjectMocks
+ private HistoryServiceImpl historyService;
+
+ @BeforeEach
+ void setUp() {
+ // Ensure redisTemplate.opsForList() returns the mocked listOps
+ when(redisTemplate.opsForList()).thenReturn(listOps);
+ }
+
+ @Test
+ void addToHistory_WithinLimit_AddsEntry() {
+ // Mock the size of the list and the range result
+ when(listOps.size("history")).thenReturn(1L);
+ when(listOps.range("history", 0, -1)).thenReturn(List.of(new HistoryEntry(TaskType.EPIC, 1L)));
+
+ // Call the method under test
+ historyService.addToHistory(TaskType.EPIC, 1L);
+ List history = historyService.getHistory();
+
+ // Verify the result
+ assertEquals(1, history.size());
+ assertEquals(TaskType.EPIC, history.get(0).getType());
+ assertEquals(1L, history.get(0).getId());
+
+ // Verify interactions with listOps
+ verify(listOps, times(1)).rightPush("history", new HistoryEntry(TaskType.EPIC, 1L));
+ }
+
+ @Test
+ void addToHistory_ExceedsLimit_RemovesOldest() {
+ // Create a list to simulate the history entries
+ List historyEntries = new ArrayList<>();
+
+ // Simulate adding 11 entries
+ for (long i = 1; i <= 11; i++) {
+ // Mock the size of the list to increase with each addition
+ when(listOps.size("history")).thenReturn(i);
+ historyService.addToHistory(TaskType.TASK, i);
+ historyEntries.add(new HistoryEntry(TaskType.TASK, i));
+
+ // If size exceeds MAX_HISTORY_SIZE (10), simulate leftPop
+ if (i > 10) {
+ historyEntries.remove(0); // Remove the oldest entry
+ }
+ }
+
+ // Mock the final state of the history after trimming
+ when(listOps.range("history", 0, -1)).thenReturn(historyEntries);
+
+ // Call getHistory to retrieve the result
+ List history = historyService.getHistory();
+
+ // Verify the result
+ assertEquals(10, history.size());
+ assertEquals(2L, history.get(0).getId()); // First entry (id=1) should be removed
+ assertEquals(11L, history.get(9).getId());
+
+ // Verify interactions with listOps
+ verify(listOps, times(11)).rightPush(eq("history"), any(HistoryEntry.class));
+ verify(listOps, times(1)).leftPop("history"); // Should trim once after exceeding limit
+ }
+}
\ No newline at end of file
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/service/FileBackedTaskManager.java b/src/task/manager/schedule/service/FileBackedTaskManager.java
index 861022e..eb0a0ad 100644
--- a/src/task/manager/schedule/service/FileBackedTaskManager.java
+++ b/src/task/manager/schedule/service/FileBackedTaskManager.java
@@ -11,7 +11,6 @@
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 {