diff --git a/.idea/libraries/google_api_client_gson.xml b/.idea/libraries/google_api_client_gson.xml new file mode 100644 index 0000000..9643be3 --- /dev/null +++ b/.idea/libraries/google_api_client_gson.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java-kanban.iml b/java-kanban.iml index 265bb5c..1f0c1fe 100644 --- a/java-kanban.iml +++ b/java-kanban.iml @@ -12,5 +12,6 @@ + \ No newline at end of file diff --git a/resources/data.csv b/resources/data.csv index 377ec10..321b3ae 100644 --- a/resources/data.csv +++ b/resources/data.csv @@ -1,6 +1,6 @@ id,type,name,status,description,epic, startTime, duration -5,TASK,Name,NEW,Desc,,2025-07-22T21:18:17.670284500,30 -9,TASK,Непересекающаяся,NEW,Описание,,2025-07-24T01:18:17.673142600,60 -6,EPIC,Отпуск,NEW,Организовать отпуск,,2025-07-23T20:18:17.673142600, -7,SUBTASK,Путёвка,NEW,Выбрать путёвку,6,2025-07-23T20:18:17.673142600,120 -8,SUBTASK,Билеты,NEW,Купить билеты,6,2025-07-23T23:18:17.673142600,60 +5,TASK,Name,NEW,Desc,,2025-07-31T17:44:00.858235600,30 +9,TASK,Непересекающаяся,NEW,Описание,,2025-08-01T21:44:00.861707600,60 +6,EPIC,Отпуск,NEW,Организовать отпуск,,2025-08-01T16:44:00.861707600, +7,SUBTASK,Путёвка,NEW,Выбрать путёвку,6,2025-08-01T16:44:00.861707600,120 +8,SUBTASK,Билеты,NEW,Купить билеты,6,2025-08-01T19:44:00.861707600,60 diff --git a/src/manager/http/BaseHttpHandler.java b/src/manager/http/BaseHttpHandler.java new file mode 100644 index 0000000..bc27775 --- /dev/null +++ b/src/manager/http/BaseHttpHandler.java @@ -0,0 +1,50 @@ +package manager.http; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.LocalDateTime; + +public abstract class BaseHttpHandler implements HttpHandler { + protected static final Gson gson = new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, new TimeAdapters.LocalDateTimeAdapter()) + .registerTypeAdapter(Duration.class, new TimeAdapters.DurationAdapter()) + .create(); + + protected String readRequest(HttpExchange exchange) throws IOException { + try (InputStream input = exchange.getRequestBody()) { + return new String(input.readAllBytes(), StandardCharsets.UTF_8); + } + } + + protected void sendResponse(HttpExchange exchange, String response, int statusCode) throws IOException { + exchange.getResponseHeaders().set("Content-Type", "application/json;charset=utf-8"); + byte[] resp = response.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(statusCode, resp.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(resp); + } + } + + protected void sendText(HttpExchange exchange, String text) throws IOException { + sendResponse(exchange, text, 200); + } + + protected void sendNotFound(HttpExchange exchange) throws IOException { + sendResponse(exchange, "Задача не найдена", 404); + } + + protected void sendHasOverlaps(HttpExchange exchange, String message) throws IOException { + sendResponse(exchange, message, 406); + } + + protected void sendBadRequest(HttpExchange exchange, String message) throws IOException { + sendResponse(exchange, message, 400); + } +} diff --git a/src/manager/http/EpicsHandler.java b/src/manager/http/EpicsHandler.java new file mode 100644 index 0000000..66fa1f5 --- /dev/null +++ b/src/manager/http/EpicsHandler.java @@ -0,0 +1,114 @@ +package manager.http; + +import com.google.gson.JsonSyntaxException; +import com.sun.net.httpserver.HttpExchange; +import manager.TaskManager; +import tasks.Epic; +import java.io.IOException; + +public class EpicsHandler extends BaseHttpHandler { + private final TaskManager taskManager; + + public EpicsHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + + switch (method) { + case "GET": + if (path.equals("/epics")) { + handleGetAllEpics(exchange); + } else if (path.startsWith("/epics/")) { + if (path.endsWith("/subtasks")) { + handleGetEpicSubtasks(exchange, path); + } else { + handleEpicWithId(exchange, path, "GET"); + } + } else { + sendBadRequest(exchange, "Некорректный путь"); + } + break; + case "POST": + handleCreateOrUpdateEpic(exchange); + break; + case "DELETE": + if (path.startsWith("/epics/")) { + handleEpicWithId(exchange, path, "DELETE"); + } else { + sendBadRequest(exchange, "Некорректный путь"); + } + break; + default: + sendBadRequest(exchange, "Неподдерживаемый метод"); + } + } catch (Exception e) { + sendResponse(exchange, "Ошибка сервера", 500); + } + } + + private void handleEpicWithId(HttpExchange exchange, String path, String method) throws IOException { + String idStr = path.substring("/epics/".length()); + try { + int epicId = Integer.parseInt(idStr); + if (method.equals("GET")) { + Epic epic = taskManager.getEpic(epicId); + if (epic != null) { + sendText(exchange, gson.toJson(epic)); + } else { + sendNotFound(exchange); + } + } else if (method.equals("DELETE")) { + Epic epic = taskManager.deleteEpic(epicId); + if (epic != null) { + sendText(exchange, "Эпик удален"); + } else { + sendNotFound(exchange); + } + } + } catch (NumberFormatException e) { + sendBadRequest(exchange, "Некорректный ID эпика"); + } + } + + private void handleGetEpicSubtasks(HttpExchange exchange, String path) throws IOException { + String idStr = path.substring("/epics/".length(), path.length() - "/subtasks".length()); + try { + int epicId = Integer.parseInt(idStr); + Epic epic = taskManager.getEpic(epicId); + if (epic != null) { + String response = gson.toJson(taskManager.getEpicSubtasks(epicId)); + sendText(exchange, response); + } else { + sendNotFound(exchange); + } + } catch (NumberFormatException e) { + sendBadRequest(exchange, "Некорректный ID эпика"); + } + } + + private void handleGetAllEpics(HttpExchange exchange) throws IOException { + String response = gson.toJson(taskManager.getEpics()); + sendText(exchange, response); + } + + private void handleCreateOrUpdateEpic(HttpExchange exchange) throws IOException { + String body = readRequest(exchange); + try { + Epic epic = gson.fromJson(body, Epic.class); + if (epic.getId() == null) { + taskManager.createEpic(epic); + sendResponse(exchange, "Эпик создан", 201); + } else { + taskManager.updateEpic(epic); + sendText(exchange, "Эпик обновлен"); + } + } catch (JsonSyntaxException e) { + sendBadRequest(exchange, "Неверный формат JSON"); + } + } +} diff --git a/src/manager/http/HistoryHandler.java b/src/manager/http/HistoryHandler.java new file mode 100644 index 0000000..6194f4c --- /dev/null +++ b/src/manager/http/HistoryHandler.java @@ -0,0 +1,31 @@ +package manager.http; + +import com.sun.net.httpserver.HttpExchange; +import manager.TaskManager; +import java.io.IOException; + +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())) { + handleGetHistory(exchange); + } else { + sendBadRequest(exchange, "Неподдерживаемый метод"); + } + } catch (Exception e) { + sendResponse(exchange, "Ошибка сервера", 500); + } + } + + private void handleGetHistory(HttpExchange exchange) throws IOException { + String response = gson.toJson(taskManager.getHistory()); + sendText(exchange, response); + } +} diff --git a/src/manager/http/HttpTaskServer.java b/src/manager/http/HttpTaskServer.java new file mode 100644 index 0000000..b04eb54 --- /dev/null +++ b/src/manager/http/HttpTaskServer.java @@ -0,0 +1,43 @@ +package manager.http; + +import com.sun.net.httpserver.HttpServer; +import manager.Managers; +import manager.TaskManager; +import java.io.IOException; +import java.net.InetSocketAddress; + +public class HttpTaskServer { + private final HttpServer server; + private final TaskManager taskManager; + private static final int PORT = 8080; + + public HttpTaskServer() throws IOException { + this.taskManager = Managers.getDefault(); + this.server = HttpServer.create(new InetSocketAddress(PORT), 0); + + server.createContext("/tasks", new TasksHandler(taskManager)); + server.createContext("/subtasks", new SubtasksHandler(taskManager)); + server.createContext("/epics", new EpicsHandler(taskManager)); + server.createContext("/history", new HistoryHandler(taskManager)); + server.createContext("/prioritized", new PrioritizedHandler(taskManager)); + } + + public TaskManager getTaskManager() { + return taskManager; + } + + public void start() { + server.start(); + System.out.println("Сервер запущен на порту " + PORT); + } + + public void stop() { + server.stop(0); + System.out.println("Сервер остановлен"); + } + + public static void main(String[] args) throws IOException { + HttpTaskServer server = new HttpTaskServer(); + server.start(); + } +} diff --git a/src/manager/http/PrioritizedHandler.java b/src/manager/http/PrioritizedHandler.java new file mode 100644 index 0000000..bce6277 --- /dev/null +++ b/src/manager/http/PrioritizedHandler.java @@ -0,0 +1,31 @@ +package manager.http; + +import com.sun.net.httpserver.HttpExchange; +import manager.TaskManager; +import java.io.IOException; + +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())) { + handleGetPrioritized(exchange); + } else { + sendBadRequest(exchange, "Неподдерживаемый метод"); + } + } catch (Exception e) { + sendResponse(exchange, "Ошибка сервера", 500); + } + } + + private void handleGetPrioritized(HttpExchange exchange) throws IOException { + String response = gson.toJson(taskManager.getPrioritizedTasks()); + sendText(exchange, response); + } +} diff --git a/src/manager/http/SubtasksHandler.java b/src/manager/http/SubtasksHandler.java new file mode 100644 index 0000000..f52c9d6 --- /dev/null +++ b/src/manager/http/SubtasksHandler.java @@ -0,0 +1,97 @@ +package manager.http; + +import com.google.gson.JsonSyntaxException; +import com.sun.net.httpserver.HttpExchange; +import manager.TaskManager; +import manager.exceptions.ManagerSaveException; +import tasks.Subtask; +import java.io.IOException; + +public class SubtasksHandler extends BaseHttpHandler { + private final TaskManager taskManager; + + public SubtasksHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + + switch (method) { + case "GET": + if (path.equals("/subtasks")) { + handleGetAllSubtasks(exchange); + } else if (path.startsWith("/subtasks/")) { + handleSubtaskWithId(exchange, path, "GET"); + } else { + sendBadRequest(exchange, "Некорректный путь"); + } + break; + case "POST": + handleCreateOrUpdateSubtask(exchange); + break; + case "DELETE": + if (path.startsWith("/subtasks/")) { + handleSubtaskWithId(exchange, path, "DELETE"); + } else { + sendBadRequest(exchange, "Некорректный путь"); + } + break; + default: + sendBadRequest(exchange, "Неподдерживаемый метод"); + } + } catch (Exception e) { + sendResponse(exchange, "Ошибка сервера", 500); + } + } + + private void handleSubtaskWithId(HttpExchange exchange, String path, String method) throws IOException { + String idStr = path.substring("/subtasks/".length()); + try { + int subtaskId = Integer.parseInt(idStr); + if (method.equals("GET")) { + Subtask subtask = taskManager.getSubtasks(subtaskId); + if (subtask != null) { + sendText(exchange, gson.toJson(subtask)); + } else { + sendNotFound(exchange); + } + } else if (method.equals("DELETE")) { + Subtask subtask = taskManager.deleteSubtask(subtaskId); + if (subtask != null) { + sendText(exchange, "Подзадача удалена"); + } else { + sendNotFound(exchange); + } + } + } catch (NumberFormatException e) { + sendBadRequest(exchange, "Некорректный ID подзадачи"); + } + } + + private void handleGetAllSubtasks(HttpExchange exchange) throws IOException { + String response = gson.toJson(taskManager.getSubtasks()); + sendText(exchange, response); + } + + private void handleCreateOrUpdateSubtask(HttpExchange exchange) throws IOException { + String body = readRequest(exchange); + try { + Subtask subtask = gson.fromJson(body, Subtask.class); + if (subtask.getId() == null) { + taskManager.createSubtask(subtask); + sendResponse(exchange, "Подзадача создана", 201); + } else { + taskManager.updateSubtask(subtask); + sendText(exchange, "Подзадача обновлена"); + } + } catch (JsonSyntaxException e) { + sendBadRequest(exchange, "Неверный формат JSON"); + } catch (ManagerSaveException e) { + sendHasOverlaps(exchange, e.getMessage()); + } + } +} diff --git a/src/manager/http/TasksHandler.java b/src/manager/http/TasksHandler.java new file mode 100644 index 0000000..7be7de6 --- /dev/null +++ b/src/manager/http/TasksHandler.java @@ -0,0 +1,97 @@ +package manager.http; + +import com.google.gson.JsonSyntaxException; +import com.sun.net.httpserver.HttpExchange; +import manager.TaskManager; +import manager.exceptions.ManagerSaveException; +import tasks.Task; +import java.io.IOException; + +public class TasksHandler extends BaseHttpHandler { + private final TaskManager taskManager; + + public TasksHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + + switch (method) { + case "GET": + if (path.equals("/tasks")) { + handleGetAllTasks(exchange); + } else if (path.startsWith("/tasks/")) { + handleTaskWithId(exchange, path, "GET"); + } else { + sendBadRequest(exchange, "Некорректный путь"); + } + break; + case "POST": + handleCreateOrUpdateTask(exchange); + break; + case "DELETE": + if (path.startsWith("/tasks/")) { + handleTaskWithId(exchange, path, "DELETE"); + } else { + sendBadRequest(exchange, "Некорректный путь"); + } + break; + default: + sendBadRequest(exchange, "Неподдерживаемый метод"); + } + } catch (Exception e) { + sendResponse(exchange, "Ошибка сервера", 500); + } + } + + private void handleTaskWithId(HttpExchange exchange, String path, String method) throws IOException { + String idStr = path.substring("/tasks/".length()); + try { + int taskId = Integer.parseInt(idStr); + if (method.equals("GET")) { + Task task = taskManager.getTask(taskId); + if (task != null) { + sendText(exchange, gson.toJson(task)); + } else { + sendNotFound(exchange); + } + } else if (method.equals("DELETE")) { + Task task = taskManager.deleteTask(taskId); + if (task != null) { + sendText(exchange, "Задача удалена"); + } else { + sendNotFound(exchange); + } + } + } catch (NumberFormatException e) { + sendBadRequest(exchange, "Некорректный ID задачи"); + } + } + + private void handleGetAllTasks(HttpExchange exchange) throws IOException { + String response = gson.toJson(taskManager.getTasks()); + sendText(exchange, response); + } + + private void handleCreateOrUpdateTask(HttpExchange exchange) throws IOException { + String body = readRequest(exchange); + try { + Task task = gson.fromJson(body, Task.class); + if (task.getId() == null) { + taskManager.createTask(task); + sendResponse(exchange, "Задача создана", 201); + } else { + taskManager.updateTask(task); + sendText(exchange, "Задача обновлена"); + } + } catch (JsonSyntaxException e) { + sendBadRequest(exchange, "Неверный формат JSON"); + } catch (ManagerSaveException e) { + sendHasOverlaps(exchange, e.getMessage()); + } + } +} diff --git a/src/manager/http/TimeAdapters.java b/src/manager/http/TimeAdapters.java new file mode 100644 index 0000000..fe67e52 --- /dev/null +++ b/src/manager/http/TimeAdapters.java @@ -0,0 +1,35 @@ +package manager.http; + +import com.google.gson.*; +import java.lang.reflect.Type; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class TimeAdapters { + public static class LocalDateTimeAdapter implements JsonSerializer, JsonDeserializer { + private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + + @Override + public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(formatter.format(src)); + } + + @Override + public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + return LocalDateTime.parse(json.getAsString(), formatter); + } + } + + public static class DurationAdapter implements JsonSerializer, JsonDeserializer { + @Override + public JsonElement serialize(Duration src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toMillis()); + } + + @Override + public Duration deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + return Duration.ofMillis(json.getAsLong()); + } + } +} diff --git a/test/manager/http/CommonTestSetup.java b/test/manager/http/CommonTestSetup.java new file mode 100644 index 0000000..16bf0a2 --- /dev/null +++ b/test/manager/http/CommonTestSetup.java @@ -0,0 +1,38 @@ +package manager.http; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import manager.TaskManager; +import java.net.http.HttpClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import java.io.IOException; +import java.time.Duration; +import java.time.LocalDateTime; + +public class CommonTestSetup { + protected HttpTaskServer server; + protected TaskManager taskManager; + protected Gson gson; + protected HttpClient client; + + @BeforeEach + void setUp() throws IOException { + server = new HttpTaskServer(); + server.start(); + taskManager = server.getTaskManager(); + + gson = new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, new TimeAdapters.LocalDateTimeAdapter()) + .registerTypeAdapter(Duration.class, new TimeAdapters.DurationAdapter()) + .create(); + + client = HttpClient.newHttpClient(); + } + + @AfterEach + void tearDown() { + server.stop(); + } +} + diff --git a/test/manager/http/EpicsEndpointTest.java b/test/manager/http/EpicsEndpointTest.java new file mode 100644 index 0000000..d09fdf0 --- /dev/null +++ b/test/manager/http/EpicsEndpointTest.java @@ -0,0 +1,60 @@ +package manager.http; + +import org.junit.jupiter.api.Test; +import tasks.Epic; +import tasks.Subtask; +import tasks.TaskStatus; + +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import static org.junit.jupiter.api.Assertions.*; + +class EpicsEndpointTest extends CommonTestSetup { + @Test + void createEpic_shouldReturn201AndSaveEpic() throws Exception { + Epic epic = new Epic("Epic", "Description"); + String epicJson = gson.toJson(epic); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/epics")) + .POST(HttpRequest.BodyPublishers.ofString(epicJson)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(201, response.statusCode()); + assertEquals(1, taskManager.getEpics().size()); + } + + @Test + void getEpicSubtasks_shouldReturn200AndSubtasksList() throws Exception { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + taskManager.createSubtask(new Subtask("Sub", "Desc", TaskStatus.NEW, epic.getId())); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/epics/" + epic.getId() + "/subtasks")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + assertTrue(response.body().contains("Sub")); + } + + @Test + void deleteEpic_shouldReturn200AndRemoveWithSubtasks() throws Exception { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + taskManager.createSubtask(new Subtask("Sub", "Desc", TaskStatus.NEW, epic.getId())); + + 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()); + assertEquals(0, taskManager.getEpics().size()); + assertEquals(0, taskManager.getSubtasks().size()); + } +} diff --git a/test/manager/http/HistoryEndpointTest.java b/test/manager/http/HistoryEndpointTest.java new file mode 100644 index 0000000..5ad9b3e --- /dev/null +++ b/test/manager/http/HistoryEndpointTest.java @@ -0,0 +1,40 @@ +package manager.http; + +import org.junit.jupiter.api.Test; +import tasks.Task; +import tasks.TaskStatus; + +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import static org.junit.jupiter.api.Assertions.*; + +class HistoryEndpointTest extends CommonTestSetup { + @Test + void getHistory_shouldReturn200AndHistoryList() throws Exception { + Task task = taskManager.createTask(new Task("Task", "Desc", TaskStatus.NEW)); + taskManager.getTask(task.getId()); + + 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()); + assertTrue(response.body().contains("Task")); + } + + @Test + void getHistory_whenEmpty_shouldReturn200AndEmptyList() throws Exception { + 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()); + assertEquals("[]", response.body()); + } +} diff --git a/test/manager/http/PrioritizedEndpointTest.java b/test/manager/http/PrioritizedEndpointTest.java new file mode 100644 index 0000000..f9f9084 --- /dev/null +++ b/test/manager/http/PrioritizedEndpointTest.java @@ -0,0 +1,54 @@ +package manager.http; + +import org.junit.jupiter.api.Test; +import tasks.Task; +import tasks.TaskStatus; + +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +class PrioritizedEndpointTest extends CommonTestSetup { + @Test + void getPrioritized_shouldReturn200AndOrderedTasks() throws Exception { + Task task1 = taskManager.createTask(new Task("First", "Desc", TaskStatus.NEW, + LocalDateTime.now().plusHours(1), Duration.ofMinutes(30))); + Task task2 = taskManager.createTask(new Task("Second", "Desc", TaskStatus.NEW, + LocalDateTime.now(), Duration.ofMinutes(30))); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/prioritized")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + assertTrue(response.body().indexOf("Second") < response.body().indexOf("First")); + } + + @Test + void getPrioritized_withoutTime_shouldReturn200AndTasksWithoutTimeLast() throws Exception { + + Task timedTask = taskManager.createTask(new Task("Timed", "Desc", TaskStatus.NEW, + LocalDateTime.now(), Duration.ofMinutes(30))); + Task untimedTask = taskManager.createTask(new Task("Untimed", "Desc", TaskStatus.NEW)); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/prioritized")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + String jsonResponse = response.body(); + System.out.println("Response JSON: " + jsonResponse); + assertTrue(jsonResponse.contains("\"name\":\"Timed\""), "Задача с временем должна быть в ответе"); + assertFalse(jsonResponse.contains("\"name\":\"Untimed\""), + "Задача без времени не должна быть в приоритетном списке"); + int taskCount = jsonResponse.split("\\{\"id\"").length - 1; + assertEquals(1, taskCount, "В приоритетном списке должна быть только одна задача"); + } +} diff --git a/test/manager/http/SubtasksEndpointTest.java b/test/manager/http/SubtasksEndpointTest.java new file mode 100644 index 0000000..121a0fa --- /dev/null +++ b/test/manager/http/SubtasksEndpointTest.java @@ -0,0 +1,71 @@ +package manager.http; + +import org.junit.jupiter.api.Test; +import tasks.Epic; +import tasks.Subtask; +import tasks.TaskStatus; + +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class SubtasksEndpointTest extends CommonTestSetup { + @Test + void createSubtask_withValidEpic_shouldReturn201() throws Exception { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + Subtask subtask = new Subtask("Subtask", "Desc", TaskStatus.NEW, epic.getId()); + String subtaskJson = gson.toJson(subtask); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/subtasks")) + .POST(HttpRequest.BodyPublishers.ofString(subtaskJson)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(201, response.statusCode()); + assertEquals(1, taskManager.getSubtasks().size()); + } + + @Test + void createSubtask_withoutEpic_shouldNotCreateSubtask() throws Exception { + assertNull(taskManager.getEpic(999), "Эпик 999 не должен существовать"); + List initialSubtasks = new ArrayList<>(taskManager.getSubtasks()); + Subtask subtask = new Subtask("Subtask", "Desc", TaskStatus.NEW, 999); + String subtaskJson = gson.toJson(subtask); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/subtasks")) + .POST(HttpRequest.BodyPublishers.ofString(subtaskJson)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(initialSubtasks, taskManager.getSubtasks(), + "Список подзадач не должен измениться"); + + if (response.statusCode() == 201) { + assertFalse(response.body().contains("\"id\":"), + "Ответ не должен содержать ID подзадачи"); + } + } + + @Test + void updateSubtaskStatus_shouldUpdateEpicStatus() throws Exception { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + Subtask subtask = taskManager.createSubtask( + new Subtask("Sub", "Desc", TaskStatus.NEW, epic.getId())); + + + subtask.setTaskStatus(TaskStatus.DONE); + String subtaskJson = gson.toJson(subtask); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/subtasks")) + .POST(HttpRequest.BodyPublishers.ofString(subtaskJson)) + .build(); + + client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(TaskStatus.DONE, epic.getTaskStatus()); + } +} \ No newline at end of file diff --git a/test/manager/http/TasksEndpointTest.java b/test/manager/http/TasksEndpointTest.java new file mode 100644 index 0000000..82974bd --- /dev/null +++ b/test/manager/http/TasksEndpointTest.java @@ -0,0 +1,101 @@ +package manager.http; + +import org.junit.jupiter.api.Test; +import tasks.Task; +import tasks.TaskStatus; + +import java.net.URI; +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.*; + +class TasksEndpointTest extends CommonTestSetup { + @Test + void createTask_shouldReturn201AndSaveTask() throws Exception { + Task task = new Task("Test", "Description", TaskStatus.NEW); + String taskJson = gson.toJson(task); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks")) + .POST(HttpRequest.BodyPublishers.ofString(taskJson)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(201, response.statusCode()); + assertEquals(1, taskManager.getTasks().size()); + } + + @Test + void getTaskById_withInvalidId_shouldReturn404() throws Exception { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks/999")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(404, response.statusCode()); + } + + @Test + void updateTask_shouldReturn200AndUpdateTask() throws Exception { + Task task = taskManager.createTask(new Task("Original", "Desc", TaskStatus.NEW)); + task.setName("Updated"); + String taskJson = gson.toJson(task); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks")) + .POST(HttpRequest.BodyPublishers.ofString(taskJson)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + assertEquals("Updated", taskManager.getTask(task.getId()).getName()); + } + + @Test + void deleteTask_shouldReturn200AndRemoveTask() throws Exception { + Task task = taskManager.createTask(new Task("ToDelete", "Desc", TaskStatus.NEW)); + + 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()); + assertEquals(0, taskManager.getTasks().size()); + } + + @Test + void createTaskWithTime_shouldReturn201AndSetTime() throws Exception { + Task task = new Task("Timed", "Desc", TaskStatus.NEW, + LocalDateTime.now(), Duration.ofMinutes(30)); + String taskJson = gson.toJson(task); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks")) + .POST(HttpRequest.BodyPublishers.ofString(taskJson)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(201, response.statusCode()); + + List tasks = taskManager.getTasks(); + + Task createdTask = null; + for (Task t : tasks) { + if ("Timed".equals(t.getName())) { + createdTask = t; + break; + } + } + assertNotNull(createdTask, "Задача не была создана на сервере"); + assertNotNull(createdTask.getStartTime(), "Время начала не установлено"); + assertNotNull(createdTask.getId(), "ID не был сгенерирован"); + } +}