diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d3352..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..870f31b
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+java-kanban.iml
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 47478b9..040df01 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
deleted file mode 100644
index 2b63946..0000000
--- a/.idea/uiDesigner.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 35eb1dd..94a25f7 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..b9f066a
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,601 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "lastFilter": {
+ "state": "OPEN",
+ "assignee": "Max-Browckin"
+ }
+}
+ {
+ "selectedUrlAndAccountId": {
+ "url": "https://github.com/Max-Browckin/java-kanban.git",
+ "accountId": "f757eb4c-7ff7-45a1-94e2-0d42525264b2"
+ }
+}
+
+
+
+
+
+
+ {
+ "associatedIndex": 3
+}
+
+
+
+
+
+
+ {
+ "keyToString": {
+ "Application.HttpTaskServer.executor": "Run",
+ "Application.Main.executor": "Run",
+ "Downloaded.Files.Path.Enabled": "true",
+ "JUnit.EpicTest.executor": "Run",
+ "JUnit.EpicTest.testAddEpicAsSubtask.executor": "Debug",
+ "JUnit.FileBackedTaskManagerTest.executor": "Run",
+ "JUnit.FileBackedTaskManagerTest.saveAndLoadMultipleTasks.executor": "Run",
+ "JUnit.FileBackedTaskManagerTest.testAddTask.executor": "Run",
+ "JUnit.FileBackedTaskManagerTest.testEpicStatusCalculation.executor": "Run",
+ "JUnit.FileBackedTaskManagerTest.testGetPrioritizedTasks.executor": "Run",
+ "JUnit.FileBackedTaskManagerTest.testLoadFromFileWithEmptyFile.executor": "Run",
+ "JUnit.FileBackedTaskManagerTest.testSaveAndLoadFromFile.executor": "Run",
+ "JUnit.FileBackedTasksManagerTest.deleteTaskShouldRemoveTaskFromFile.executor": "Run",
+ "JUnit.FileBackedTasksManagerTest.executor": "Run",
+ "JUnit.FileBackedTasksManagerTest.saveAndLoadMultipleTasks.executor": "Run",
+ "JUnit.HttpTaskServerTest.executor": "Run",
+ "JUnit.HttpTaskServerTest.testAddSubtask.executor": "Run",
+ "JUnit.HttpTaskServerTest.testAddTask.executor": "Run",
+ "JUnit.HttpTaskServerTest.testUpdateSubtask.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.getHistoryShouldReturnListOf10Tasks.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.getHistoryShouldReturnOldEpicAfterUpdate.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.getHistoryShouldReturnOldSubtaskAfterUpdate.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.getHistoryShouldReturnOldTaskAfterUpdate.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.shouldMaintainDataIntegrityAfterTaskFieldUpdate.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.shouldNotHaveInvalidSubtaskIdsInEpic.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.shouldNotRetainOldIdInDeletedSubtask.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.testGetTaskByIDAddsToHistory.executor": "Run",
+ "JUnit.InMemoryHistoryManagerTest.testUpdateTaskUpdatesTaskAndAddsOldVersionToHistory.executor": "Run",
+ "JUnit.InMemoryTaskManagerTest.executor": "Run",
+ "JUnit.ManagersTest.executor": "Run",
+ "JUnit.SubtaskTest.SubtasksWithEqualEpicIDShouldBeEqual.executor": "Run",
+ "JUnit.SubtaskTest.constructorWithAllParamsShouldSetAllFields.executor": "Run",
+ "JUnit.SubtaskTest.constructorWithEpicIDShouldSetEpicID.executor": "Run",
+ "JUnit.SubtaskTest.executor": "Run",
+ "JUnit.SubtaskTest.toStringShouldReturnCorrectString.executor": "Run",
+ "JUnit.TaskManagerTest.executor": "Run",
+ "JUnit.TaskManagerTest.testAddTask.executor": "Run",
+ "JUnit.TaskTest.executor": "Run",
+ "Maven. [clean,test].executor": "Run",
+ "Repository.Attach.Annotations": "false",
+ "Repository.Attach.JavaDocs": "false",
+ "Repository.Attach.Sources": "false",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrary": "JUnit5",
+ "com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrarySuperClass.JUnit5": "",
+ "create.test.in.the.same.root": "true",
+ "git-widget-placeholder": "#4 on sprint__9-solution-http-api",
+ "kotlin-language-version-configured": "true",
+ "last_directory_selection": "C:/Users/Pe/Desktop/Курсы JAVA/java-kanban/src/model",
+ "last_opened_file_path": "C:/Users/Pe/Downloads/gson-2.9.0.jar",
+ "onboarding.tips.debug.path": "C:/Users/Pe/Desktop/Курсы JAVA/java-kanban/addmaven/src/main/java/org/example/Main.java",
+ "project.structure.last.edited": "Modules",
+ "project.structure.proportion": "0.15",
+ "project.structure.side.proportion": "0.2",
+ "settings.editor.selected.configurable": "project.propCompiler"
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1728579792798
+
+
+ 1728579792798
+
+
+
+ 1728672955693
+
+
+
+ 1728672955693
+
+
+
+ 1728847181637
+
+
+
+ 1728847181637
+
+
+
+ 1728852834001
+
+
+
+ 1728852834001
+
+
+
+ 1728852950849
+
+
+
+ 1728852950849
+
+
+
+ 1729017107826
+
+
+
+ 1729017107827
+
+
+
+ 1733325330751
+
+
+
+ 1733325330751
+
+
+
+ 1733326144984
+
+
+
+ 1733326144984
+
+
+
+ 1733326611935
+
+
+
+ 1733326611935
+
+
+
+ 1733326687599
+
+
+
+ 1733326687599
+
+
+
+ 1733326933671
+
+
+
+ 1733326933671
+
+
+
+ 1733327124483
+
+
+
+ 1733327124483
+
+
+
+ 1733327334405
+
+
+
+ 1733327334405
+
+
+
+ 1733328226546
+
+
+
+ 1733328226546
+
+
+
+ 1737906130476
+
+
+
+ 1737906130476
+
+
+
+ 1737906698041
+
+
+
+ 1737906698041
+
+
+
+ 1738047329421
+
+
+
+ 1738047329421
+
+
+
+ 1738047449787
+
+
+
+ 1738047449787
+
+
+
+ 1738169216256
+
+
+
+ 1738169216256
+
+
+
+ 1738169369883
+
+
+
+ 1738169369883
+
+
+
+ 1738169553329
+
+
+
+ 1738169553329
+
+
+
+ 1738320082047
+
+
+
+ 1738320082047
+
+
+
+ 1739824491160
+
+
+
+ 1739824491160
+
+
+
+ 1739831473570
+
+
+
+ 1739831473570
+
+
+
+ 1739831597227
+
+
+
+ 1739831597227
+
+
+
+ 1739867839850
+
+
+
+ 1739867839850
+
+
+
+ 1739867962508
+
+
+
+ 1739867962508
+
+
+
+ 1739868061629
+
+
+
+ 1739868061629
+
+
+
+ 1739868154592
+
+
+
+ 1739868154592
+
+
+
+ 1739868612790
+
+
+
+ 1739868612790
+
+
+
+ 1739868625630
+
+
+
+ 1739868625630
+
+
+
+ 1739889876306
+
+
+
+ 1739889876306
+
+
+
+ 1739964679722
+
+
+
+ 1739964679723
+
+
+
+ 1740347599727
+
+
+
+ 1740347599727
+
+
+
+ 1740347642026
+
+
+
+ 1740347642026
+
+
+
+ 1740347804363
+
+
+
+ 1740347804363
+
+
+
+ 1740347858040
+
+
+
+ 1740347858040
+
+
+
+ 1740830811952
+
+
+
+ 1740830811952
+
+
+
+ 1740839603730
+
+
+
+ 1740839603730
+
+
+
+ 1740840208644
+
+
+
+ 1740840208644
+
+
+
+ 1740840811088
+
+
+
+ 1740840811088
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Test/manager/InMemoryHistoryManagerTest.java b/Test/manager/InMemoryHistoryManagerTest.java
deleted file mode 100644
index 6dfb7f4..0000000
--- a/Test/manager/InMemoryHistoryManagerTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package manager;
-
-import model.Epic;
-import model.Status;
-import model.Subtask;
-import model.Task;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class InMemoryHistoryManagerTest {
-
- private static TaskManager taskManager;
-
- @BeforeEach
- public void beforeEach() {
- taskManager = Managers.getDefault();
- }
-
- @Test
- public void getHistoryShouldReturnListOf10Tasks() {
- for (int i = 0; i < 20; i++) {
- taskManager.addTask(new Task("Some name", "Some description"));
- }
-
- List tasks = taskManager.getTasks();
- for (Task task : tasks) {
- taskManager.getTaskByID(task.getId());
- }
-
- List list = taskManager.getHistory();
- assertEquals(10, list.size(), "Неверное количество элементов в истории ");
- }
-
- @Test
- public void getHistoryShouldReturnOldTaskAfterUpdate() {
- Task washFloor = new Task("Помыть полы", "С новым средством");
- taskManager.addTask(washFloor);
- taskManager.getTaskByID(washFloor.getId());
- taskManager.updateTask(new Task(washFloor.getId(), "Не забыть помыть полы",
- "Можно и без средства", Status.IN_PROGRESS));
- List tasks = taskManager.getHistory();
- Task oldTask = tasks.getFirst();
- assertEquals(washFloor.getName(), oldTask.getName(), "В истории не сохранилась старая версия задачи");
- assertEquals(washFloor.getDescription(), oldTask.getDescription(),
- "В истории не сохранилась старая версия задачи");
-
- }
-
- @Test
- public void getHistoryShouldReturnOldEpicAfterUpdate() {
- Epic flatRenovation = new Epic(1, "Сделать ремонт", "Нужно успеть за отпуск");
- taskManager.addEpic(flatRenovation);
- taskManager.getEpicByID(flatRenovation.getId());
- taskManager.updateEpic(new Epic(flatRenovation.getId(), "Новое имя", "новое описание"
- ));
- List epics = taskManager.getHistory();
- Epic oldEpic = (Epic) epics.getFirst();
- assertEquals(flatRenovation.getName(), oldEpic.getName(),
- "В истории не сохранилась старая версия эпика");
- assertEquals(flatRenovation.getDescription(), oldEpic.getDescription(),
- "В истории не сохранилась старая версия эпика");
- }
-
- @Test
- public void getHistoryShouldReturnOldSubtaskAfterUpdate() {
- Epic flatRenovation = new Epic(1, "Сделать ремонт", "Нужно успеть за отпуск");
- taskManager.addEpic(flatRenovation);
- Subtask flatRenovationSubtask3 = new Subtask("Заказать книжный шкаф", "Из темного дерева",
- flatRenovation.getId());
- taskManager.addSubtask(flatRenovationSubtask3);
- taskManager.getSubtaskByID(flatRenovationSubtask3.getId());
- taskManager.updateSubtask(new Subtask(flatRenovationSubtask3.getId(), "Новое имя",
- "новое описание", Status.IN_PROGRESS, flatRenovation.getId()));
- List subtasks = taskManager.getHistory();
- Subtask oldSubtask = (Subtask) subtasks.getFirst();
- assertEquals(flatRenovationSubtask3.getName(), oldSubtask.getName(),
- "В истории не сохранилась старая версия эпика");
- assertEquals(flatRenovationSubtask3.getDescription(), oldSubtask.getDescription(),
- "В истории не сохранилась старая версия эпика");
- }
-}
\ No newline at end of file
diff --git a/Test/manager/InMemoryTaskManagerTest.java b/Test/manager/InMemoryTaskManagerTest.java
deleted file mode 100644
index 4cbfa5e..0000000
--- a/Test/manager/InMemoryTaskManagerTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package manager;
-
-import model.Epic;
-import model.Status;
-import model.Subtask;
-import model.Task;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class InMemoryTaskManagerTest {
-
- private static TaskManager taskManager;
-
- @BeforeEach
- public void beforeEach() {
- taskManager = Managers.getDefault();
- }
-
- @Test
- void addNewTask() {
- //проверяем, что InMemoryTaskManager добавляет задачи и может найти их по id;
- final Task task = taskManager.addTask(new Task("Test addNewTask", "Test addNewTask description"));
- final Task savedTask = taskManager.getTaskByID(task.getId());
- assertNotNull(savedTask, "Задача не найдена.");
- assertEquals(task, savedTask, "Задачи не совпадают.");
-
- final List tasks = taskManager.getTasks();
- assertNotNull(tasks, "Задачи не возвращаются.");
- assertEquals(1, tasks.size(), "Неверное количество задач.");
- assertEquals(task, tasks.getFirst(), "Задачи не совпадают.");
- }
-
- @Test
- void addNewEpicAndSubtasks() {
- //проверяем, что InMemoryTaskManager добавляет эпики и подзадачи и может найти их по id;
- final Epic flatRenovation = taskManager.addEpic(new Epic(1, "Сделать ремонт", "Нужно успеть за отпуск"));
- final Subtask flatRenovationSubtask1 = taskManager.addSubtask(new Subtask("Поклеить обои",
- "Обязательно светлые!", flatRenovation.getId()));
- final Subtask flatRenovationSubtask2 = taskManager.addSubtask(new Subtask("Установить новую технику",
- "Старую продать на Авито", flatRenovation.getId()));
- final Subtask flatRenovationSubtask3 = taskManager.addSubtask(new Subtask("Заказать книжный шкаф", "Из темного дерева",
- flatRenovation.getId()));
- final Epic savedEpic = taskManager.getEpicByID(flatRenovation.getId());
- final Subtask savedSubtask1 = taskManager.getSubtaskByID(flatRenovationSubtask1.getId());
- final Subtask savedSubtask2 = taskManager.getSubtaskByID(flatRenovationSubtask2.getId());
- final Subtask savedSubtask3 = taskManager.getSubtaskByID(flatRenovationSubtask3.getId());
- assertNotNull(savedEpic, "Эпик не найден.");
- assertNotNull(savedSubtask2, "Подзадача не найдена.");
- assertEquals(flatRenovation, savedEpic, "Эпики не совпадают.");
- assertEquals(flatRenovationSubtask1, savedSubtask1, "Подзадачи не совпадают.");
- assertEquals(flatRenovationSubtask3, savedSubtask3, "Подзадачи не совпадают.");
-
- final List epics = taskManager.getEpics();
- assertNotNull(epics, "Эпики не возвращаются.");
- assertEquals(1, epics.size(), "Неверное количество эпиков.");
- assertEquals(flatRenovation, epics.getFirst(), "Эпики не совпадают.");
-
- final List subtasks = taskManager.getSubtasks();
- assertNotNull(subtasks, "Подзадачи не возвращаются.");
- assertEquals(3, subtasks.size(), "Неверное количество подзадач.");
- assertEquals(savedSubtask1, subtasks.getFirst(), "Подзадачи не совпадают.");
- }
-
- @Test
- public void updateTaskShouldReturnTaskWithTheSameId() {
- final Task expected = new Task("имя", "описание");
- taskManager.addTask(expected);
- final Task updatedTask = new Task(expected.getId(), "новое имя", "новое описание", Status.DONE);
- final Task actual = taskManager.updateTask(updatedTask);
- assertEquals(expected, actual, "Вернулась задачи с другим id");
- }
-
-
- @Test
- public void updateSubtaskShouldReturnSubtaskWithTheSameId() {
- final Epic epic = new Epic(1, "Сделать ремонт", "Нужно успеть за отпуск");
- taskManager.addEpic(epic);
- final Subtask expected = new Subtask("имя", "описание", epic.getId());
- taskManager.addSubtask(expected);
- final Subtask updatedSubtask = new Subtask(expected.getId(), "новое имя", "новое описание",
- Status.DONE, epic.getId());
- final Subtask actual = taskManager.updateSubtask(updatedSubtask);
- assertEquals(expected, actual, "Вернулась подзадача с другим id");
- }
-
- @Test
- public void deleteTasksShouldReturnEmptyList() {
- taskManager.addTask(new Task("Купить книги", "Список в заметках"));
- taskManager.addTask(new Task("Помыть полы", "С новым средством"));
- taskManager.deleteTasks();
- List tasks = taskManager.getTasks();
- assertTrue(tasks.isEmpty(), "После удаления задач список должен быть пуст.");
- }
-
- @Test
- public void deleteEpicsShouldReturnEmptyList() {
- taskManager.addEpic(new Epic(1, "Сделать ремонт", "Нужно успеть за отпуск"));
- taskManager.deleteEpics();
- List epics = taskManager.getEpics();
- assertTrue(epics.isEmpty(), "После удаления эпиков список должен быть пуст.");
- }
-
- @Test
- public void deleteSubtasksShouldReturnEmptyList() {
- Epic flatRenovation = new Epic(1, "Сделать ремонт", "Нужно успеть за отпуск");
- taskManager.addEpic(flatRenovation);
- taskManager.addSubtask(new Subtask("Поклеить обои", "Обязательно светлые!",
- flatRenovation.getId()));
- taskManager.addSubtask(new Subtask("Установить новую технику", "Старую продать на Авито",
- flatRenovation.getId()));
- taskManager.addSubtask(new Subtask("Заказать книжный шкаф", "Из темного дерева",
- flatRenovation.getId()));
-
- taskManager.deleteSubtasks();
- List subtasks = taskManager.getSubtasks();
- assertTrue(subtasks.isEmpty(), "После удаления подзадач список должен быть пуст.");
- }
-
- @Test
- public void tasksWithEqualIdShouldBeEqual() {
- Task task1 = new Task(10, "Забрать заказ", "На Ozon", Status.NEW);
- Task task2 = new Task(10, "Забрать заказ", "На Wildberries", Status.DONE);
- assertEquals(task1, task2, "Ошибка! Экземпляры класса Task должны быть равны друг другу, если равен их id;");
- }
-
- @Test
- public void EpicsWithEqualIdShouldBeEqual() {
- Epic epic1 = new Epic(10, "Сделать ремонт", "Уложиться в 2 миллиона");
- Epic epic2 = new Epic(10, "Подготовиться к собеседованию", "1 июля в 11:00");
- assertEquals(epic1, epic2,
- "Ошибка! Наследники класса Task должны быть равны друг другу, если равен их id;");
- }
-
- @Test
- public void SubtasksWithEqualEpicIDShouldBeEqual() {
- Subtask subtask1 = new Subtask(10, "Забрать заказ", "На Ozon", Status.NEW, 5);
- Subtask subtask2 = new Subtask(10, "Забрать заказ", "На Wildberries", Status.DONE, 5);
- assertEquals(subtask1, subtask2, "Ошибка! Наследники класса Task должны быть равны друг другу, если равен их epicID;");
- }
-
-
- @Test
- void TaskCreatedAndTaskAddedShouldHaveSameVariables() {
- Task expected = new Task(1, "Помыть полы", "С новым средством", Status.DONE);
- taskManager.addTask(expected);
- List list = taskManager.getTasks();
- Task actual = list.getFirst();
- assertEquals(expected.getId(), actual.getId());
- assertEquals(expected.getName(), actual.getName());
- assertEquals(expected.getDescription(), actual.getDescription());
- assertEquals(expected.getStatus(), actual.getStatus());
- }
-
-}
\ No newline at end of file
diff --git a/Test/model/SubtaskTest.java b/Test/model/SubtaskTest.java
deleted file mode 100644
index 626c7c1..0000000
--- a/Test/model/SubtaskTest.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package model;
-
-import org.junit.jupiter.api.Test;
-
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class SubtaskTest {
-
- @Test
- public void SubtasksWithEqualEpicIDShouldBeEqual() {
- Subtask subtask1 = new Subtask(10, "Забрать заказ", "На Ozon", Status.NEW, 5);
- Subtask subtask2 = new Subtask(10, "Забрать заказ", "На Wildberries", Status.DONE, 5);
- assertEquals(subtask1, subtask2, "Ошибка! Наследники класса Task должны быть равны друг другу, если равен их epicID;");
- }
-}
\ No newline at end of file
diff --git a/java-kanban.iml b/java-kanban.iml
index 55a0bff..c3380da 100644
--- a/java-kanban.iml
+++ b/java-kanban.iml
@@ -1,24 +1,18 @@
-
-
+
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -27,24 +21,24 @@
-
-
-
-
-
-
-
-
+
+
-
+
-
-
+
+
+
+
+
+
+
+
diff --git a/src/Main.java b/src/Main.java
index 23497af..52ab0ce 100644
--- a/src/Main.java
+++ b/src/Main.java
@@ -5,6 +5,9 @@
import model.Subtask;
import model.Task;
+import java.time.Duration;
+import java.time.LocalDateTime;
+
public class Main {
private static final TaskManager taskManager = Managers.getDefault();
@@ -16,22 +19,27 @@ public static void main(String[] args) {
}
private static void addTasks() {
- Task packBoxes = new Task("Упаковать вещи", "В коробки и мешки");
+ Task packBoxes = new Task("Упаковать вещи", "В коробки и мешки", Duration.ofMinutes(30), LocalDateTime.now());
taskManager.addTask(packBoxes);
- Task packBoxesToUpdate = new Task(packBoxes.getId(), "Упаковать вещи быстро", "В коробки и мешки",
- Status.IN_PROGRESS);
+ Task packBoxesToUpdate = new Task(packBoxes.getId(), "Упаковать вещи быстро", "В коробки и мешки",
+ Status.IN_PROGRESS, Duration.ofMinutes(30), LocalDateTime.now());
taskManager.updateTask(packBoxesToUpdate);
- taskManager.addTask(new Task("Придумать список дел после перезда", "Список в заметках"));
- Epic moving = new Epic(10, "Переезд", "Нужно успеть до конца месяца");
+ Task planTasks = new Task("Придумать список дел после переезда", "Список в заметках", Duration.ofMinutes(60), LocalDateTime.now().plusHours(1));
+ taskManager.addTask(planTasks);
+
+ Epic moving = new Epic("Переезд", "Нужно успеть до конца месяца");
taskManager.addEpic(moving);
- Subtask packKitchen = new Subtask("Упаковать кухню", "В отдельные коробки",
- moving.getId());
- Subtask packBedroom = new Subtask("Упаковать спальню", "В большие коробки",
- moving.getId());
+
+ Subtask packKitchen = new Subtask("Упаковать кухню", "В отдельные коробки", moving.getId(),
+ Duration.ofMinutes(120), LocalDateTime.now().plusHours(2));
taskManager.addSubtask(packKitchen);
+
+ Subtask packBedroom = new Subtask("Упаковать спальню", "В большие коробки", moving.getId(),
+ Duration.ofMinutes(90), LocalDateTime.now().plusHours(3));
taskManager.addSubtask(packBedroom);
+
packBedroom.setStatus(Status.DONE);
taskManager.updateSubtask(packBedroom);
}
diff --git a/src/exceptions/ManagerSaveException.java b/src/exceptions/ManagerSaveException.java
new file mode 100644
index 0000000..e43db7e
--- /dev/null
+++ b/src/exceptions/ManagerSaveException.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class ManagerSaveException extends RuntimeException {
+ public ManagerSaveException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
\ No newline at end of file
diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java
new file mode 100644
index 0000000..b697527
--- /dev/null
+++ b/src/manager/FileBackedTaskManager.java
@@ -0,0 +1,195 @@
+package manager;
+
+import exceptions.ManagerSaveException;
+import model.Epic;
+import model.Status;
+import model.Subtask;
+import model.Task;
+import tasktype.TaskType;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+public class FileBackedTaskManager extends InMemoryTaskManager {
+ private final File file;
+
+ public FileBackedTaskManager(File file) {
+ this.file = file;
+ }
+
+ @Override
+ public Task addTask(Task task) {
+ Task addedTask = super.addTask(task);
+ save();
+ return addedTask;
+ }
+
+ @Override
+ public Epic addEpic(Epic epic) {
+ Epic addedEpic = super.addEpic(epic);
+ save();
+ return addedEpic;
+ }
+
+ @Override
+ public Subtask addSubtask(Subtask subtask) {
+ Subtask addedSubtask = super.addSubtask(subtask);
+ save();
+ return addedSubtask;
+ }
+
+ @Override
+ public Task updateTask(Task updatedTask) {
+ Task existingTask = super.updateTask(updatedTask);
+ save();
+ return existingTask;
+ }
+
+ @Override
+ public Epic updateEpic(Epic updatedEpic) {
+ Epic existingEpic = super.updateEpic(updatedEpic);
+ save();
+ return existingEpic;
+ }
+
+ @Override
+ public Subtask updateSubtask(Subtask updatedSubtask) {
+ Subtask existingSubtask = super.updateSubtask(updatedSubtask);
+ save();
+ return existingSubtask;
+ }
+
+ @Override
+ public void deleteTaskByID(int id) {
+ super.deleteTaskByID(id);
+ save();
+ }
+
+ @Override
+ public void deleteEpicByID(int id) {
+ super.deleteEpicByID(id);
+ save();
+ }
+
+ @Override
+ public void deleteSubtaskByID(int id) {
+ super.deleteSubtaskByID(id);
+ save();
+ }
+
+ @Override
+ public void deleteTasks() {
+ super.deleteTasks();
+ save();
+ }
+
+ @Override
+ public void deleteEpics() {
+ super.deleteEpics();
+ save();
+ }
+
+ @Override
+ public void deleteSubtasks() {
+ super.deleteSubtasks();
+ save();
+ }
+
+ public void save() {
+ try (PrintWriter writer = new PrintWriter(file)) {
+ writer.println("id,type,name,status,description,epic,duration,startTime");
+ for (Task task : getTasks()) {
+ writer.println(toString(task));
+ }
+ for (Epic epic : getEpics()) {
+ writer.println(toString(epic));
+ }
+ for (Subtask subtask : getSubtasks()) {
+ writer.println(toString(subtask));
+ }
+ } catch (IOException e) {
+ throw new ManagerSaveException("Failed to save tasks to file", e);
+ }
+ }
+
+ private String toString(Task task) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(task.getId()).append(",")
+ .append(task.getType()).append(",")
+ .append(task.getName()).append(",")
+ .append(task.getStatus()).append(",")
+ .append(task.getDescription()).append(",");
+
+ if (task instanceof Subtask) {
+ sb.append(((Subtask) task).getEpicID()).append(",");
+ } else {
+ sb.append(",");
+ }
+
+ sb.append(task.getDuration() != null ? task.getDuration().toMinutes() : 0).append(",")
+ .append(task.getStartTime() != null ? task.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : "");
+
+ return sb.toString();
+ }
+
+ public static FileBackedTaskManager loadFromFile(File file) {
+ FileBackedTaskManager manager = new FileBackedTaskManager(file);
+ try {
+ List lines = Files.readAllLines(file.toPath());
+ if (lines.size() > 1) {
+
+ for (String line : lines.subList(1, lines.size())) {
+ Task task = fromString(line);
+ if (task instanceof Epic) {
+ manager.addEpic((Epic) task);
+ System.out.println("Loaded epic: " + task);
+ }
+ }
+
+ for (String line : lines.subList(1, lines.size())) {
+ Task task = fromString(line);
+ if (task instanceof Subtask) {
+ manager.addSubtask((Subtask) task);
+ System.out.println("Loaded subtask: " + task);
+ } else if (task instanceof Task && !(task instanceof Epic)) {
+ manager.addTask(task);
+ System.out.println("Loaded task: " + task);
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new ManagerSaveException("Failed to load tasks from file", e);
+ }
+ return manager;
+ }
+
+ private static Task fromString(String value) {
+ String[] parts = value.split(",");
+ int id = Integer.parseInt(parts[0]);
+ String type = parts[1];
+ String name = parts[2];
+ Status status = Status.valueOf(parts[3]);
+ String description = parts[4];
+
+ TaskType taskType = TaskType.valueOf(type);
+ Duration duration = Duration.ofMinutes(Long.parseLong(parts[6]));
+ LocalDateTime startTime = parts.length > 7 && !parts[7].isEmpty() ? LocalDateTime.parse(parts[7]) : null;
+
+ switch (taskType) {
+ case SUBTASK:
+ int epicID = Integer.parseInt(parts[5]);
+ return new Subtask(id, name, description, status, duration, startTime, epicID);
+ case EPIC:
+ return new Epic(id, name, description, status, duration, startTime);
+ case TASK:
+ default:
+ return new Task(id, name, description, status, duration, startTime);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/manager/HistoryManager.java b/src/manager/HistoryManager.java
index b2bcde7..d06a752 100644
--- a/src/manager/HistoryManager.java
+++ b/src/manager/HistoryManager.java
@@ -5,6 +5,11 @@
import java.util.List;
public interface HistoryManager {
+
void add(Task task);
+
+ void remove(int id);
+
List getHistory();
+
}
\ No newline at end of file
diff --git a/src/manager/InMemoryHistoryManager.java b/src/manager/InMemoryHistoryManager.java
index b9908b3..3676eae 100644
--- a/src/manager/InMemoryHistoryManager.java
+++ b/src/manager/InMemoryHistoryManager.java
@@ -4,20 +4,119 @@
import java.util.*;
-
public class InMemoryHistoryManager implements HistoryManager {
- private final List history = new ArrayList<>();
+ private static class CustomLinkedList {
+ private final Map table = new HashMap<>();
+ private Node head;
+ private Node tail;
+
+ private static class Node {
+ private Task task;
+ private Node prev;
+ private Node next;
+
+ public Task getTask() {
+ return task;
+ }
+
+ public void setTask(Task task) {
+ this.task = task;
+ }
+
+ public Node getPrev() {
+ return prev;
+ }
+
+ public void setPrev(Node prev) {
+ this.prev = prev;
+ }
+
+ public Node getNext() {
+ return next;
+ }
+
+ public void setNext(Node next) {
+ this.next = next;
+ }
+ }
+
+ private void linkLast(Task task) {
+ Node element = new Node();
+ element.setTask(task);
+
+ if (table.containsKey(task.getId())) {
+ removeNode(table.get(task.getId()));
+ }
+
+ if (head == null) {
+ tail = element;
+ head = element;
+ element.setNext(null);
+ element.setPrev(null);
+ } else {
+ element.setPrev(tail);
+ element.setNext(null);
+ tail.setNext(element);
+ tail = element;
+ }
+
+ table.put(task.getId(), element);
+ }
+
+ private List getTasks() {
+ List result = new ArrayList<>();
+ Node element = head;
+ while (element != null) {
+ result.add(element.getTask());
+ element = element.getNext();
+ }
+ return result;
+ }
+
+ private void removeNode(Node node) {
+ if (node != null) {
+ table.remove(node.getTask().getId());
+ Node prev = node.getPrev();
+ Node next = node.getNext();
+
+ if (head == node) {
+ head = node.getNext();
+ }
+ if (tail == node) {
+ tail = node.getPrev();
+ }
+
+ if (prev != null) {
+ prev.setNext(next);
+ }
+
+ if (next != null) {
+ next.setPrev(prev);
+ }
+ }
+ }
+
+ private Node getNode(int id) {
+ return table.get(id);
+ }
+ }
+
+ private final CustomLinkedList list = new CustomLinkedList();
@Override
public void add(Task task) {
- if (history.size() >= 10) {
- history.remove(0);
- }
- history.add(task);
+ list.linkLast(task);
+ }
+
+ @Override
+ public void remove(int id) {
+ list.removeNode(list.getNode(id));
}
@Override
public List getHistory() {
- return List.copyOf(history);
+ return list.getTasks();
}
-}
\ No newline at end of file
+}
+
+
diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java
index aa804bd..665bad6 100644
--- a/src/manager/InMemoryTaskManager.java
+++ b/src/manager/InMemoryTaskManager.java
@@ -1,31 +1,38 @@
package manager;
-
import model.Epic;
import model.Status;
import model.Subtask;
import model.Task;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.time.ZoneOffset;
+import java.util.*;
public class InMemoryTaskManager implements TaskManager {
private final Map tasks = new HashMap<>();
private final Map epics = new HashMap<>();
private final Map subtasks = new HashMap<>();
private final HistoryManager historyManager;
+ private final Set prioritizedTasks = new TreeSet<>(Comparator.comparing(Task::getStartTime, Comparator.nullsLast(Comparator.naturalOrder())));
private int nextID = 1;
public InMemoryTaskManager() {
this.historyManager = Managers.getDefaultHistory();
}
+ private int getNextID() {
+ return nextID++;
+ }
+
@Override
public Task addTask(Task task) {
+ validateTask(task);
+ if (isOverlapping(task)) {
+ throw new IllegalArgumentException("Task overlaps with existing task");
+ }
task.setId(getNextID());
tasks.put(task.getId(), task);
+ prioritizedTasks.add(task);
return task;
}
@@ -38,22 +45,41 @@ public Epic addEpic(Epic epic) {
@Override
public Subtask addSubtask(Subtask subtask) {
- subtask.setId(getNextID());
Epic epic = epics.get(subtask.getEpicID());
+ if (epic == null) {
+ throw new IllegalArgumentException("Epic with ID " + subtask.getEpicID() + " does not exist.");
+ }
+ validateTask(subtask);
+
+ subtask.setId(getNextID());
+
+ while (isOverlapping(subtask)) {
+ subtask.setStartTime(subtask.getStartTime().plusMinutes(10));
+ }
+
epic.addSubtask(subtask);
subtasks.put(subtask.getId(), subtask);
+ prioritizedTasks.add(subtask);
updateEpicStatus(epic);
return subtask;
}
@Override
- public Task updateTask(Task task) {
- Integer taskID = task.getId();
- if (taskID == null || !tasks.containsKey(taskID)) {
- return null;
+ public Task updateTask(Task updatedTask) {
+ validateTask(updatedTask);
+ Task existingTask = getTaskByID(updatedTask.getId());
+ if (existingTask != null) {
+ tasks.remove(existingTask.getId());
+ prioritizedTasks.remove(existingTask);
+ if (isOverlapping(updatedTask)) {
+ tasks.put(existingTask.getId(), existingTask);
+ prioritizedTasks.add(existingTask);
+ throw new IllegalArgumentException("Task overlaps with existing task");
+ }
+ tasks.put(updatedTask.getId(), updatedTask);
+ prioritizedTasks.add(updatedTask);
}
- tasks.replace(taskID, task);
- return task;
+ return existingTask;
}
@Override
@@ -62,23 +88,7 @@ public Epic updateEpic(Epic epic) {
if (epicID == null || !epics.containsKey(epicID)) {
return null;
}
- // если у эпика были подзадачи, удаляем их из мапы с подзадачами
- Epic oldEpic = epics.get(epicID);
- ArrayList oldEpicSubtaskList = oldEpic.getSubtaskList();
- if (!oldEpicSubtaskList.isEmpty()) {
- for (Subtask subtask : oldEpicSubtaskList) {
- subtasks.remove(subtask.getId());
- }
- }
- epics.replace(epicID, epic);
- // если у обновленного эпика есть подзадачи, добавляем их в мапу подзадач
- ArrayList newEpicSubtaskList = epic.getSubtaskList();
- if (!newEpicSubtaskList.isEmpty()) {
- for (Subtask subtask : newEpicSubtaskList) {
- subtasks.put(subtask.getId(), subtask);
- }
- }
- // обновляем статус эпика
+ epics.put(epicID, epic);
updateEpicStatus(epic);
return epic;
}
@@ -90,14 +100,23 @@ public Subtask updateSubtask(Subtask subtask) {
return null;
}
int epicID = subtask.getEpicID();
- Subtask oldSubtask = subtasks.get(subtaskID);
- subtasks.replace(subtaskID, subtask);
- // обновляем подзадачу в списке подзадач эпика и проверяем статус эпика
Epic epic = epics.get(epicID);
- ArrayList subtaskList = epic.getSubtaskList();
- subtaskList.remove(oldSubtask);
- subtaskList.add(subtask);
- epic.setSubtaskList(subtaskList);
+ Subtask oldSubtask = subtasks.get(subtaskID);
+
+ epic.getSubtaskList().remove(oldSubtask);
+ subtasks.remove(subtaskID);
+ prioritizedTasks.remove(oldSubtask);
+
+ if (isOverlapping(subtask)) {
+ epic.getSubtaskList().add(oldSubtask);
+ subtasks.put(subtaskID, oldSubtask);
+ prioritizedTasks.add(oldSubtask);
+ throw new IllegalArgumentException("Subtask overlaps with existing task");
+ }
+
+ subtasks.put(subtaskID, subtask);
+ prioritizedTasks.add(subtask);
+ epic.addSubtask(subtask);
updateEpicStatus(epic);
return subtask;
}
@@ -151,49 +170,76 @@ public ArrayList getEpicSubtasks(Epic epic) {
@Override
public void deleteTasks() {
+ for (Task task : tasks.values()) {
+ historyManager.remove(task.getId());
+ prioritizedTasks.remove(task);
+ }
tasks.clear();
}
@Override
public void deleteEpics() {
+ for (Epic epic : epics.values()) {
+ for (Subtask subtask : epic.getSubtaskList()) {
+ subtasks.remove(subtask.getId());
+ prioritizedTasks.remove(subtask);
+ historyManager.remove(subtask.getId());
+ }
+ }
epics.clear();
- subtasks.clear();
}
@Override
public void deleteSubtasks() {
- subtasks.clear();
- for (Epic epic : epics.values()) {
- epic.clearSubtasks();
- epic.setStatus(Status.NEW);
+ for (Subtask subtask : new ArrayList<>(subtasks.values())) {
+ int epicID = subtask.getEpicID();
+ subtasks.remove(subtask.getId());
+ prioritizedTasks.remove(subtask);
+ historyManager.remove(subtask.getId());
+ Epic epic = epics.get(epicID);
+ if (epic != null) {
+ epic.getSubtaskList().remove(subtask);
+ updateEpicStatus(epic);
+ }
}
}
@Override
public void deleteTaskByID(int id) {
- tasks.remove(id);
+ Task task = tasks.remove(id);
+ if (task != null) {
+ prioritizedTasks.remove(task);
+ historyManager.remove(id);
+ }
}
@Override
public void deleteEpicByID(int id) {
- ArrayList epicSubtasks = epics.get(id).getSubtaskList();
- epics.remove(id);
- for (Subtask subtask : epicSubtasks) {
- subtasks.remove(subtask.getId());
+ Epic epic = epics.get(id);
+ if (epic != null) {
+ for (Subtask subtask : epic.getSubtaskList()) {
+ subtasks.remove(subtask.getId());
+ prioritizedTasks.remove(subtask);
+ historyManager.remove(subtask.getId());
+ }
+ epics.remove(id);
}
}
@Override
public void deleteSubtaskByID(int id) {
Subtask subtask = subtasks.get(id);
- int epicID = subtask.getEpicID();
- subtasks.remove(id);
- // обновляем список подзадач и статус эпика
- Epic epic = epics.get(epicID);
- ArrayList subtaskList = epic.getSubtaskList();
- subtaskList.remove(subtask);
- epic.setSubtaskList(subtaskList);
- updateEpicStatus(epic);
+ if (subtask != null) {
+ int epicID = subtask.getEpicID();
+ subtasks.remove(id);
+ prioritizedTasks.remove(subtask);
+ historyManager.remove(id);
+ Epic epic = epics.get(epicID);
+ if (epic != null) {
+ epic.getSubtaskList().remove(subtask);
+ updateEpicStatus(epic);
+ }
+ }
}
@Override
@@ -201,8 +247,35 @@ public List getHistory() {
return historyManager.getHistory();
}
- private int getNextID() {
- return nextID++;
+ @Override
+ public List getPrioritizedTasks() {
+ return new ArrayList<>(prioritizedTasks);
+ }
+
+ private boolean isOverlapping(Task newTask) {
+ for (Task existingTask : tasks.values()) {
+ if (existingTask.getId() != newTask.getId() && isOverlapping(existingTask, newTask)) {
+ return true;
+ }
+ }
+ for (Subtask existingSubtask : subtasks.values()) {
+ if (existingSubtask.getId() != newTask.getId() && isOverlapping(existingSubtask, newTask)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isOverlapping(Task t1, Task t2) {
+ if (t1.getStartTime() == null || t2.getStartTime() == null) {
+ return false;
+ }
+ long t1StartEpoch = t1.getStartTime().toEpochSecond(ZoneOffset.UTC);
+ long t1EndEpoch = t1StartEpoch + t1.getDuration().getSeconds();
+ long t2StartEpoch = t2.getStartTime().toEpochSecond(ZoneOffset.UTC);
+ long t2EndEpoch = t2StartEpoch + t2.getDuration().getSeconds();
+
+ return t1StartEpoch < t2EndEpoch && t2StartEpoch < t1EndEpoch;
}
private void updateEpicStatus(Epic epic) {
@@ -218,7 +291,7 @@ private void updateEpicStatus(Epic epic) {
allIsInNewCount++;
}
}
- if (allIsDoneCount == list.size()) {
+ if (list.isEmpty() || allIsDoneCount == list.size()) {
epic.setStatus(Status.DONE);
} else if (allIsInNewCount == list.size()) {
epic.setStatus(Status.NEW);
@@ -226,4 +299,13 @@ private void updateEpicStatus(Epic epic) {
epic.setStatus(Status.IN_PROGRESS);
}
}
-}
+
+ void validateTask(Task task) {
+ if (task.getName() == null || task.getName().isEmpty()) {
+ throw new IllegalArgumentException("Task name cannot be null or empty.");
+ }
+ if (task.getDescription() == null) {
+ throw new IllegalArgumentException("Task description cannot be null.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/manager/Node.java b/src/manager/Node.java
new file mode 100644
index 0000000..4f9bb35
--- /dev/null
+++ b/src/manager/Node.java
@@ -0,0 +1,34 @@
+package manager;
+
+import model.Task;
+
+public class Node {
+ private Task task;
+ private manager.Node prev;
+ private manager.Node next;
+
+ public manager.Node getNext() {
+ return next;
+ }
+
+ public manager.Node getPrev() {
+ return prev;
+ }
+
+ public Task getTask() {
+ return task;
+ }
+
+ public void setNext(manager.Node next) {
+ this.next = next;
+ }
+
+ public void setPrev(manager.Node prev) {
+ this.prev = prev;
+ }
+
+ public void setTask(Task task) {
+ this.task = task;
+ }
+ }
+
diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java
index 917f3f8..f033444 100644
--- a/src/manager/TaskManager.java
+++ b/src/manager/TaskManager.java
@@ -1,6 +1,5 @@
package manager;
-
import model.Epic;
import model.Subtask;
import model.Task;
@@ -10,23 +9,44 @@
public interface TaskManager {
Task addTask(Task task);
+
Epic addEpic(Epic epic);
+
Subtask addSubtask(Subtask subtask);
- Task updateTask(Task task);
+
+ Task updateTask(Task updatedTask);
+
Epic updateEpic(Epic epic);
+
Subtask updateSubtask(Subtask subtask);
+
Task getTaskByID(int id);
+
Epic getEpicByID(int id);
+
Subtask getSubtaskByID(int id);
+
ArrayList getTasks();
+
ArrayList getEpics();
+
ArrayList getSubtasks();
+
ArrayList getEpicSubtasks(Epic epic);
+
void deleteTasks();
+
void deleteEpics();
+
void deleteSubtasks();
+
void deleteTaskByID(int id);
+
void deleteEpicByID(int id);
+
void deleteSubtaskByID(int id);
+
List getHistory();
-}
+
+ List getPrioritizedTasks();
+}
\ No newline at end of file
diff --git a/src/model/Epic.java b/src/model/Epic.java
index 3817186..af57640 100644
--- a/src/model/Epic.java
+++ b/src/model/Epic.java
@@ -1,22 +1,41 @@
package model;
+import java.time.Duration;
+import java.time.LocalDateTime;
import java.util.ArrayList;
-import java.util.Objects;
public class Epic extends Task {
private ArrayList subtaskList = new ArrayList<>();
public Epic(int id, String name, String description) {
super(id, name, description);
- // Статус NEW устанавливается по умолчанию в конструкторе родительского класса Task
+ }
+
+ public Epic(String name, String description) {
+ super(name, description);
+ }
+
+ public Epic(int id, String name, String description, Status status, Duration duration, LocalDateTime startTime) {
+ super(id, name, description, status, duration, startTime);
+ }
+
+ public Epic(int id, String name, String description, Duration duration, LocalDateTime startTime) {
+ super(id, name, description, Status.NEW, duration, startTime);
}
public void addSubtask(Subtask subtask) {
- subtaskList.add(subtask);
+ if (subtask == null) {
+ throw new IllegalArgumentException("Subtask cannot be null");
+ }
+ if (!subtaskList.contains(subtask)) {
+ subtaskList.add(subtask);
+ updateEpicStatus();
+ }
}
public void clearSubtasks() {
subtaskList.clear();
+ updateEpicStatus();
}
public ArrayList getSubtaskList() {
@@ -24,7 +43,57 @@ public ArrayList getSubtaskList() {
}
public void setSubtaskList(ArrayList subtaskList) {
+ if (subtaskList == null) {
+ throw new IllegalArgumentException("Subtask list cannot be null");
+ }
this.subtaskList = subtaskList;
+ updateEpicStatus();
+ }
+
+ private void updateEpicStatus() {
+ if (subtaskList.isEmpty()) {
+ this.setStatus(Status.NEW);
+ this.duration = Duration.ZERO;
+ this.startTime = null;
+ return;
+ }
+
+ LocalDateTime earliestStart = null;
+ LocalDateTime latestEnd = null;
+ Duration totalDuration = Duration.ZERO;
+ int doneCount = 0;
+ int newCount = 0;
+
+ for (Subtask subtask : subtaskList) {
+ if (subtask.getStartTime() != null) {
+ if (earliestStart == null || subtask.getStartTime().isBefore(earliestStart)) {
+ earliestStart = subtask.getStartTime();
+ }
+ if (latestEnd == null || subtask.getEndTime().isAfter(latestEnd)) {
+ latestEnd = subtask.getEndTime();
+ }
+ }
+ if (subtask.getDuration() != null) {
+ totalDuration = totalDuration.plus(subtask.getDuration());
+ }
+
+ if (subtask.getStatus() == Status.DONE) {
+ doneCount++;
+ } else if (subtask.getStatus() == Status.NEW) {
+ newCount++;
+ }
+ }
+
+ this.startTime = earliestStart;
+ this.duration = totalDuration;
+
+ if (doneCount == subtaskList.size()) {
+ this.setStatus(Status.DONE);
+ } else if (newCount == subtaskList.size()) {
+ this.setStatus(Status.NEW);
+ } else {
+ this.setStatus(Status.IN_PROGRESS);
+ }
}
@Override
@@ -33,25 +102,11 @@ public String toString() {
"name='" + getName() + '\'' +
", description='" + getDescription() + '\'' +
", id=" + getId() +
- ", subtaskList.size=" + subtaskList.size() +
+ ", subtaskList=" + subtaskList +
", status=" + getStatus() +
+ ", duration=" + (getDuration() != null ? getDuration().toMinutes() + " minutes" : "null") +
+ ", startTime=" + (getStartTime() != null ? getStartTime().format(formatter) : "null") +
+ ", endTime=" + (getEndTime() != null ? getEndTime().format(formatter) : "null") +
'}';
}
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- Epic other = (Epic) obj;
- return getId() == other.getId();
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getId());
- }
}
\ No newline at end of file
diff --git a/src/model/Subtask.java b/src/model/Subtask.java
index 38410af..19f75ac 100644
--- a/src/model/Subtask.java
+++ b/src/model/Subtask.java
@@ -1,6 +1,14 @@
package model;
+import com.google.gson.annotations.Expose;
+import tasktype.TaskType;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
public class Subtask extends Task {
+ @Expose
private final int epicID;
public Subtask(String name, String description, int epicID) {
@@ -8,8 +16,13 @@ public Subtask(String name, String description, int epicID) {
this.epicID = epicID;
}
- public Subtask(int id, String name, String description, Status status, int epicID) {
- super(id, name, description, status);
+ public Subtask(int id, String name, String description, Status status, Duration duration, LocalDateTime startTime, int epicID) {
+ super(id, name, description, status, duration, startTime);
+ this.epicID = epicID;
+ }
+
+ public Subtask(String name, String description, int epicID, Duration duration, LocalDateTime startTime) {
+ super(name, description, duration, startTime);
this.epicID = epicID;
}
@@ -19,19 +32,34 @@ public int getEpicID() {
@Override
public String toString() {
- return "model.Subtask{" +
+ return "Subtask{" +
"name='" + getName() + '\'' +
", description='" + getDescription() + '\'' +
", id=" + getId() +
", epicID=" + epicID +
", status=" + getStatus() +
+ ", duration=" + (getDuration() != null ? getDuration().toMinutes() + " minutes" : "null") +
+ ", startTime=" + (getStartTime() != null ? getStartTime().format(formatter) : "null") +
+ ", endTime=" + (getEndTime() != null ? getEndTime().format(formatter) : "null") +
'}';
}
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
Subtask subtask = (Subtask) o;
return epicID == subtask.epicID;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), epicID);
+ }
+
+ @Override
+ public TaskType getType() {
+ return TaskType.SUBTASK;
+ }
}
\ No newline at end of file
diff --git a/src/model/Task.java b/src/model/Task.java
index e70661f..d4dfb15 100644
--- a/src/model/Task.java
+++ b/src/model/Task.java
@@ -1,44 +1,93 @@
package model;
+import com.google.gson.annotations.Expose;
+import tasktype.TaskType;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.Objects;
public class Task {
- private String name;
- private String description;
- private int id;
- private Status status;
+ @Expose
+ protected int id;
+ @Expose
+ protected String name;
+ @Expose
+ protected String description;
+ @Expose
+ protected Status status;
+ @Expose
+ protected Duration duration;
+ @Expose
+ protected LocalDateTime startTime;
+
+ protected static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
- public Task(int id, String name, String description, Status status) {
- this.id = id;
+ public Task(String name, String description) {
this.name = name;
this.description = description;
- this.status = status;
+ this.status = Status.NEW;
}
- public Task(String name, String description) {
+ public Task(Task otherTask) {
+ this.id = otherTask.getId();
+ this.name = otherTask.getName();
+ this.description = otherTask.getDescription();
+ this.status = otherTask.getStatus();
+ this.duration = otherTask.getDuration();
+ this.startTime = otherTask.getStartTime();
+ }
+
+ public Task(String name, String description, Duration duration, LocalDateTime startTime) {
this.name = name;
this.description = description;
this.status = Status.NEW;
+ this.duration = duration;
+ this.startTime = startTime;
}
public Task(int id, String name, String description) {
- this(id, name, description, Status.NEW);
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.status = Status.NEW;
}
- public String getName() {
- return name;
+ public Task(int id, String name, String description, Status status) {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.status = status;
}
- public void setName(String name) {
+ public Task(int id, String name, String description, Status status, Duration duration, LocalDateTime startTime) {
+ this.id = id;
this.name = name;
+ this.description = description;
+ this.status = status;
+ this.duration = duration;
+ this.startTime = startTime;
}
- public String getDescription() {
- return description;
+ public LocalDateTime getEndTime() {
+ return startTime != null && duration != null ? startTime.plus(duration) : null;
}
- public void setDescription(String description) {
- this.description = description;
+ public Duration getDuration() {
+ return duration;
+ }
+
+ public void setDuration(Duration duration) {
+ this.duration = duration;
+ }
+
+ public LocalDateTime getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(LocalDateTime startTime) {
+ this.startTime = startTime;
}
public int getId() {
@@ -49,6 +98,22 @@ public void setId(int id) {
this.id = id;
}
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
public Status getStatus() {
return status;
}
@@ -58,34 +123,32 @@ public void setStatus(Status status) {
}
@Override
- public boolean equals(Object object) {
- if (this == object) return true;
- if (object == null || getClass() != object.getClass()) return false;
- Task task = (Task) object;
- return id == task.id;
+ public String toString() {
+ return "Task{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", description='" + description + '\'' +
+ ", status=" + status +
+ ", duration=" + (duration != null ? duration.toMinutes() + " minutes" : "null") +
+ ", startTime=" + (startTime != null ? startTime.format(formatter) : "null") +
+ ", endTime=" + (getEndTime() != null ? getEndTime().format(formatter) : "null") +
+ '}';
}
@Override
- public int hashCode() {
- int hash = 17;
- if (name != null) {
- hash = hash + name.hashCode();
- }
- hash = hash * 31;
- if (description != null) {
- hash = hash + description.hashCode();
- }
- return hash;
+ 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;
}
@Override
- public String toString() {
- return "model.Task{" +
- "name='" + name + '\'' +
- ", description='" + description + '\'' +
- ", id=" + id +
- ", status=" + status +
- '}';
+ public int hashCode() {
+ return Objects.hash(id);
}
+ public TaskType getType() {
+ return TaskType.TASK;
+ }
}
\ No newline at end of file
diff --git a/src/server/BaseHttpHandler.java b/src/server/BaseHttpHandler.java
new file mode 100644
index 0000000..b561eac
--- /dev/null
+++ b/src/server/BaseHttpHandler.java
@@ -0,0 +1,54 @@
+package server;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import server.adapters.DurationAdapter;
+import server.adapters.LocalDateTimeAdapter;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+public abstract class BaseHttpHandler implements HttpHandler {
+ protected final Gson gson = new GsonBuilder()
+ .excludeFieldsWithoutExposeAnnotation()
+ .registerTypeAdapter(Duration.class, new DurationAdapter())
+ .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
+ .create();
+
+ @Override
+ public abstract void handle(HttpExchange exchange) throws IOException;
+
+ protected void sendText(HttpExchange exchange, String text, int statusCode) throws IOException {
+ byte[] response = text.getBytes(StandardCharsets.UTF_8);
+ exchange.getResponseHeaders().add("Content-Type", "application/json");
+ exchange.sendResponseHeaders(statusCode, response.length);
+ try (OutputStream os = exchange.getResponseBody()) {
+ os.write(response);
+ }
+ }
+
+ protected void sendNotFound(HttpExchange exchange) throws IOException {
+ sendText(exchange, "Not Found", 404);
+ }
+
+ protected void sendNotFound(HttpExchange exchange, String message) throws IOException {
+ sendText(exchange, message, 404);
+ }
+
+ protected void sendBadRequest(HttpExchange exchange, String message) throws IOException {
+ sendText(exchange, message, 400);
+ }
+
+ protected void sendHasInteractions(HttpExchange exchange) throws IOException {
+ sendText(exchange, "Task overlaps with existing tasks", 406);
+ }
+
+ protected void sendInternalError(HttpExchange exchange) throws IOException {
+ sendText(exchange, "Internal Server Error", 500);
+ }
+}
\ No newline at end of file
diff --git a/src/server/EpicHandler.java b/src/server/EpicHandler.java
new file mode 100644
index 0000000..e952772
--- /dev/null
+++ b/src/server/EpicHandler.java
@@ -0,0 +1,86 @@
+package server;
+
+import com.sun.net.httpserver.HttpExchange;
+import manager.TaskManager;
+import model.Epic;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+public class EpicHandler extends BaseHttpHandler {
+ private final TaskManager taskManager;
+
+ public EpicHandler(TaskManager taskManager) {
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ try {
+ String method = exchange.getRequestMethod();
+ String path = exchange.getRequestURI().getPath();
+ String[] pathParts = path.split("/");
+
+ switch (method) {
+ case "GET":
+ handleGetRequest(exchange, pathParts);
+ break;
+ case "POST":
+ handlePostRequest(exchange);
+ break;
+ case "DELETE":
+ handleDeleteRequest(exchange, pathParts);
+ break;
+ default:
+ sendNotFound(exchange);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ sendInternalError(exchange);
+ }
+ }
+
+ private void handleGetRequest(HttpExchange exchange, String[] pathParts) throws IOException {
+ if (pathParts.length == 2) {
+ ArrayList epics = taskManager.getEpics();
+ sendText(exchange, gson.toJson(epics), 200);
+ } else if (pathParts.length == 3) {
+ int id = Integer.parseInt(pathParts[2]);
+ Epic epic = taskManager.getEpicByID(id);
+ if (epic != null) {
+ sendText(exchange, gson.toJson(epic), 200);
+ } else {
+ sendNotFound(exchange);
+ }
+ }
+ }
+
+ private void handlePostRequest(HttpExchange exchange) throws IOException {
+ String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
+ Epic epic = gson.fromJson(body, Epic.class);
+ if (epic == null || epic.getName() == null || epic.getDescription() == null) {
+ sendBadRequest(exchange, "Invalid epic data");
+ return;
+ }
+
+ if (epic.getId() == 0) {
+ taskManager.addEpic(epic);
+ sendText(exchange, gson.toJson(epic), 201);
+ } else {
+ taskManager.updateEpic(epic);
+ sendText(exchange, gson.toJson(epic), 200);
+ }
+ }
+
+ private void handleDeleteRequest(HttpExchange exchange, String[] pathParts) throws IOException {
+ if (pathParts.length == 2) {
+ taskManager.deleteEpics();
+ sendText(exchange, "All epics deleted", 200);
+ } else if (pathParts.length == 3) {
+ int id = Integer.parseInt(pathParts[2]);
+ taskManager.deleteEpicByID(id);
+ sendText(exchange, "Epic deleted", 200);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/HistoryHandler.java b/src/server/HistoryHandler.java
new file mode 100644
index 0000000..a736dd0
--- /dev/null
+++ b/src/server/HistoryHandler.java
@@ -0,0 +1,31 @@
+package server;
+
+import com.sun.net.httpserver.HttpExchange;
+import manager.TaskManager;
+import model.Task;
+
+import java.io.IOException;
+import java.util.List;
+
+public class HistoryHandler extends BaseHttpHandler {
+ private final TaskManager taskManager;
+
+ public HistoryHandler(TaskManager taskManager) {
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ try {
+ if ("GET".equals(exchange.getRequestMethod())) {
+ List history = taskManager.getHistory();
+ sendText(exchange, gson.toJson(history), 200);
+ } else {
+ sendNotFound(exchange);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ sendInternalError(exchange);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/HttpTaskServer.java b/src/server/HttpTaskServer.java
new file mode 100644
index 0000000..aeaeb8f
--- /dev/null
+++ b/src/server/HttpTaskServer.java
@@ -0,0 +1,34 @@
+package server;
+
+import com.sun.net.httpserver.HttpServer;
+import manager.TaskManager;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class HttpTaskServer {
+ private static final int PORT = 8080;
+ private final HttpServer httpServer;
+ private final TaskManager taskManager;
+
+ public HttpTaskServer(TaskManager taskManager) throws IOException {
+ this.taskManager = taskManager;
+ this.httpServer = HttpServer.create(new InetSocketAddress(PORT), 0);
+
+ this.httpServer.createContext("/tasks", new TaskHandler(taskManager));
+ this.httpServer.createContext("/subtasks", new SubtaskHandler(taskManager));
+ this.httpServer.createContext("/epics", new EpicHandler(taskManager));
+ this.httpServer.createContext("/history", new HistoryHandler(taskManager));
+ this.httpServer.createContext("/prioritized", new PrioritizedHandler(taskManager));
+ }
+
+ public void start() {
+ httpServer.start();
+ System.out.println("HTTP-сервер запущен на порту " + PORT);
+ }
+
+ public void stop() {
+ httpServer.stop(0);
+ System.out.println("HTTP-сервер остановлен");
+ }
+}
\ No newline at end of file
diff --git a/src/server/PrioritizedHandler.java b/src/server/PrioritizedHandler.java
new file mode 100644
index 0000000..9e419d6
--- /dev/null
+++ b/src/server/PrioritizedHandler.java
@@ -0,0 +1,31 @@
+package server;
+
+import com.sun.net.httpserver.HttpExchange;
+import manager.TaskManager;
+import model.Task;
+
+import java.io.IOException;
+import java.util.List;
+
+public class PrioritizedHandler extends BaseHttpHandler {
+ private final TaskManager taskManager;
+
+ public PrioritizedHandler(TaskManager taskManager) {
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ try {
+ if ("GET".equals(exchange.getRequestMethod())) {
+ List prioritizedTasks = taskManager.getPrioritizedTasks();
+ sendText(exchange, gson.toJson(prioritizedTasks), 200);
+ } else {
+ sendNotFound(exchange);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ sendInternalError(exchange);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/SubtaskHandler.java b/src/server/SubtaskHandler.java
new file mode 100644
index 0000000..e36d915
--- /dev/null
+++ b/src/server/SubtaskHandler.java
@@ -0,0 +1,133 @@
+package server;
+
+import com.google.gson.JsonSyntaxException;
+import com.sun.net.httpserver.HttpExchange;
+import manager.TaskManager;
+import model.Subtask;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+public class SubtaskHandler extends BaseHttpHandler {
+ private static final Logger logger = Logger.getLogger(SubtaskHandler.class.getName());
+ private final TaskManager taskManager;
+
+ public SubtaskHandler(TaskManager taskManager) {
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ try {
+ String method = exchange.getRequestMethod();
+ String path = exchange.getRequestURI().getPath();
+ String[] pathParts = path.split("/");
+
+ logger.info("Received " + method + " request for path: " + path);
+
+ switch (method) {
+ case "GET":
+ handleGetRequest(exchange, pathParts);
+ break;
+ case "POST":
+ handlePostRequest(exchange);
+ break;
+ case "DELETE":
+ handleDeleteRequest(exchange, pathParts);
+ break;
+ default:
+ sendNotFound(exchange);
+ }
+ } catch (Exception e) {
+ logger.severe("Error handling request: " + e.getMessage());
+ sendInternalError(exchange);
+ }
+ }
+
+ private void handleGetRequest(HttpExchange exchange, String[] pathParts) throws IOException {
+ if (pathParts.length == 2) {
+ ArrayList subtasks = taskManager.getSubtasks();
+ logger.info("Returning all subtasks: " + subtasks.size());
+ sendText(exchange, gson.toJson(subtasks), 200);
+ } else if (pathParts.length == 3) {
+ try {
+ int id = Integer.parseInt(pathParts[2]);
+ Subtask subtask = taskManager.getSubtaskByID(id);
+ if (subtask != null) {
+ logger.info("Returning subtask with ID: " + id);
+ sendText(exchange, gson.toJson(subtask), 200);
+ } else {
+ logger.warning("Subtask not found with ID: " + id);
+ sendNotFound(exchange, "Subtask not found with ID: " + id);
+ }
+ } catch (NumberFormatException e) {
+ logger.warning("Invalid subtask ID format: " + pathParts[2]);
+ sendBadRequest(exchange, "Invalid subtask ID format");
+ }
+ }
+ }
+
+ private void handlePostRequest(HttpExchange exchange) throws IOException {
+ String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
+ logger.info("Received POST request with body: " + body);
+
+ try {
+ Subtask subtask = gson.fromJson(body, Subtask.class);
+ if (subtask == null) {
+ logger.warning("Invalid subtask data received");
+ sendBadRequest(exchange, "Invalid subtask data");
+ return;
+ }
+
+ if (subtask.getId() == 0) {
+ if (taskManager.getEpicByID(subtask.getEpicID()) == null) {
+ logger.warning("Epic not found with ID: " + subtask.getEpicID());
+ sendNotFound(exchange, "Epic not found with ID: " + subtask.getEpicID());
+ return;
+ }
+ try {
+ taskManager.addSubtask(subtask);
+ logger.info("Created new subtask with ID: " + subtask.getId());
+ sendText(exchange, gson.toJson(subtask), 201);
+ } catch (IllegalArgumentException e) {
+ sendHasInteractions(exchange);
+ }
+ } else {
+ if (taskManager.getSubtaskByID(subtask.getId()) == null) {
+ logger.warning("Subtask not found with ID: " + subtask.getId());
+ sendNotFound(exchange, "Subtask not found with ID: " + subtask.getId());
+ return;
+ }
+ taskManager.updateSubtask(subtask);
+ logger.info("Updated subtask with ID: " + subtask.getId());
+ sendText(exchange, gson.toJson(subtask), 200);
+ }
+ } catch (JsonSyntaxException e) {
+ logger.severe("Error parsing subtask data: " + e.getMessage());
+ sendBadRequest(exchange, "Invalid JSON data");
+ } catch (Exception e) {
+ logger.severe("Error handling request: " + e.getMessage());
+ sendInternalError(exchange);
+ }
+ }
+
+ private void handleDeleteRequest(HttpExchange exchange, String[] pathParts) throws IOException {
+ if (pathParts.length == 2) {
+ taskManager.deleteSubtasks();
+ logger.info("All subtasks deleted");
+ sendText(exchange, "All subtasks deleted", 200);
+ } else if (pathParts.length == 3) {
+ try {
+ int id = Integer.parseInt(pathParts[2]);
+ taskManager.deleteSubtaskByID(id);
+ logger.info("Deleted subtask with ID: " + id);
+ sendText(exchange, "Subtask deleted", 200);
+ } catch (NumberFormatException e) {
+ logger.warning("Invalid subtask ID format: " + pathParts[2]);
+ sendBadRequest(exchange, "Invalid subtask ID format");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/TaskHandler.java b/src/server/TaskHandler.java
new file mode 100644
index 0000000..94522c7
--- /dev/null
+++ b/src/server/TaskHandler.java
@@ -0,0 +1,85 @@
+package server;
+
+import com.sun.net.httpserver.HttpExchange;
+import manager.TaskManager;
+import model.Task;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+public class TaskHandler extends BaseHttpHandler {
+ private final TaskManager taskManager;
+
+ public TaskHandler(TaskManager taskManager) {
+ this.taskManager = taskManager;
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ try {
+ String method = exchange.getRequestMethod();
+ String path = exchange.getRequestURI().getPath();
+ String[] pathParts = path.split("/");
+
+ switch (method) {
+ case "GET":
+ handleGetRequest(exchange, pathParts);
+ break;
+ case "POST":
+ handlePostRequest(exchange);
+ break;
+ case "DELETE":
+ handleDeleteRequest(exchange, pathParts);
+ break;
+ default:
+ sendNotFound(exchange);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ sendInternalError(exchange);
+ }
+ }
+
+ private void handleGetRequest(HttpExchange exchange, String[] pathParts) throws IOException {
+ if (pathParts.length == 2) {
+ ArrayList tasks = taskManager.getTasks();
+ sendText(exchange, gson.toJson(tasks), 200);
+ } else if (pathParts.length == 3) {
+ int id = Integer.parseInt(pathParts[2]);
+ Task task = taskManager.getTaskByID(id);
+ if (task != null) {
+ sendText(exchange, gson.toJson(task), 200);
+ } else {
+ sendNotFound(exchange);
+ }
+ }
+ }
+
+ private void handlePostRequest(HttpExchange exchange) throws IOException {
+ String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
+ Task task = gson.fromJson(body, Task.class);
+ if (task.getId() == 0) {
+ try {
+ taskManager.addTask(task);
+ sendText(exchange, gson.toJson(task), 201);
+ } catch (IllegalArgumentException e) {
+ sendHasInteractions(exchange);
+ }
+ } else {
+ taskManager.updateTask(task);
+ sendText(exchange, gson.toJson(task), 200);
+ }
+ }
+
+ private void handleDeleteRequest(HttpExchange exchange, String[] pathParts) throws IOException {
+ if (pathParts.length == 2) {
+ taskManager.deleteTasks();
+ sendText(exchange, "All tasks deleted", 200);
+ } else if (pathParts.length == 3) {
+ int id = Integer.parseInt(pathParts[2]);
+ taskManager.deleteTaskByID(id);
+ sendText(exchange, "Task deleted", 200);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/adapters/DurationAdapter.java b/src/server/adapters/DurationAdapter.java
new file mode 100644
index 0000000..ba85507
--- /dev/null
+++ b/src/server/adapters/DurationAdapter.java
@@ -0,0 +1,17 @@
+package server.adapters;
+
+import com.google.gson.*;
+import java.lang.reflect.Type;
+import java.time.Duration;
+
+public class DurationAdapter implements JsonSerializer, JsonDeserializer {
+ @Override
+ public JsonElement serialize(Duration duration, Type type, JsonSerializationContext context) {
+ return new JsonPrimitive(duration.toSeconds());
+ }
+
+ @Override
+ public Duration deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
+ return Duration.ofSeconds(json.getAsLong());
+ }
+}
\ No newline at end of file
diff --git a/src/server/adapters/LocalDateTimeAdapter.java b/src/server/adapters/LocalDateTimeAdapter.java
new file mode 100644
index 0000000..f2404c1
--- /dev/null
+++ b/src/server/adapters/LocalDateTimeAdapter.java
@@ -0,0 +1,20 @@
+package server.adapters;
+import com.google.gson.*;
+import java.lang.reflect.Type;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class LocalDateTimeAdapter implements JsonSerializer, JsonDeserializer {
+ private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+
+ @Override
+ public JsonElement serialize(LocalDateTime dateTime, Type type, JsonSerializationContext context) {
+ return new JsonPrimitive(formatter.format(dateTime));
+ }
+
+ @Override
+ public LocalDateTime deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
+ return LocalDateTime.parse(json.getAsString(), formatter);
+ }
+}
\ No newline at end of file
diff --git a/src/tasktype/TaskType.java b/src/tasktype/TaskType.java
new file mode 100644
index 0000000..1405e80
--- /dev/null
+++ b/src/tasktype/TaskType.java
@@ -0,0 +1,7 @@
+package tasktype;
+
+public enum TaskType {
+ TASK,
+ EPIC,
+ SUBTASK
+}
\ No newline at end of file
diff --git a/test/http/HttpTaskServerTest.java b/test/http/HttpTaskServerTest.java
new file mode 100644
index 0000000..70a9d1a
--- /dev/null
+++ b/test/http/HttpTaskServerTest.java
@@ -0,0 +1,327 @@
+package http;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import manager.InMemoryTaskManager;
+import manager.TaskManager;
+import model.Epic;
+import model.Subtask;
+import model.Task;
+import model.Status;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import server.HttpTaskServer;
+import server.adapters.DurationAdapter;
+import server.adapters.LocalDateTimeAdapter;
+
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class HttpTaskServerTest {
+
+ private HttpTaskServer taskServer;
+ private TaskManager manager;
+ private Gson gson;
+
+
+ @BeforeEach
+ public void setUp() throws IOException {
+
+ manager = new InMemoryTaskManager();
+
+ manager.deleteTasks();
+ manager.deleteEpics();
+ manager.deleteSubtasks();
+
+ taskServer = new HttpTaskServer(manager);
+ taskServer.start();
+
+ gson = new GsonBuilder()
+ .registerTypeAdapter(Duration.class, new DurationAdapter())
+ .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
+ .create();
+ }
+
+ @AfterEach
+ public void tearDown() {
+
+ taskServer.stop();
+ }
+
+ @Test
+ public void testAddTask() throws IOException, InterruptedException {
+ Task task = new Task("Test Task", "Test Description");
+ task.setStatus(Status.NEW);
+ task.setDuration(Duration.ofMinutes(30));
+ task.setStartTime(LocalDateTime.now());
+ String taskJson = gson.toJson(task);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/tasks"))
+ .POST(HttpRequest.BodyPublishers.ofString(taskJson))
+ .header("Content-Type", "application/json")
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(201, response.statusCode(), "Ожидался статус 201 (Created)");
+
+ List tasks = manager.getTasks();
+ assertNotNull(tasks, "Список задач не должен быть null");
+ assertEquals(1, tasks.size(), "Ожидалась одна задача в списке");
+ assertEquals("Test Task", tasks.get(0).getName(), "Название задачи не совпадает");
+ }
+
+ @Test
+ public void testGetTask() throws IOException, InterruptedException {
+ Task task = new Task("Test Task", "Test Description");
+ task.setStatus(Status.NEW);
+ task.setDuration(Duration.ofMinutes(30));
+ task.setStartTime(LocalDateTime.now());
+ manager.addTask(task);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/tasks/" + task.getId()))
+ .GET()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ Task returnedTask = gson.fromJson(response.body(), Task.class);
+ assertNotNull(returnedTask, "Задача не должна быть null");
+ assertEquals(task.getName(), returnedTask.getName(), "Название задачи не совпадает");
+ assertEquals(task.getDuration(), returnedTask.getDuration(), "Длительность задачи не совпадает");
+ assertEquals(task.getStartTime(), returnedTask.getStartTime(), "Время начала задачи не совпадает");
+ }
+
+ @Test
+ public void testDeleteTask() throws IOException, InterruptedException {
+ Task task = new Task(1, "Test Task", "Test Description");
+ task.setStatus(Status.NEW);
+ manager.addTask(task);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/tasks/" + task.getId()))
+ .DELETE()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ assertNull(manager.getTaskByID(task.getId()), "Задача должна быть удалена");
+ }
+
+ @Test
+ public void testAddEpic() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ String epicJson = gson.toJson(epic);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/epics"))
+ .POST(HttpRequest.BodyPublishers.ofString(epicJson))
+ .header("Content-Type", "application/json")
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(201, response.statusCode(), "Ожидался статус 201 (Created)");
+
+ List epics = manager.getEpics();
+ assertNotNull(epics, "Список эпиков не должен быть null");
+ assertEquals(1, epics.size(), "Ожидался один эпик в списке");
+ assertEquals("Test Epic", epics.get(0).getName(), "Название эпика не совпадает");
+ }
+
+ @Test
+ public void testGetEpic() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ manager.addEpic(epic);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/epics/" + epic.getId()))
+ .GET()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ Epic returnedEpic = gson.fromJson(response.body(), Epic.class);
+ assertNotNull(returnedEpic, "Эпик не должен быть null");
+ assertEquals(epic.getName(), returnedEpic.getName(), "Название эпика не совпадает");
+ }
+
+ @Test
+ public void testDeleteEpic() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ manager.addEpic(epic);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/epics/" + epic.getId()))
+ .DELETE()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ assertNull(manager.getEpicByID(epic.getId()), "Эпик должен быть удален");
+ }
+
+ @Test
+ public void testAddSubtask() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ manager.addEpic(epic);
+
+ Subtask subtask = new Subtask("Test Subtask", "Test Description", epic.getId());
+ subtask.setStatus(Status.NEW);
+ subtask.setDuration(Duration.ofMinutes(30));
+ subtask.setStartTime(LocalDateTime.now());
+
+ String subtaskJson = gson.toJson(subtask);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/subtasks"))
+ .POST(HttpRequest.BodyPublishers.ofString(subtaskJson))
+ .header("Content-Type", "application/json")
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(201, response.statusCode(), "Ожидался статус 201 (Created)");
+
+ List subtasks = manager.getSubtasks();
+ assertNotNull(subtasks, "Список подзадач не должен быть null");
+ assertEquals(1, subtasks.size(), "Ожидалась одна подзадача в списке");
+ assertEquals("Test Subtask", subtasks.get(0).getName(), "Название подзадачи не совпадает");
+ }
+
+ @Test
+ public void testGetSubtask() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ manager.addEpic(epic);
+
+ Subtask subtask = new Subtask("Test Subtask", "Test Description", epic.getId());
+ subtask.setStatus(Status.NEW);
+ subtask.setDuration(Duration.ofMinutes(30));
+ subtask.setStartTime(LocalDateTime.now());
+ manager.addSubtask(subtask);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/subtasks/" + subtask.getId()))
+ .GET()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ Subtask returnedSubtask = gson.fromJson(response.body(), Subtask.class);
+ assertNotNull(returnedSubtask, "Подзадача не должна быть null");
+ assertEquals(subtask.getName(), returnedSubtask.getName(), "Название подзадачи не совпадает");
+ assertEquals(subtask.getDuration(), returnedSubtask.getDuration(), "Длительность подзадачи не совпадает");
+ assertEquals(subtask.getStartTime(), returnedSubtask.getStartTime(), "Время начала подзадачи не совпадает");
+ }
+
+ @Test
+ public void testDeleteSubtask() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ manager.addEpic(epic);
+
+ Subtask subtask = new Subtask("Test Subtask", "Test Description", epic.getId());
+ subtask.setStatus(Status.NEW);
+ manager.addSubtask(subtask);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/subtasks/" + subtask.getId()))
+ .DELETE()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ assertNull(manager.getSubtaskByID(subtask.getId()), "Подзадача должна быть удалена");
+ }
+
+ @Test
+ public void testUpdateSubtask() throws IOException, InterruptedException {
+ Epic epic = new Epic("Test Epic", "Test Description");
+ manager.addEpic(epic);
+
+ Subtask subtask = new Subtask("Test Subtask", "Test Description", epic.getId());
+ subtask.setStatus(Status.NEW);
+ subtask.setDuration(Duration.ofMinutes(30));
+ subtask.setStartTime(LocalDateTime.now());
+ manager.addSubtask(subtask);
+
+ subtask.setDescription("Updated Description");
+ String subtaskJson = gson.toJson(subtask);
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/subtasks"))
+ .POST(HttpRequest.BodyPublishers.ofString(subtaskJson))
+ .header("Content-Type", "application/json")
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ Subtask updatedSubtask = manager.getSubtaskByID(subtask.getId());
+ assertNotNull(updatedSubtask, "Подзадача не должна быть null");
+ assertEquals("Updated Description", updatedSubtask.getDescription(), "Описание подзадачи не обновлено");
+ }
+
+ @Test
+ public void testGetHistory() throws IOException, InterruptedException {
+ Task task1 = new Task("Task 1", "Description 1");
+ Task task2 = new Task("Task 2", "Description 2");
+ manager.addTask(task1);
+ manager.addTask(task2);
+
+ manager.getTaskByID(task1.getId());
+ manager.getTaskByID(task2.getId());
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:8080/history"))
+ .GET()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode(), "Ожидался статус 200 (OK)");
+
+ List history = gson.fromJson(response.body(), new TypeToken>() {
+ }.getType());
+ assertNotNull(history, "История не должна быть null");
+ assertEquals(2, history.size(), "Ожидалось две задачи в истории");
+ assertEquals(task1.getName(), history.get(0).getName(), "Название первой задачи не совпадает");
+ assertEquals(task2.getName(), history.get(1).getName(), "Название второй задачи не совпадает");
+ }
+}
diff --git a/test/manager/FileBackedTaskManagerTest.java b/test/manager/FileBackedTaskManagerTest.java
new file mode 100644
index 0000000..e9c910e
--- /dev/null
+++ b/test/manager/FileBackedTaskManagerTest.java
@@ -0,0 +1,83 @@
+package manager;
+
+import model.Epic;
+import model.Subtask;
+import model.Task;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class FileBackedTaskManagerTest extends TaskManagerTest {
+ private File file;
+
+ @BeforeEach
+ public void setUp() {
+ file = new File("test_tasks.csv");
+ taskManager = new FileBackedTaskManager(file);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+
+ @Override
+ protected FileBackedTaskManager createTaskManager() {
+ return new FileBackedTaskManager(file);
+ }
+
+ @Test
+ public void testAddTaskAndCheckFileContent() throws IOException {
+ Task task = new Task("Test Task", "Description", Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addTask(task);
+
+ List lines = Files.readAllLines(file.toPath());
+ assertEquals(2, lines.size());
+ assertTrue(lines.get(1).contains("Test Task"));
+ }
+
+ @Test
+ public void testDeleteTaskAndCheckFileContent() throws IOException {
+ Task task = new Task("Test Task", "Description", Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addTask(task);
+ taskManager.deleteTaskByID(task.getId());
+
+ List lines = Files.readAllLines(file.toPath());
+ assertEquals(1, lines.size());
+ }
+
+ @Test
+ public void testUpdateTaskAndCheckFileContent() throws IOException {
+ LocalDateTime now = LocalDateTime.now();
+
+ Task task = new Task("Test Task", "Description", Duration.ofMinutes(30), now);
+ taskManager.addTask(task);
+
+ task.setDescription("Updated Description");
+ taskManager.updateTask(task);
+
+ List lines = Files.readAllLines(file.toPath());
+ assertTrue(lines.get(1).contains("Updated Description"));
+ }
+
+ @Test
+ public void testLoadFromFileWithEmptyFile() throws IOException {
+ file.createNewFile();
+
+ FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(file);
+ assertTrue(loadedManager.getTasks().isEmpty());
+ assertTrue(loadedManager.getEpics().isEmpty());
+ assertTrue(loadedManager.getSubtasks().isEmpty());
+ }
+}
\ No newline at end of file
diff --git a/test/manager/InMemoryHistoryManagerTest.java b/test/manager/InMemoryHistoryManagerTest.java
new file mode 100644
index 0000000..20056d2
--- /dev/null
+++ b/test/manager/InMemoryHistoryManagerTest.java
@@ -0,0 +1,93 @@
+package manager;
+
+import model.Epic;
+import model.Status;
+import model.Task;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class InMemoryHistoryManagerTest {
+
+ @Test
+ public void testEmptyHistory() {
+ InMemoryHistoryManager historyManager = new InMemoryHistoryManager();
+ List history = historyManager.getHistory();
+ assertEquals(0, history.size(), "History should be empty initially.");
+ }
+
+ @Test
+ public void testDuplicateTask() {
+ InMemoryHistoryManager historyManager = new InMemoryHistoryManager();
+ Task task = new Task(1, "Task1", "Description1", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+
+
+ historyManager.add(task);
+ historyManager.add(task);
+ List history = historyManager.getHistory();
+
+ assertEquals(1, history.size(), "History should contain only one instance of the task.");
+ assertEquals(task, history.get(0), "The task in history should be the same as the added task.");
+ }
+
+ @Test
+ public void testRemoveFromBeginning() {
+ InMemoryHistoryManager historyManager = new InMemoryHistoryManager();
+ Task task1 = new Task(1, "Task1", "Description1", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+ Task task2 = new Task(2, "Task2", "Description2", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+ Task task3 = new Task(3, "Task3", "Description3", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+
+ historyManager.add(task1);
+ historyManager.add(task2);
+ historyManager.add(task3);
+
+ historyManager.remove(1);
+ List history = historyManager.getHistory();
+
+ assertEquals(2, history.size(), "History should contain two tasks after removing the first one.");
+ assertEquals(task2, history.get(0), "The first task in history should now be Task2.");
+ assertEquals(task3, history.get(1), "The second task in history should still be Task3.");
+ }
+
+ @Test
+ public void testRemoveFromMiddle() {
+ InMemoryHistoryManager historyManager = new InMemoryHistoryManager();
+ Task task1 = new Task(1, "Task1", "Description1", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+ Task task2 = new Task(2, "Task2", "Description2", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+ Task task3 = new Task(3, "Task3", "Description3", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+
+ historyManager.add(task1);
+ historyManager.add(task2);
+ historyManager.add(task3);
+
+ historyManager.remove(2);
+ List history = historyManager.getHistory();
+
+ assertEquals(2, history.size(), "History should contain two tasks after removing the second one.");
+ assertEquals(task1, history.get(0), "The first task in history should still be Task1.");
+ assertEquals(task3, history.get(1), "The second task in history should now be Task3.");
+ }
+
+ @Test
+ public void testRemoveFromEnd() {
+ InMemoryHistoryManager historyManager = new InMemoryHistoryManager();
+ Task task1 = new Task(1, "Task1", "Description1", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+ Task task2 = new Task(2, "Task2", "Description2", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+ Task task3 = new Task(3, "Task3", "Description3", Status.NEW, Duration.ofMinutes(30), LocalDateTime.now());
+
+ historyManager.add(task1);
+ historyManager.add(task2);
+ historyManager.add(task3);
+
+ historyManager.remove(3);
+ List history = historyManager.getHistory();
+
+ assertEquals(2, history.size(), "History should contain two tasks after removing the last one.");
+ assertEquals(task1, history.get(0), "The first task in history should still be Task1.");
+ assertEquals(task2, history.get(1), "The second task in history should now be Task2.");
+ }
+}
\ No newline at end of file
diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java
new file mode 100644
index 0000000..a633fe9
--- /dev/null
+++ b/test/manager/InMemoryTaskManagerTest.java
@@ -0,0 +1,67 @@
+package manager;
+
+
+import model.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class InMemoryTaskManagerTest {
+ private InMemoryTaskManager taskManager;
+
+ @BeforeEach
+ void setUp() {
+ taskManager = new InMemoryTaskManager();
+ }
+
+ @Test
+ void testAddTask() {
+ Task task = new Task("Test Task", "This is a test task");
+ Task addedTask = taskManager.addTask(task);
+ assertNotNull(addedTask);
+ assertEquals("Test Task", addedTask.getName());
+ assertEquals(1, addedTask.getId());
+ }
+
+ @Test
+ void testUpdateTask() {
+ Task task = new Task("Test Task", "This is a test task");
+ taskManager.addTask(task);
+ task.setName("Updated Task");
+ Task updatedTask = taskManager.updateTask(task);
+ assertNotNull(updatedTask);
+ assertEquals("Updated Task", updatedTask.getName());
+ }
+
+ @Test
+ void testGetTaskByID() {
+ Task task = new Task("Test Task", "This is a test task");
+ taskManager.addTask(task);
+ Task retrievedTask = taskManager.getTaskByID(task.getId());
+ assertNotNull(retrievedTask);
+ assertEquals(task.getId(), retrievedTask.getId());
+ }
+
+ @Test
+ void testDeleteTaskByID() {
+ Task task = new Task("Test Task", "This is a test task");
+ taskManager.addTask(task);
+ taskManager.deleteTaskByID(task.getId());
+ assertNull(taskManager.getTaskByID(task.getId()));
+ }
+
+ @Test
+ void testDeleteTasks() {
+ Task task1 = new Task("Task 1", "Description 1");
+ Task task2 = new Task("Task 2", "Description 2");
+ taskManager.addTask(task1);
+ taskManager.addTask(task2);
+ taskManager.deleteTasks();
+ assertTrue(taskManager.getTasks().isEmpty());
+ }
+
+
+}
\ No newline at end of file
diff --git a/Test/manager/ManagersTest.java b/test/manager/ManagersTest.java
similarity index 100%
rename from Test/manager/ManagersTest.java
rename to test/manager/ManagersTest.java
diff --git a/test/manager/TaskManagerTest.java b/test/manager/TaskManagerTest.java
new file mode 100644
index 0000000..9f2b477
--- /dev/null
+++ b/test/manager/TaskManagerTest.java
@@ -0,0 +1,138 @@
+package manager;
+
+import model.Epic;
+import model.Status;
+import model.Subtask;
+import model.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public abstract class TaskManagerTest {
+ protected T taskManager;
+
+ @BeforeEach
+ public void setUp() {
+ taskManager = createTaskManager();
+ }
+
+ protected abstract T createTaskManager();
+
+ @Test
+ public void testAddTask() {
+ Task task = new Task("Test Task", "Description", Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addTask(task);
+ assertEquals(task, taskManager.getTaskByID(task.getId()));
+ }
+
+ @Test
+ public void testAddEpic() {
+ Epic epic = new Epic(10, "Test Epic", "Epic Description");
+ taskManager.addEpic(epic);
+ assertEquals(epic, taskManager.getEpicByID(epic.getId()));
+ }
+
+ @Test
+ public void testAddSubtask() {
+ Epic epic = new Epic(10, "Test Epic", "Epic Description");
+ taskManager.addEpic(epic);
+ Subtask subtask = new Subtask("Test Subtask", "Subtask Description", epic.getId(), Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addSubtask(subtask);
+ assertEquals(subtask, taskManager.getSubtaskByID(subtask.getId()));
+ }
+
+ @Test
+ public void testEpicStatusCalculation() {
+ Epic epic = new Epic(10, "Test Epic", "Epic Description");
+ taskManager.addEpic(epic);
+ Subtask subtask1 = new Subtask("Subtask 1", "Description", epic.getId(), Duration.ofMinutes(30), LocalDateTime.now());
+ Subtask subtask2 = new Subtask("Subtask 2", "Description", epic.getId(), Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addSubtask(subtask1);
+ taskManager.addSubtask(subtask2);
+
+ assertEquals(Status.NEW, epic.getStatus());
+
+ subtask1.setStatus(Status.DONE);
+ taskManager.updateSubtask(subtask1);
+
+ assertEquals(Status.IN_PROGRESS, epic.getStatus());
+
+ subtask2.setStatus(Status.DONE);
+ taskManager.updateSubtask(subtask2);
+
+ assertEquals(Status.DONE, epic.getStatus());
+ }
+
+ @Test
+ public void testGetPrioritizedTasks() {
+ LocalDateTime now = LocalDateTime.now();
+
+ Task task1 = new Task("Task 1", "Description 1", Duration.ofMinutes(30), now.plusMinutes(10));
+ Task task2 = new Task("Task 2", "Description 2", Duration.ofMinutes(30), now.plusMinutes(50));
+ Task task3 = new Task("Task 3", "Description 3", Duration.ofMinutes(30), now.plusMinutes(90));
+
+ taskManager.addTask(task1);
+ taskManager.addTask(task2);
+ taskManager.addTask(task3);
+
+ List prioritizedTasks = taskManager.getPrioritizedTasks();
+
+ assertEquals(3, prioritizedTasks.size());
+ assertEquals(task1, prioritizedTasks.get(0));
+ assertEquals(task2, prioritizedTasks.get(1));
+ assertEquals(task3, prioritizedTasks.get(2));
+ }
+
+ @Test
+ public void testDeleteTaskByID() {
+ Task task = new Task("Test Task", "Description", Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addTask(task);
+ taskManager.deleteTaskByID(task.getId());
+ assertNull(taskManager.getTaskByID(task.getId()));
+ }
+
+ @Test
+ public void testDeleteEpicWithSubtasks() {
+ Epic epic = new Epic(10, "Test Epic", "Epic Description");
+ taskManager.addEpic(epic);
+ Subtask subtask = new Subtask("Test Subtask", "Subtask Description", epic.getId(), Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addSubtask(subtask);
+ taskManager.deleteEpicByID(epic.getId());
+ assertNull(taskManager.getEpicByID(epic.getId()));
+ assertNull(taskManager.getSubtaskByID(subtask.getId()));
+ }
+
+ @Test
+ public void testUpdateTask() {
+ Task task = new Task("Test Task", "Description", Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addTask(task);
+ task.setDescription("Updated Description");
+ taskManager.updateTask(task);
+ assertEquals("Updated Description", taskManager.getTaskByID(task.getId()).getDescription());
+ }
+
+ @Test
+ public void testUpdateEpic() {
+ Epic epic = new Epic(10, "Test Epic", "Epic Description");
+ taskManager.addEpic(epic);
+ epic.setDescription("Updated Epic Description");
+ taskManager.updateEpic(epic);
+ assertEquals("Updated Epic Description", taskManager.getEpicByID(epic.getId()).getDescription());
+ }
+
+ @Test
+ public void testUpdateSubtask() {
+ Epic epic = new Epic(10, "Test Epic", "Epic Description");
+ taskManager.addEpic(epic);
+ Subtask subtask = new Subtask("Test Subtask", "Subtask Description", epic.getId(), Duration.ofMinutes(30), LocalDateTime.now());
+ taskManager.addSubtask(subtask);
+ subtask.setDescription("Updated Subtask Description");
+ taskManager.updateSubtask(subtask);
+ assertEquals("Updated Subtask Description", taskManager.getSubtaskByID(subtask.getId()).getDescription());
+ }
+}
\ No newline at end of file
diff --git a/Test/model/EpicTest.java b/test/model/EpicTest.java
similarity index 100%
rename from Test/model/EpicTest.java
rename to test/model/EpicTest.java
diff --git a/test/model/SubtaskTest.java b/test/model/SubtaskTest.java
new file mode 100644
index 0000000..3a0fb96
--- /dev/null
+++ b/test/model/SubtaskTest.java
@@ -0,0 +1,31 @@
+package model;
+
+import org.junit.jupiter.api.Test;
+
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SubtaskTest {
+
+ @Test
+ void testEquals() {
+ Subtask subtask1 = new Subtask("Test Subtask", "This is a test subtask", 1);
+ Subtask subtask2 = new Subtask("Another Subtask", "This is another test subtask", 1);
+ Subtask subtask3 = new Subtask("Different Subtask", "This is a different test subtask", 2);
+
+ assertEquals(subtask1, subtask2);
+ assertNotEquals(subtask1, subtask3);
+ }
+
+ @Test
+ void testHashCode() {
+ Subtask subtask1 = new Subtask("Test Subtask", "This is a test subtask", 1);
+ Subtask subtask2 = new Subtask("Another Subtask", "This is another test subtask", 1);
+ Subtask subtask3 = new Subtask("Different Subtask", "This is a different test subtask", 2);
+
+ assertEquals(subtask1.hashCode(), subtask2.hashCode());
+ assertNotEquals(subtask1.hashCode(), subtask3.hashCode());
+ }
+
+
+}
\ No newline at end of file
diff --git a/Test/model/TaskTest.java b/test/model/TaskTest.java
similarity index 100%
rename from Test/model/TaskTest.java
rename to test/model/TaskTest.java