diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java index dca451b..3f2baca 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java +++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java @@ -5,8 +5,7 @@ @SpringBootApplication public class FilmorateApplication { - public static void main(String[] args) { - SpringApplication.run(FilmorateApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(FilmorateApplication.class, args); + } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java index 08cf0a1..f5a1938 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -2,6 +2,66 @@ import org.springframework.web.bind.annotation.RestController; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import lombok.extern.slf4j.Slf4j; +import ru.yandex.practicum.filmorate.model.Film; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + @RestController +@RequestMapping("/films") +@Slf4j public class FilmController { + private final Map films = new HashMap<>(); + private Long idCounter = 1L; + + private void validateFilm(Film film) { + if (film.getName() == null || film.getName().isBlank()) { + log.warn("Название фильма не может быть пустым"); + throw new ValidationException("Название фильма не может быть пустым"); + } + if (film.getDescription() != null && film.getDescription().length() > 200) { + log.warn("Максимальная длина описания — 200 символов"); + throw new ValidationException("Максимальная длина описания — 200 символов"); + } + if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) { + log.warn("Дата релиза должна быть не раньше 28 декабря 1895 года"); + throw new ValidationException("Дата релиза должна быть не раньше 28 декабря 1895 года"); + } + if (film.getDuration() <= 0) { + log.warn("Продолжительность фильма должна быть положительным числом"); + throw new ValidationException("Продолжительность фильма должна быть положительным числом"); + } + } + + @PostMapping + public Film addFilm(@RequestBody Film film) { + validateFilm(film); + film.setId(idCounter++); + films.put(film.getId(), film); + log.info("Добавлен фильм с ID {}: {}", film.getId(), film); + return film; + } + + @PutMapping + public Film updateFilm(@RequestBody Film film) { + if (film.getId() == null || !films.containsKey(film.getId())) { + log.warn("Фильм с ID {} не найден", film.getId()); + throw new ValidationException("Фильм с указанным ID не существует"); + } + validateFilm(film); + films.put(film.getId(), film); + log.info("Обновлен фильм с ID {}: {}", film.getId(), film); + return film; + } + + @GetMapping + public List getAllFilms() { + log.info("Получен запрос всех фильмов. Текущее количество: {}", films.size()); + return new ArrayList<>(films.values()); + } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java new file mode 100644 index 0000000..2e77f19 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -0,0 +1,65 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.extern.slf4j.Slf4j; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/users") +@Slf4j +public class UserController { + private final Map users = new HashMap<>(); + private Long idCounter = 1L; + + private void validateUser(User user) { + if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) { + log.warn("Электронная почта не может быть пустой и должна содержать символ @"); + throw new ValidationException("Электронная почта не может быть пустой и должна содержать символ @"); + } + if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) { + log.warn("Логин не может быть пустым и содержать пробелы"); + throw new ValidationException("Логин не может быть пустым и содержать пробелы"); + } + if (user.getName() == null || user.getName().isBlank()) { + user.setName(user.getLogin()); + log.info("Для пользователя {} установлено имя из логина", user.getLogin()); + } + if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) { + log.warn("Дата рождения не может быть в будущем"); + throw new ValidationException("Дата рождения не может быть в будущем"); + } + } + + @PostMapping + public User createUser(@RequestBody User user) { + validateUser(user); + user.setId(idCounter++); + users.put(user.getId(), user); + log.info("Создан пользователь: {}", user); + return user; + } + + @PutMapping + public User updateUser(@RequestBody User user) { + if (user.getId() == null || !users.containsKey(user.getId())) { + log.warn("Пользователь с id {} не найден", user.getId()); + throw new ValidationException("Пользователь с указанным id не существует"); + } + validateUser(user); + users.put(user.getId(), user); + log.info("Обновлен пользователь: {}", user); + return user; + } + + @GetMapping + public List getAllUsers() { + log.info("Получен запрос всех пользователей. Текущее количество: {}", users.size()); + return new ArrayList<>(users.values()); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java new file mode 100644 index 0000000..532f9bd --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java index 3614a44..06dd12b 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -1,12 +1,14 @@ package ru.yandex.practicum.filmorate.model; -import lombok.Getter; -import lombok.Setter; -/** - * Film. - */ -@Getter -@Setter +import lombok.Data; +import java.time.LocalDate; + +@Data public class Film { + private Long id; + private String name; + private String description; + private LocalDate releaseDate; + private int duration; } diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java new file mode 100644 index 0000000..8a3fbe0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.Data; +import java.time.LocalDate; + +@Data +public class User { + private Long id; + private String email; + private String login; + private String name; + private LocalDate birthday; +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java b/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java new file mode 100644 index 0000000..48e3c78 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java @@ -0,0 +1,56 @@ +package ru.yandex.practicum.filmorate; + +import ru.yandex.practicum.filmorate.controller.FilmController; +import ru.yandex.practicum.filmorate.controller.UserController; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.User; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import static org.junit.jupiter.api.Assertions.*; + +class BoundaryTests { + private FilmController filmController = new FilmController(); + private UserController userController = new UserController(); + + @Test + void filmValidation_BoundaryValues() { + // Проверка минимально допустимой даты релиза + Film film1 = new Film(); + film1.setName("Boundary Film"); + film1.setDescription("Desc"); + film1.setReleaseDate(LocalDate.of(1895, 12, 28)); + film1.setDuration(1); + assertDoesNotThrow(() -> filmController.addFilm(film1)); + + // Проверка максимальной длины описания + Film film2 = new Film(); + film2.setName("Boundary Film"); + film2.setDescription("a".repeat(200)); + film2.setReleaseDate(LocalDate.of(2000, 1, 1)); + film2.setDuration(1); + assertDoesNotThrow(() -> filmController.addFilm(film2)); + } + + @Test + void userValidation_BoundaryValues() { + // Проверка сегодняшней даты рождения + User user1 = new User(); + user1.setEmail("test@example.com"); + user1.setLogin("testlogin"); + user1.setBirthday(LocalDate.now()); + assertDoesNotThrow(() -> userController.createUser(user1)); + + // Проверка минимально допустимого логина + User user2 = new User(); + user2.setEmail("test@example.com"); + user2.setLogin("a"); // Минимальный логин + user2.setBirthday(LocalDate.of(1990, 1, 1)); + assertDoesNotThrow(() -> userController.createUser(user2)); + } + + @Test + void nullRequest_ThrowsException() { + assertThrows(Exception.class, () -> filmController.addFilm(null)); + assertThrows(Exception.class, () -> userController.createUser(null)); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java new file mode 100644 index 0000000..50c0ac7 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java @@ -0,0 +1,113 @@ +package ru.yandex.practicum.filmorate; + +import ru.yandex.practicum.filmorate.controller.FilmController; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.Film; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class FilmControllerTest { + private FilmController filmController; + + @BeforeEach + void setUp() { + filmController = new FilmController(); + } + + @Test + void addFilm_ValidFilm_ReturnsFilmWithId() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + Film result = filmController.addFilm(film); + + assertNotNull(result.getId()); + assertEquals(film.getName(), result.getName()); + } + + @Test + void addFilm_EmptyName_ThrowsValidationException() { + Film film = new Film(); + film.setName(""); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void addFilm_TooLongDescription_ThrowsValidationException() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("a".repeat(201)); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void addFilm_TooEarlyReleaseDate_ThrowsValidationException() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(1895, 12, 27)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void addFilm_NegativeDuration_ThrowsValidationException() { + Film film = new Film(); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(-1); + + assertThrows(ValidationException.class, () -> filmController.addFilm(film)); + } + + @Test + void updateFilm_NonExistingId_ThrowsValidationException() { + Film film = new Film(); + film.setId(999L); + film.setName("Valid Film"); + film.setDescription("Valid description"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film.setDuration(120); + + assertThrows(ValidationException.class, () -> filmController.updateFilm(film)); + } + + @Test + void getAllFilms_EmptyList_ReturnsEmptyList() { + List films = filmController.getAllFilms(); + assertTrue(films.isEmpty()); + } + + @Test + void getAllFilms_WithFilms_ReturnsAllFilms() { + Film film1 = new Film(); + film1.setName("Film 1"); + film1.setReleaseDate(LocalDate.of(2000, 1, 1)); + film1.setDuration(120); + filmController.addFilm(film1); + + Film film2 = new Film(); + film2.setName("Film 2"); + film2.setReleaseDate(LocalDate.of(2001, 1, 1)); + film2.setDuration(90); + filmController.addFilm(film2); + + List films = filmController.getAllFilms(); + assertEquals(2, films.size()); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java new file mode 100644 index 0000000..527cfa5 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java @@ -0,0 +1,129 @@ +package ru.yandex.practicum.filmorate; + +import ru.yandex.practicum.filmorate.controller.UserController; +import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.model.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class UserControllerTest { + private UserController userController; + + @BeforeEach + void setUp() { + userController = new UserController(); + } + + @Test + void createUser_ValidUser_ReturnsUserWithId() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + User result = userController.createUser(user); + + assertNotNull(result.getId()); + assertEquals(user.getEmail(), result.getEmail()); + } + + @Test + void createUser_EmptyEmail_ThrowsValidationException() { + User user = new User(); + user.setEmail(""); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_InvalidEmailFormat_ThrowsValidationException() { + User user = new User(); + user.setEmail("invalid-email"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_EmptyLogin_ThrowsValidationException() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin(""); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_LoginWithSpaces_ThrowsValidationException() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("test login"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void createUser_EmptyName_SetsNameFromLogin() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setName(""); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + User result = userController.createUser(user); + assertEquals(user.getLogin(), result.getName()); + } + + @Test + void createUser_FutureBirthday_ThrowsValidationException() { + User user = new User(); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.now().plusDays(1)); + + assertThrows(ValidationException.class, () -> userController.createUser(user)); + } + + @Test + void updateUser_NonExistingId_ThrowsValidationException() { + User user = new User(); + user.setId(999L); + user.setEmail("test@example.com"); + user.setLogin("testlogin"); + user.setBirthday(LocalDate.of(1990, 1, 1)); + + assertThrows(ValidationException.class, () -> userController.updateUser(user)); + } + + @Test + void getAllUsers_EmptyList_ReturnsEmptyList() { + List users = userController.getAllUsers(); + assertTrue(users.isEmpty()); + } + + @Test + void getAllUsers_WithUsers_ReturnsAllUsers() { + User user1 = new User(); + user1.setEmail("user1@example.com"); + user1.setLogin("user1"); + user1.setBirthday(LocalDate.of(1990, 1, 1)); + userController.createUser(user1); + + User user2 = new User(); + user2.setEmail("user2@example.com"); + user2.setLogin("user2"); + user2.setBirthday(LocalDate.of(1991, 1, 1)); + userController.createUser(user2); + + List users = userController.getAllUsers(); + assertEquals(2, users.size()); + } +} \ No newline at end of file