diff --git a/pom.xml b/pom.xml
index f81fee6..383688d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,6 +37,11 @@
org.springframework.boot
spring-boot-starter-validation
+
+ org.zalando
+ logbook-spring-boot-starter
+ 3.7.2
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
new file mode 100644
index 0000000..d5053c7
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/ErrorHandler.java
@@ -0,0 +1,31 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.ErrorResponse;
+
+@RestControllerAdvice
+public class ErrorHandler {
+ @ExceptionHandler(value = {MethodArgumentNotValidException.class, ValidationException.class})
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ErrorResponse handleValidationException(final Exception e) {
+ return new ErrorResponse("Ошибка валидации", e.getMessage());
+ }
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public ErrorResponse handleNotFoundException(final NotFoundException e) {
+ return new ErrorResponse("Объект не найден", e.getMessage());
+ }
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public ErrorResponse handleThrowable(final Throwable e) {
+ return new ErrorResponse("Произошла непредвиденная ошибка", e.getMessage());
+ }
+}
\ No newline at end of file
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 8c62826..0e16103 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -1,124 +1,99 @@
package ru.yandex.practicum.filmorate.controller;
import jakarta.validation.Valid;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
-import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.Film;
-
-import java.time.LocalDate;
-import java.time.Month;
-import java.util.ArrayList;
-import java.util.HashMap;
+import ru.yandex.practicum.filmorate.service.FilmService;
import java.util.List;
@RestController
@RequestMapping("/films")
public class FilmController {
- private static final int FILM_DESCRIPTION_LENGTH = 200;
- private static final LocalDate EARLIEST_RELEASE_DATE = LocalDate.of(1895, Month.DECEMBER, 28);
- private static final Logger log = LoggerFactory.getLogger(FilmController.class);
- private final HashMap films = new HashMap<>();
+ private final FilmService filmService;
+
+ @Autowired
+ public FilmController(FilmService filmService) {
+ this.filmService = filmService;
+ }
+
+ /**
+ * Возвращает фильм по id
+ */
+ @GetMapping("/{id}")
+ public ResponseEntity getFilmById(@PathVariable int id) {
+ return ResponseEntity.ok(filmService.getFilmById(id));
+ }
/**
* Возвращает все фильмы в виде списка
*/
@GetMapping
public ResponseEntity> getFilms() {
- return ResponseEntity.ok(new ArrayList<>(films.values()));
+ return ResponseEntity.ok(filmService.getFilms());
}
/**
* Добавляет новый фильм.
- * Проверяет поля фильма на соответствие.
- * Если всё хорошо, то создаёт копию переданного фильма, присваивает уникальный ID и сохраняет в контроллер.
*
* @param newFilm объект фильма, который нужно добавить
- * @return копия созданного фильма с присвоенным ID
+ * @return ответ, содержащий созданный фильм с присвоенным ID
*/
@PostMapping
public ResponseEntity addFilm(@Valid @RequestBody Film newFilm) {
- try {
- checkIsValidFilm(newFilm);
- } catch (ValidationException exception) {
- log.error("Ошибка валидации данных фильма {} при добавлении: {}", newFilm.getId(), exception.getMessage());
- throw exception;
- }
- log.trace("Данные фильма {} прошли валидацию при добавлении", newFilm.getId());
-
- newFilm.setId(getNextId());
- films.put(newFilm.getId(), newFilm);
- log.info("Фильм {} добавлен", newFilm.getId());
-
- return ResponseEntity.status(HttpStatus.CREATED).body(newFilm);
+ Film createdFilm = filmService.addFilm(newFilm);
+ return ResponseEntity.status(HttpStatus.CREATED).body(createdFilm);
}
/**
* Обновляет фильм.
- * Проверяет поля переданного фильма на соответствие.
- * Если всё хорошо и переданный фильм существует в контроллере, то создаёт копию и сохраняет в контроллер.
*
* @param updatedFilm объект фильма, который нужно обновить
- * @return копия обновлённого фильма
+ * @return ответ, содержащий обновлённый фильм
*/
@PutMapping
public ResponseEntity updateFilm(@Valid @RequestBody Film updatedFilm) {
- try {
- checkIsValidFilm(updatedFilm);
- } catch (ValidationException exception) {
- log.error("Ошибка валидации данных фильма {} при обновлении: {}", updatedFilm.getId(),
- exception.getMessage());
- throw exception;
- }
-
- log.trace("Фильм {} прошёл валидацию для обновления", updatedFilm.getId());
- if (!films.containsKey(updatedFilm.getId())) {
- String filmNotFound = "Фильм " + updatedFilm.getId() + " для обновления не найден";
- log.error(filmNotFound);
- throw new ValidationException(filmNotFound);
- }
-
- films.put(updatedFilm.getId(), updatedFilm);
- log.info("Фильм {} обновлён", updatedFilm.getId());
-
- return ResponseEntity.ok(updatedFilm);
+ Film savedFilm = filmService.updateFilm(updatedFilm);
+ return ResponseEntity.ok(savedFilm);
}
/**
- * Генерирует следующий Id.
- * Находит максимальный текущй Id и увеличивает его.
+ * Добавляет лайк к фильму
+ *
+ * @param id идентификатор фильма, которому нужно добавить лайк.
+ * @param userId идентификатор пользователя, кто поставил лайк.
+ * @return пустой ответ со статусом 200
*/
- private int getNextId() {
- int currentMaxId = films.keySet()
- .stream()
- .mapToInt(id -> id)
- .max()
- .orElse(0);
- return ++currentMaxId;
+ @PutMapping("/{id}/like/{userId}")
+ public ResponseEntity addLikeByUser(@PathVariable int id, @PathVariable int userId) {
+ filmService.addLike(id, userId);
+ return ResponseEntity.ok().build();
}
/**
- * Проверяет переданный фильм на соответствие условиям.
- * Если не удовлетворяет какой-то проверке, то выбрасывается ошибка
+ * Убирает лайк с фильма
+ *
+ * @param id идентификатор фильма, которому нужно убрать лайк.
+ * @param userId идентификатор пользователя, чей лайк надо убрать.
+ * @return пустой ответ со статусом 200
*/
- private void checkIsValidFilm(Film film) throws ValidationException {
- if (film == null) {
- throw new ValidationException("Фильм не найден");
- }
-
- if (film.getDescription().length() > FILM_DESCRIPTION_LENGTH) {
- throw new ValidationException("Описание фильма не может быть больше 200 символов");
- }
-
- if (film.getReleaseDate().isBefore(EARLIEST_RELEASE_DATE)) {
- throw new ValidationException("Дата релиза фильма не может быть раньше 28 декабря 1895 года");
- }
+ @DeleteMapping("/{id}/like/{userId}")
+ public ResponseEntity removeLikeByUser(@PathVariable int id, @PathVariable int userId) {
+ filmService.removeLike(id, userId);
+ return ResponseEntity.ok().build();
+ }
- if (film.getDuration() <= 0) {
- throw new ValidationException("Продолжительность фильма должна быть положительным числом");
- }
+ /**
+ * Возвращает самые популярные фильмы в виде списка.
+ *
+ * @param count количество фильмов, которое максимально надо вернуть
+ * @return список самых популярных фильмов
+ */
+ @GetMapping("/popular")
+ public ResponseEntity> getMostPopularFilms(
+ @RequestParam(required = false, defaultValue = "10") int count) {
+ return ResponseEntity.ok(filmService.getMostPopularFilms(count));
}
}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
index a742286..a441f3a 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -1,122 +1,110 @@
package ru.yandex.practicum.filmorate.controller;
import jakarta.validation.Valid;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
-import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.User;
-
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.HashMap;
+import ru.yandex.practicum.filmorate.service.UserService;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
- private static final Logger log = LoggerFactory.getLogger(UserController.class);
- private final HashMap users = new HashMap<>();
+ private final UserService userService;
+
+ @Autowired
+ public UserController(UserService userService) {
+ this.userService = userService;
+ }
+
+ /**
+ * Возвращает пользователя по id
+ */
+ @GetMapping("/{id}")
+ public ResponseEntity getUserById(@PathVariable int id) {
+ return ResponseEntity.ok(userService.getUserById(id));
+ }
/**
* Возвращает всех пользователей в виде списка
*/
@GetMapping
public ResponseEntity> getUsers() {
- return ResponseEntity.ok(new ArrayList<>(users.values()));
+ return ResponseEntity.ok(userService.getUsers());
}
/**
* Добавляет нового пользователя.
- * Проверяет поля пользователя на соответствие.
- * Если всё хорошо, то создаёт копию переданного пользователя, присваивает уникальный ID и сохраняет в контроллер.
*
* @param newUser объект пользователя, который нужно добавить
- * @return копия созданного пользователя с присвоенным ID
+ * @return ответ, содержащий созданного пользователя с присвоенным ID
*/
@PostMapping
public ResponseEntity addUser(@Valid @RequestBody User newUser) {
- try {
- checkIsValidUser(newUser);
- } catch (ValidationException exception) {
- log.error("Ошибка валидации данных пользователя при добавлении: {}",
- exception.getMessage());
- throw exception;
- }
- log.info("Данные пользователя прошли валидацию при добавлении");
-
- newUser.setId(getNextId());
- users.put(newUser.getId(), newUser);
- log.info("Пользователь {} добавлен", newUser.getId());
-
- return ResponseEntity.status(HttpStatus.CREATED).body(newUser);
+ User createdUser = userService.addUser(newUser);
+ return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
/**
* Обновляет пользователя.
- * Проверяет поля переданного пользователя на соответствие.
- * Если всё хорошо и переданный пользователь существует в контроллере, то создаёт копию и сохраняет в контроллер.
*
* @param updatedUser объект пользователя, который нужно обновить
- * @return копия обновлённого пользователя
+ * @return ответ, содержащий обновлённого пользователя
*/
@PutMapping
public ResponseEntity updateUser(@Valid @RequestBody User updatedUser) {
- try {
- checkIsValidUser(updatedUser);
- } catch (ValidationException exception) {
- log.error("Ошибка валидации данных пользователя {} при обновлении: {}", updatedUser.getId(),
- exception.getMessage());
- throw exception;
- }
- log.info("Данные пользователя {} прошли валидацию при обновлении", updatedUser.getId());
-
- if (!users.containsKey(updatedUser.getId())) {
- String userNotFound = "Пользователь " + updatedUser.getId() + " для обновления не найден";
- log.error(userNotFound);
- throw new ValidationException(userNotFound);
- }
-
- users.put(updatedUser.getId(), updatedUser);
- log.info("Пользователь {} обновлён", updatedUser.getId());
-
- return ResponseEntity.ok(updatedUser);
+ User savedUser = userService.updateUser(updatedUser);
+ return ResponseEntity.ok(savedUser);
}
/**
- * Генерирует следующий Id.
- * Находит максимальный текущй Id и увеличивает его.
+ * Добавляет пользователя в друзья к другому пользователю
+ *
+ * @param id идентификатор пользователя, которому нужно добавить друга.
+ * @param friendId идентификатор пользователя, которого нужно добавить в друзья
+ * @return пустой ответ со статусом 200
*/
- private int getNextId() {
- int currentMaxId = users.keySet()
- .stream()
- .mapToInt(id -> id)
- .max()
- .orElse(0);
- return ++currentMaxId;
+ @PutMapping("/{id}/friends/{friendId}")
+ public ResponseEntity addFriend(@PathVariable int id, @PathVariable int friendId) {
+ userService.addFriends(id, friendId);
+ return ResponseEntity.ok().build();
}
/**
- * Проверяет переданного пользователя на соответствие условиям.
- * Если не удовлетворяет какой-то проверке, то выбрасывается ошибка
+ * Удаляет пользователя из друзей другого пользователя
+ *
+ * @param id идентификатор пользователя, у которого нужно удалить друга.
+ * @param friendId идентификатор пользователя, которого нужно удалить из друзей
+ * @return пустой ответ со статусом 200
*/
- private void checkIsValidUser(User user) throws ValidationException {
- if (user == null) {
- throw new ValidationException("Пользователь не найден");
- }
-
- if (user.getLogin().contains(" ")) {
- throw new ValidationException("Логин пользователя не может содержать пробел");
- }
+ @DeleteMapping("/{id}/friends/{friendId}")
+ public ResponseEntity removeFriend(@PathVariable int id, @PathVariable int friendId) {
+ userService.removeFriends(id, friendId);
+ return ResponseEntity.ok().build();
+ }
- if (user.getBirthday() != null && user.getBirthday().isAfter(LocalDate.now())) {
- throw new ValidationException("Дата рождения не может быть в будущем");
- }
+ /**
+ * Возвращает друзей пользователя по его id.
+ *
+ * @param id идентификатор пользователя.
+ * @return список друзей-пользователей указанного пользователя.
+ */
+ @GetMapping("/{id}/friends")
+ public ResponseEntity> getAllFriends(@PathVariable int id) {
+ return ResponseEntity.ok(userService.getUserFriendsById(id));
+ }
- if (user.getName() == null || user.getName().isEmpty()) {
- user.setName(user.getLogin());
- }
+ /**
+ * Возвращает общих друзей пользователей по их id.
+ *
+ * @param id идентификатор пользователя, для которого ищем общих друзей.
+ * @param otherId идентификатор пользователя, с кем ищем общих друзей.
+ * @return список общих друзей в виде списка пользователей.
+ */
+ @GetMapping("/{id}/friends/common/{otherId}")
+ public ResponseEntity> getCommonFriends(@PathVariable int id, @PathVariable int otherId) {
+ return ResponseEntity.ok(userService.getCommonFriends(id, otherId));
}
}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java
new file mode 100644
index 0000000..dd8d262
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class NotFoundException extends RuntimeException {
+ public NotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java b/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java
new file mode 100644
index 0000000..9d04f7d
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/ErrorResponse.java
@@ -0,0 +1,14 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.Getter;
+
+@Getter
+public class ErrorResponse {
+ String error;
+ String description;
+
+ public ErrorResponse(String error,String description) {
+ this.description = description;
+ this.error = error;
+ }
+}
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 a80bbf0..e9b9a17 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -5,14 +5,13 @@
import lombok.*;
import java.time.LocalDate;
+import java.util.Set;
-/**
- * Film.
- */
@Data
@Builder(toBuilder = true)
@EqualsAndHashCode(of = { "id" })
@AllArgsConstructor
+@NoArgsConstructor
public class Film {
private int id;
@@ -26,4 +25,6 @@ public class Film {
@NotNull
private Integer duration;
+
+ private Set likes;
}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
index 3f13eab..4f16a1e 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -2,12 +2,10 @@
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import lombok.*;
import java.time.LocalDate;
+import java.util.Set;
@Data
@Builder(toBuilder = true)
@@ -25,4 +23,6 @@ public class User {
private String login;
private LocalDate birthday;
+
+ Set friends;
}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
new file mode 100644
index 0000000..5fd80e8
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -0,0 +1,148 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.time.LocalDate;
+import java.time.Month;
+import java.util.Comparator;
+import java.util.List;
+
+@Service
+public class FilmService {
+ private static final int FILM_DESCRIPTION_LENGTH = 200;
+ private static final LocalDate EARLIEST_RELEASE_DATE = LocalDate.of(1895, Month.DECEMBER, 28);
+ private static final Logger log = LoggerFactory.getLogger(FilmService.class);
+ private final FilmStorage filmStorage;
+ private final UserStorage userStorage;
+
+ @Autowired
+ public FilmService(FilmStorage filmStorage, UserStorage userStorage) {
+ this.filmStorage = filmStorage;
+ this.userStorage = userStorage;
+ }
+
+ /**
+ * Возвращает фильм по id.
+ * Вызывает метод хранилища по получению фильма по id.
+ */
+ public Film getFilmById(int id) {
+ return filmStorage.getFilmById(id);
+ }
+
+ /**
+ * Возвращает все фильмы в виде списка.
+ * Вызывает метод хранилища по получению всех фильмов
+ */
+ public List getFilms() {
+ return filmStorage.getFilms();
+ }
+
+ /**
+ * Добавляет новый фильм.
+ * Проверяет поля переданного фильма на соответствие.
+ * Если всё хорошо, то вызывает соответствующий метод хранилища.
+ */
+ public Film addFilm(Film newFilm) {
+ try {
+ checkIsValidFilm(newFilm);
+ } catch (ValidationException exception) {
+ log.error("Ошибка валидации данных фильма {} при добавлении: {}", newFilm.getId(), exception.getMessage());
+ throw exception;
+ }
+ log.trace("Данные фильма {} прошли валидацию при добавлении", newFilm.getId());
+ return filmStorage.addFilm(newFilm);
+ }
+
+ /**
+ * Обновляет фильм.
+ * Проверяет поля переданного фильма на соответствие.
+ * Если всё хорошо, то вызывает соответствующий метод хранилища.
+ */
+ public Film updateFilm(Film updatedFilm) {
+ try {
+ checkIsValidFilm(updatedFilm);
+ } catch (ValidationException exception) {
+ log.error("Ошибка валидации данных фильма {} при обновлении: {}", updatedFilm.getId(),
+ exception.getMessage());
+ throw exception;
+ }
+ log.trace("Фильм {} прошёл валидацию для обновления", updatedFilm.getId());
+ return filmStorage.updateFilm(updatedFilm);
+ }
+
+ /**
+ * Добавляет лайк к фильму.
+ * Если пользователь и фильм существуют, то добавляет id пользователя в список тех, кто его лайкнул.
+ * Вызывает метод хранилища по обновлению фильма.
+ */
+ public void addLike(int filmId, int userId) {
+ userStorage.getUserById(userId);
+ log.trace("Пользователь {} найден для добавления лайка", userId);
+ Film film = filmStorage.getFilmById(filmId);
+ log.trace("Фильм {} найден для добавления лайка", filmId);
+
+ film.getLikes().add(userId);
+ log.info("Добавление лайка от пользователя {} для фильма {} выполнено", userId, filmId);
+
+ filmStorage.updateFilm(film);
+ }
+
+ /**
+ * Убирает лайк у фильма.
+ * Если фильм существу, то убирает id пользователя из списка тех, кто его лайкнул.
+ * Вызывает метод хранилища по обновлению фильма.
+ */
+ public void removeLike(int filmId, int userId) {
+ userStorage.getUserById(userId);
+ log.trace("Пользователь {} найден для удаления лайка", userId);
+ Film film = filmStorage.getFilmById(filmId);
+ log.trace("Фильм {} найден для удаления лайка", filmId);
+
+ film.getLikes().remove(userId);
+ log.info("Удаление лайка от пользователя {} для фильма {} выполнено", userId, filmId);
+
+ filmStorage.updateFilm(film);
+ }
+
+ /**
+ * Возвращает список самых популярных фильмов в виде списка.
+ * Вызывает метод хранилища по получению всех фильмов, сортирует их и фильтрует по количеству.
+ */
+ public List getMostPopularFilms(int count) {
+ return filmStorage.getFilms().stream()
+ .sorted(Comparator.comparingInt(
+ (Film film) -> film.getLikes().size()).reversed().thenComparingInt(Film::getId))
+ .limit(count)
+ .toList();
+ }
+
+ /**
+ * Проверяет переданный фильм на соответствие условиям.
+ * Если не удовлетворяет какой-то проверке, то выбрасывается ошибка
+ */
+ private void checkIsValidFilm(Film film) throws ValidationException {
+ if (film == null) {
+ throw new ValidationException("Фильм для валидации входных параметров не найден");
+ }
+
+ if (film.getDescription().length() > FILM_DESCRIPTION_LENGTH) {
+ throw new ValidationException("Описание фильма не может быть больше 200 символов");
+ }
+
+ if (film.getReleaseDate().isBefore(EARLIEST_RELEASE_DATE)) {
+ throw new ValidationException("Дата релиза фильма не может быть раньше 28 декабря 1895 года");
+ }
+
+ if (film.getDuration() <= 0) {
+ throw new ValidationException("Продолжительность фильма должна быть положительным числом");
+ }
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
new file mode 100644
index 0000000..5ef5d21
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -0,0 +1,168 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Service
+public class UserService {
+ private final UserStorage userStorage;
+ private static final Logger log = LoggerFactory.getLogger(UserService.class);
+
+ @Autowired
+ public UserService(UserStorage userStorage) {
+ this.userStorage = userStorage;
+ }
+
+ /**
+ * Возвращает пользователя по id.
+ * Вызывает метод хранилища по получению пользователя по id.
+ */
+ public User getUserById(int id) {
+ return userStorage.getUserById(id);
+ }
+
+ /**
+ * Возвращает всех пользователей в виде списка.
+ * Вызывает соответствующий метод хранилища.
+ */
+ public List getUsers() {
+ return userStorage.getUsers();
+ }
+
+ /**
+ * Добавляет нового пользователя.
+ * Проверяет поля пользователя на соответствие.
+ * Если всё хорошо, то вызывает соответствующий метод хранилища.
+ *
+ * @param newUser объект пользователя, который нужно добавить
+ * @return созданный пользователь с присвоенным ID
+ */
+ public User addUser(User newUser) {
+ try {
+ checkIsValidUser(newUser);
+ } catch (ValidationException exception) {
+ log.error("Ошибка валидации данных пользователя при добавлении: {}",
+ exception.getMessage());
+ throw exception;
+ }
+ log.trace("Данные новго пользователя c логином {} прошли валидацию при добавлении", newUser.getLogin());
+ return userStorage.addUser(newUser);
+ }
+
+ /**
+ * Обновляет пользователя.
+ * Проверяет поля переданного пользователя на соответствие.
+ * Если всё хорошо, то вызывает соответствующий метод хранилища.
+ *
+ * @param updatedUser объект пользователя, который нужно обновить
+ * @return обновлённый пользователь
+ */
+ public User updateUser(User updatedUser) {
+ try {
+ checkIsValidUser(updatedUser);
+ } catch (ValidationException exception) {
+ log.error("Ошибка валидации данных пользователя {} при обновлении: {}", updatedUser.getId(),
+ exception.getMessage());
+ throw exception;
+ }
+ log.info("Данные пользователя {} прошли валидацию при обновлении", updatedUser.getId());
+
+ return userStorage.updateUser(updatedUser);
+ }
+
+ /**
+ * Добавляет пользователей в друзья.
+ * Если оба пользователя существуют, то обновляет их списки друзей и обновляет пользователей в хранилище.
+ */
+ public void addFriends(int firstUserId, int secondUserId) {
+ User firstUser = userStorage.getUserById(firstUserId);
+ log.trace("Пользователь {} найден для добавления в друзья", firstUserId);
+ User secondUser = userStorage.getUserById(secondUserId);
+ log.trace("Пользователь {} найден для добавления в друзья", secondUserId);
+
+ firstUser.getFriends().add(secondUserId);
+ secondUser.getFriends().add(firstUser.getId());
+ log.info("Пользователь {} добавлен в друзья пользователя {}.",
+ secondUserId, firstUserId);
+
+ userStorage.updateUser(firstUser);
+ userStorage.updateUser(secondUser);
+ log.trace("Пользователи обновлены в хранилище.");
+ }
+
+ /**
+ * Удаляет пользователей из друзей.
+ * Если оба пользователя существуют, то обновляет их списки друзей и передаёт обновлённых пользователей в мапу.
+ */
+ public void removeFriends(int firstUserId, int secondUserId) {
+ User firstUser = userStorage.getUserById(firstUserId);
+ log.trace("Пользователь {} найден для удаления мз друзей", firstUserId);
+ User secondUser = userStorage.getUserById(secondUserId);
+ log.trace("Пользователь {} найден для удаления мз друзей", secondUserId);
+
+ firstUser.getFriends().remove(secondUserId);
+ secondUser.getFriends().remove(firstUserId);
+ log.info("Пользователь {} удален из друзей пользователя {}.",
+ secondUserId, firstUserId);
+
+ userStorage.updateUser(firstUser);
+ userStorage.updateUser(secondUser);
+ log.trace("Пользователи обновлены в хранилище.");
+ }
+
+ /**
+ * Ищет общих друзей у пользователей
+ * Если оба пользователя существуют, то возвращает список общих друзей.
+ */
+ public List getCommonFriends(int firstUserId, int secondUserId) {
+ List firstFriends = getUserFriendsById(firstUserId);
+ List secondFriends = getUserFriendsById(secondUserId);
+ log.trace("Списки друзей пользователей для поиска общих друзей составлены", secondUserId);
+
+ return firstFriends.stream()
+ .filter(secondFriends::contains)
+ .toList();
+ }
+
+ /**
+ * Возвращает всех друзей пользователя
+ * Если пользователь существуюет, то возвращает список по id
+ */
+ public List getUserFriendsById(int userId) {
+ User user = userStorage.getUserById(userId);
+ log.trace("Пользователь {} найден для поиска всех его друзей", userId);
+
+ return user.getFriends().stream().map(id -> userStorage.getUserById(id)).toList();
+ }
+
+ /**
+ * Проверяет переданного пользователя на соответствие условиям.
+ * Если не удовлетворяет какой-то проверке, то выбрасывается ошибка
+ */
+ private void checkIsValidUser(User user) throws ValidationException {
+ if (user == null) {
+ throw new NotFoundException("Пользователь для валидации входных параметров не найден");
+ }
+
+ if (user.getLogin().contains(" ")) {
+ throw new ValidationException("Логин пользователя не может содержать пробел");
+ }
+
+ if (user.getBirthday() != null && user.getBirthday().isAfter(LocalDate.now())) {
+ throw new ValidationException("Дата рождения не может быть в будущем");
+ }
+
+ if (user.getName() == null || user.getName().isEmpty()) {
+ user.setName(user.getLogin());
+ }
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
new file mode 100644
index 0000000..8ba57f5
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
@@ -0,0 +1,26 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.Film;
+import java.util.List;
+
+public interface FilmStorage {
+ /**
+ * Возвращает фильм по id
+ */
+ Film getFilmById(int id);
+
+ /**
+ * Возвращает все фильмы в виде списка
+ */
+ List getFilms();
+
+ /**
+ * Добавляет новый фильм.
+ */
+ Film addFilm(Film newFilm);
+
+ /**
+ * Обновляет фильм.
+ */
+ Film updateFilm(Film updatedFilm);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
new file mode 100644
index 0000000..e128d75
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
@@ -0,0 +1,110 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+@Component
+public class InMemoryFilmStorage implements FilmStorage {
+ private final HashMap films = new HashMap<>();
+ private static final Logger log = LoggerFactory.getLogger(InMemoryFilmStorage.class);
+
+ /**
+ * Возвращает фильм по id
+ */
+ @Override
+ public Film getFilmById(int id) {
+ if (films.get(id) == null) {
+ String filmNotFound = "Фильм по указанному id " + id + " не найден.";
+ log.error(filmNotFound);
+ throw new NotFoundException(filmNotFound);
+ }
+ return films.get(id);
+ }
+
+ /**
+ * Возвращает все фильмы в виде списка
+ */
+ @Override
+ public List getFilms() {
+ return new ArrayList<>(films.values());
+ }
+
+ /**
+ * Добавляет новый фильм.
+ * Создаёт копию переданного фильма, присваивает уникальный ID и сохраняет в набор фильмов.
+ *
+ * @param newFilm объект фильма, который нужно добавить
+ * @return созданный фильм с присвоенным ID
+ */
+ @Override
+ public Film addFilm(Film newFilm) {
+ if (newFilm == null) {
+ String filmNotFound = "Фильм для добавления не найден";
+ log.error(filmNotFound);
+ throw new NotFoundException(filmNotFound);
+ }
+
+ newFilm.setId(getNextId());
+ log.info("Фильму {} присвоен id {}", newFilm.getName(), newFilm.getId());
+
+ if (newFilm.getLikes() == null) {
+ newFilm.setLikes(new HashSet<>());
+ }
+
+ films.put(newFilm.getId(), newFilm);
+ log.info("Фильм {} добавлен в хранилище", newFilm.getId());
+ return newFilm;
+ }
+
+ /**
+ * Обновляет фильм.
+ * Проверяет поля переданного фильма на соответствие.
+ * Если всё хорошо и переданный фильм существует в контроллере, то создаёт копию и сохраняет в контроллер.
+ *
+ * @param updatedFilm объект фильма, который нужно обновить
+ * @return копия обновлённого фильма
+ */
+ @Override
+ public Film updateFilm(Film updatedFilm) {
+ if (updatedFilm == null) {
+ String filmNotFound = "Фильм для обновления не найден";
+ log.error(filmNotFound);
+ throw new NotFoundException(filmNotFound);
+ }
+
+ if (!films.containsKey(updatedFilm.getId())) {
+ String filmNotFoundInStorage = "Фильм " + updatedFilm.getId() + " для обновления не найден в хранилище";
+ log.error(filmNotFoundInStorage);
+ throw new NotFoundException(filmNotFoundInStorage);
+ }
+
+ if (updatedFilm.getLikes() == null) {
+ updatedFilm.setLikes(new HashSet<>());
+ }
+
+ films.put(updatedFilm.getId(), updatedFilm);
+ log.info("Фильм {} обновлён в хранилище", updatedFilm.getId());
+ return updatedFilm;
+ }
+
+ /**
+ * Генерирует следующий Id.
+ * Находит максимальный текущй Id и увеличивает его.
+ */
+ private int getNextId() {
+ int currentMaxId = films.keySet()
+ .stream()
+ .mapToInt(id -> id)
+ .max()
+ .orElse(0);
+ return ++currentMaxId;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
new file mode 100644
index 0000000..1af6df2
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
@@ -0,0 +1,108 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.*;
+
+@Component
+public class InMemoryUserStorage implements UserStorage {
+ private static final Logger log = LoggerFactory.getLogger(InMemoryUserStorage.class);
+ private final HashMap users = new HashMap<>();
+
+ /**
+ * Возвращает пользователя по id
+ */
+ @Override
+ public User getUserById(int id) {
+ if (users.get(id) == null) {
+ String userNotFound = "Пользователь с id = " + id + " не найден";
+ log.error(userNotFound);
+ throw new NotFoundException(userNotFound);
+ }
+
+ return users.get(id);
+ }
+
+ /**
+ * Возвращает всех пользователей в виде списка
+ */
+ @Override
+ public List getUsers() {
+ return new ArrayList<>(users.values());
+ }
+
+ /**
+ * Добавляет нового пользователя.
+ * Присваивает уникальный ID и сохраняет в набор пользователей.
+ *
+ * @param newUser объект пользователя, который нужно добавить
+ * @return созданный пользователь с присвоенным ID
+ */
+ @Override
+ public User addUser(User newUser) {
+ if (newUser == null) {
+ String userNotFound = "Пользователь для добавления не найден";
+ log.error(userNotFound);
+ throw new NotFoundException(userNotFound);
+ }
+
+ newUser.setId(getNextId());
+ log.info("Пользователю с логином {} присвоен id {}", newUser.getLogin(), newUser.getId());
+
+ if (newUser.getFriends() == null) {
+ newUser.setFriends(new HashSet<>());
+ }
+
+ users.put(newUser.getId(), newUser);
+ log.info("Пользователь {} добавлен в хранилище", newUser.getId());
+ return newUser;
+ }
+
+ /**
+ * Обновляет пользователя.
+ * Если переданный пользователь существует, то обновляет пользователя в наборе пользователей.
+ *
+ * @param updatedUser объект пользователя, который нужно обновить
+ * @return обновлённый пользователь
+ */
+ @Override
+ public User updateUser(User updatedUser) {
+ if (updatedUser == null) {
+ String userNotFound = "Пользователь для обновления не найден";
+ log.error(userNotFound);
+ throw new NotFoundException(userNotFound);
+ }
+
+ if (!users.containsKey(updatedUser.getId())) {
+ String userNotFound = "Пользователь " + updatedUser.getId() + " для обновления не найден в хранилище";
+ log.error(userNotFound);
+ throw new NotFoundException(userNotFound);
+ }
+
+ if (updatedUser.getFriends() == null) {
+ updatedUser.setFriends(new HashSet<>());
+ }
+
+ users.put(updatedUser.getId(), updatedUser);
+ log.info("Пользователь {} обновлён", updatedUser.getId());
+
+ return updatedUser;
+ }
+
+ /**
+ * Генерирует следующий Id.
+ * Находит максимальный текущй Id и увеличивает его.
+ */
+ private int getNextId() {
+ int currentMaxId = users.keySet()
+ .stream()
+ .mapToInt(id -> id)
+ .max()
+ .orElse(0);
+ return ++currentMaxId;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
new file mode 100644
index 0000000..7a01079
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
@@ -0,0 +1,36 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.User;
+import java.util.List;
+
+public interface UserStorage {
+ /**
+ * Возвращает пользователя по id
+ */
+ User getUserById(int id);
+
+ /**
+ * Возвращает всех пользователей в виде списка
+ */
+ List getUsers();
+
+ /**
+ * Добавляет нового пользователя.
+ * Проверяет поля пользователя на соответствие.
+ * Если всё хорошо, то создаёт копию переданного пользователя, присваивает уникальный ID и сохраняет в контроллер.
+ *
+ * @param newUser объект пользователя, который нужно добавить
+ * @return копия созданного пользователя с присвоенным ID
+ */
+ User addUser(User newUser);
+
+ /**
+ * Обновляет пользователя.
+ * Проверяет поля переданного пользователя на соответствие.
+ * Если всё хорошо и переданный пользователь существует в контроллере, то создаёт копию и сохраняет в контроллер.
+ *
+ * @param updatedUser объект пользователя, который нужно обновить
+ * @return копия обновлённого пользователя
+ */
+ User updateUser(User updatedUser);
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8b13789..3eb70b5 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,2 @@
+logging.level.org.zalando.logbook: TRACE
diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
index 6ec6c48..4a321e2 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
@@ -3,8 +3,18 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.service.FilmService;
+import ru.yandex.practicum.filmorate.service.UserService;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.InMemoryFilmStorage;
+import ru.yandex.practicum.filmorate.storage.InMemoryUserStorage;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
import java.time.LocalDate;
import java.time.Month;
@@ -28,12 +38,29 @@ class FilmControllerTest {
private static final int FILM_DURATION_2 = 200;
private static final int FILM_DURATION_INCORRECT = -100;
+ private static final int FILM_ID = 100;
+ private static final int FILM_COUNT_1 = 1;
+ private static final int FILM_DEFAULT_COUNT = 10;
+
+ private static final String USER_NAME = "Вася";
+ private static final String USER_EMAIL = "mail@mail.ru";
+ private static final String USER_LOGIN = "user";
+ private static final LocalDate USER_BIRTHDAY = LocalDate.of(1992, Month.DECEMBER, 12);
+ private static final Integer USER_ID = 567;
+
FilmController filmController;
+ UserController userController;
@BeforeEach
@DisplayName("Инициализирует контроллер")
void setUp() {
- filmController = new FilmController();
+ UserStorage userStorage = new InMemoryUserStorage();
+ UserService userService = new UserService(userStorage);
+ userController = new UserController(userService);
+
+ FilmStorage filmStorage = new InMemoryFilmStorage();
+ FilmService filmService = new FilmService(filmStorage, userStorage);
+ filmController = new FilmController(filmService);
}
@Test
@@ -187,4 +214,173 @@ void test_updateFilm_WhenIncorrectReleaseDate_ShouldThrowsError() {
"Фильм может быть обновлён с некорректной датой релиза");
}
+ @Test
+ @DisplayName("При получении существующего фильма по id контроллер должен вернуть его")
+ void test_getFilmById_WhenFilmExists_ShouldReturnOne() {
+ //given
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+
+ //when
+ Film findFilm = filmController.getFilmById(createdFilm.getId()).getBody();
+
+ //then
+ assertNotNull(findFilm, "В контроллере нет фильмов");
+ assertEquals(FILM_NAME, findFilm.getName(), "В контроллере некорректный фильм");
+ }
+
+ @Test
+ @DisplayName("При получении несуществующего фильма по id контроллер должен вернуть ошибку")
+ void test_getFilmById_WhenFilmNotExists_ShouldThrowsError() {
+ //given && when && then
+ assertThrows(NotFoundException.class,
+ () -> filmController.getFilmById(FILM_ID).getBody(),
+ "Из контроллера получен несуществующий фильм");
+ }
+
+ @Test
+ @DisplayName("При добавлении лайка существующему фильму от существующего пользователя должен вернуться OK 200")
+ void test_addLikeByUser_WhenFilmAndUserExist_ShouldReturn200() {
+ //given
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+
+
+ //when
+ ResponseEntity response = filmController.addLikeByUser(createdFilm.getId(), createdUser.getId());
+
+ //then
+ assertEquals(HttpStatus.OK, response.getStatusCode(), "Вернулся не успешный ответ");
+ }
+
+ @Test
+ @DisplayName("При добавлении лайка существующему фильму от несуществующего пользователя должен вернуть ошибку")
+ void test_addLikeByUser_WhenUserNotExists_ShouldThrowsError() {
+ //given && when
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+
+ //then
+ assertThrows(NotFoundException.class, () -> filmController.addLikeByUser(createdFilm.getId(), USER_ID),
+ "Удалось поставить лайк от несуществующего пользователя");
+ }
+
+ @Test
+ @DisplayName("При добавлении лайка несуществующему фильму от существующего пользователя должен вернуть ошибку")
+ void test_addLikeByUser_WhenFilmNotExists_ShouldThrowsError() {
+ //given && when
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+
+ //then
+ assertThrows(NotFoundException.class, () -> filmController.addLikeByUser(FILM_ID, createdUser.getId()),
+ "Удалось поставить лайк несуществующему фильму");
+ }
+
+ @Test
+ @DisplayName("При удалении лайка с существующего фильма от существующего пользователя должен вернуть OK 200")
+ void test_removeLikeByUser_WhenFilmAndUserExist_ShouldReturn200() {
+ //given
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+
+ //when
+ filmController.addLikeByUser(createdFilm.getId(), createdUser.getId());
+ ResponseEntity response = filmController.removeLikeByUser(createdFilm.getId(), createdUser.getId());
+
+ //then
+ assertEquals(HttpStatus.OK, response.getStatusCode(), "Вернулся не успешный ответ");
+ }
+
+ @Test
+ @DisplayName("При удалении лайка с существующего фильма от несуществующего пользователя должен вернуть ошибку")
+ void test_removeLikeByUser_WhenUserNotExists_ShouldThrowsError() {
+ //given && when
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+
+ //then
+ assertThrows(NotFoundException.class, () -> filmController.removeLikeByUser(createdFilm.getId(), USER_ID),
+ "Удалось убрать лайк от несуществующего пользователя");
+ }
+
+ @Test
+ @DisplayName("При удалении лайка с несуществующего фильма от существующего пользователя должен вернуть ошибку")
+ void test_removeLikeByUser_WhenFilmNotExists_ShouldThrowsError() {
+ //given && when
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+
+ //then
+ assertThrows(NotFoundException.class, () -> filmController.removeLikeByUser(FILM_ID, createdUser.getId()),
+ "Удалось убрать лайк с несуществующего фильма");
+ }
+
+ @Test
+ @DisplayName("При запросе одного фильма должен вернуть самый популярный")
+ void test_getMostPopularFilms_WhenRequestOne_ShouldReturnOne() {
+ //given
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film film2 = Film.builder().name(FILM_NAME_2).description(FILM_DESCRIPTION_2).releaseDate(FILM_RELEASE_DATE_2)
+ .duration(FILM_DURATION_2).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+ Film createdFilm2 = filmController.addFilm(film2).getBody();
+
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+ User user2 = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser2 = userController.addUser(user2).getBody();
+
+ //when
+ filmController.addLikeByUser(createdFilm.getId(), createdUser.getId());
+ filmController.addLikeByUser(createdFilm2.getId(), createdUser.getId());
+ filmController.addLikeByUser(createdFilm2.getId(), createdUser2.getId());
+ List popularFilms = filmController.getMostPopularFilms(FILM_COUNT_1).getBody();
+
+ //then
+ assertNotNull(popularFilms, "В контроллере нет популярных фильмов");
+ assertEquals(1, popularFilms.size(), "В контроллере не верное количество фильмов");
+ assertEquals(FILM_NAME_2, popularFilms.get(0).getName(), "В контроллере не корректный фильм");
+ }
+
+ @Test
+ @DisplayName("Если передано дефолтное количество фильмов, то должен вернуть все (до дефолтного включительно)")
+ void test_getMostPopularFilms_WhenRequestNothing_ShouldReturnAll() {
+ //given
+ Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE)
+ .duration(FILM_DURATION).build();
+ Film film2 = Film.builder().name(FILM_NAME_2).description(FILM_DESCRIPTION_2).releaseDate(FILM_RELEASE_DATE_2)
+ .duration(FILM_DURATION_2).build();
+ Film createdFilm = filmController.addFilm(film).getBody();
+ Film createdFilm2 = filmController.addFilm(film2).getBody();
+
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+ User user2 = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser2 = userController.addUser(user2).getBody();
+
+ //when
+ filmController.addLikeByUser(createdFilm.getId(), createdUser.getId());
+ filmController.addLikeByUser(createdFilm2.getId(), createdUser.getId());
+ filmController.addLikeByUser(createdFilm2.getId(), createdUser2.getId());
+ List popularFilms = filmController.getMostPopularFilms(FILM_DEFAULT_COUNT).getBody();
+
+ //then
+ assertNotNull(popularFilms, "В контроллере нет популярных фильмов");
+ assertEquals(2, popularFilms.size(), "В контроллере не верное количество фильмов");
+ assertEquals(FILM_NAME_2, popularFilms.get(0).getName(), "В контроллере не корректный 1-ый фильм");
+ assertEquals(FILM_NAME, popularFilms.get(1).getName(), "В контроллере не корректный 2-ой фильм");
+ }
}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
index 7666f1b..3c76ba9 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
@@ -3,8 +3,14 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.service.UserService;
+import ru.yandex.practicum.filmorate.storage.InMemoryUserStorage;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
import java.time.LocalDate;
import java.time.Month;
@@ -15,24 +21,32 @@
class UserControllerTest {
private static final String USER_NAME = "Вася";
private static final String USER_NAME_2 = "Петя";
+ private static final String USER_NAME_3 = "Коля";
private static final String USER_EMAIL = "mail@mail.ru";
private static final String USER_EMAIL_2 = "newMail@mail.ru";
+ private static final String USER_EMAIL_3 = "newNewMail@mail.ru";
private static final String USER_LOGIN = "user";
private static final String USER_LOGIN_2 = "newUser";
+ private static final String USER_LOGIN_3 = "friendUser";
private static final String USER_LOGIN_INCORRECT = "new User";
private static final LocalDate USER_BIRTHDAY = LocalDate.of(1992, Month.DECEMBER, 12);
private static final LocalDate USER_BIRTHDAY_2 = LocalDate.of(1982, Month.OCTOBER, 22);
+ private static final LocalDate USER_BIRTHDAY_3 = LocalDate.of(1968, Month.NOVEMBER, 2);
private static final LocalDate USER_BIRTHDAY_INCORRECT = LocalDate.of(3035, Month.DECEMBER, 12);
+ private static final Integer USER_ID = 567;
+
UserController userController;
@BeforeEach
@DisplayName("Инициализирует контроллер")
void setUp() {
- userController = new UserController();
+ UserStorage userStorage = new InMemoryUserStorage();
+ UserService userService = new UserService(userStorage);
+ userController = new UserController(userService);
}
@Test
@@ -68,7 +82,7 @@ void test_addUser_WhenCorrectFields_ShouldAddToController() {
List usersByController = userController.getUsers().getBody();
//then
- assertNotNull(usersByController,"В контроллере нет пользователей");
+ assertNotNull(usersByController, "В контроллере нет пользователей");
assertEquals(1, usersByController.size(), "В контроллере не верное количество пользователей");
assertEquals(USER_NAME, usersByController.get(0).getName(), "В контроллере некорректный пользователь");
}
@@ -97,7 +111,7 @@ void test_addUser_WhenNameIsNull_ShouldReturnNameWithLoginValue() {
List usersByController = userController.getUsers().getBody();
//then
- assertNotNull(usersByController,"В контроллере нет пользователей");
+ assertNotNull(usersByController, "В контроллере нет пользователей");
assertEquals(1, usersByController.size(), "В контроллере не верное количество пользователей");
assertEquals(USER_LOGIN, usersByController.get(0).getName(), "Имя пользователя не равно логину");
}
@@ -128,7 +142,7 @@ void test_updateUser_WhenCorrectFields_ShouldUpdateInController() {
List usersByController = userController.getUsers().getBody();
//then
- assertNotNull(usersByController,"В контроллере нет пользователей");
+ assertNotNull(usersByController, "В контроллере нет пользователей");
assertEquals(1, usersByController.size(), "В контроллере не верное количество пользователей");
assertEquals(USER_NAME_2, usersByController.get(0).getName(), "В контроллере некорректный пользователь");
}
@@ -162,7 +176,7 @@ void test_updateUser_WhenNameIsNull_ShouldReturnNameWithLoginValue() {
List usersByController = userController.getUsers().getBody();
//then
- assertNotNull(usersByController,"В контроллере нет пользователей");
+ assertNotNull(usersByController, "В контроллере нет пользователей");
assertEquals(1, usersByController.size(), "В контроллере не верное количество пользователей");
assertEquals(USER_LOGIN, usersByController.get(0).getName(), "Имя пользователя не равно логину");
}
@@ -183,4 +197,170 @@ void test_updateUser_WhenBirthdayInFuture_ShouldThrowsError() {
"Пользователь обновлён с датой рождения в будущем");
}
+ @Test
+ @DisplayName("При получении существующего пользователя по id контроллер должен вернуть его")
+ void test_getUserById_WhenUserExists_ShouldReturnOne() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser = userController.addUser(user).getBody();
+
+ //when
+ User findUser = userController.getUserById(createdUser.getId()).getBody();
+
+ //then
+ assertNotNull(findUser, "В контроллере нет пользователей");
+ assertEquals(USER_NAME, findUser.getName(), "В контроллере некорректный пользователь");
+ }
+
+ @Test
+ @DisplayName("При получении несуществующего пользователя по id контроллер должен выбросить ошибку")
+ void test_getUserById_WhenUserNotExists_ShouldThrowsError() {
+ //given && when && then
+ assertThrows(NotFoundException.class,
+ () -> userController.getUserById(USER_ID),
+ "Из контроллера получен несуществующий пользователь");
+ }
+
+ @Test
+ @DisplayName("При добавлении в друзья существующих пользователей ответ должен быть OK 200")
+ void test_addFriend_WhenUsersExist_ShouldReturn200() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User user2 = User.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2).birthday(USER_BIRTHDAY_2)
+ .build();
+ User createdUser1 = userController.addUser(user).getBody();
+ User createdUser2 = userController.addUser(user2).getBody();
+
+ //when
+ ResponseEntity response = userController.addFriend(createdUser1.getId(), createdUser2.getId());
+
+ //then
+ assertEquals(HttpStatus.OK, response.getStatusCode(),
+ "При добавлении в друзья вернулся не успешный ответ");
+ }
+
+ @Test
+ @DisplayName("При добавлении в друзья несуществующего пользователя контроллер должен выбросить ошибку")
+ void test_addFriend_WhenOneUserNotExists_ShouldThrowsError() {
+ //given && when
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser1 = userController.addUser(user).getBody();
+
+ //then
+ assertThrows(NotFoundException.class,
+ () -> userController.addFriend(createdUser1.getId(), USER_ID),
+ "Удалось добавить в друзья не существующего пользователя");
+ }
+
+ @Test
+ @DisplayName("При удалении из друзей существующих пользователей ответ должен быть OK 200")
+ void test_removeFriend_WhenFriendsExist_ShouldReturn200() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User user2 = User.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2).birthday(USER_BIRTHDAY_2)
+ .build();
+ User createdUser1 = userController.addUser(user).getBody();
+ User createdUser2 = userController.addUser(user2).getBody();
+ userController.addFriend(createdUser1.getId(), createdUser2.getId());
+
+ //when
+ ResponseEntity response = userController.removeFriend(createdUser1.getId(), createdUser2.getId());
+
+ //then
+ assertEquals(HttpStatus.OK, response.getStatusCode(),
+ "При удалении из друзей вернулся не успешный ответ");
+ }
+
+ @Test
+ @DisplayName("При удалении из друзей несуществующего пользователя контроллер должен выбросить ошибку")
+ void test_removeFriend_WhenOneFriendNotExist_ShouldReturn200() {
+ //given && when
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser1 = userController.addUser(user).getBody();
+
+ //then
+ assertThrows(NotFoundException.class,
+ () -> userController.removeFriend(createdUser1.getId(), USER_ID),
+ "Удалось удалить из друзей несуществующего пользователя");
+ }
+
+ @Test
+ @DisplayName("При получении всех друзей пользователя должен возвращаться корректный список")
+ void test_getAllFriends_WhenAddFriends_ShouldReturnOne() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User user2 = User.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2).birthday(USER_BIRTHDAY_2)
+ .build();
+ User createdUser1 = userController.addUser(user).getBody();
+ User createdUser2 = userController.addUser(user2).getBody();
+
+ //when
+ userController.addFriend(createdUser1.getId(), createdUser2.getId());
+ List userFriends = userController.getAllFriends(createdUser1.getId()).getBody();
+
+ //then
+ assertNotNull(userFriends, "У пользователя нет друзей");
+ assertEquals(USER_NAME_2, userFriends.get(0).getName(), "В друзьях некорректный пользователь");
+ }
+
+ @Test
+ @DisplayName("При отсутствии друзей у пользователя должен возвращаться пустой список")
+ void test_getAllFriends_WhenNoFriends_ShouldReturnEmpty() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User createdUser1 = userController.addUser(user).getBody();
+
+ //when
+ List userFriends = userController.getAllFriends(createdUser1.getId()).getBody();
+
+ //then
+ assertNotNull(userFriends, "Список друзей не проинициализировался");
+ assertEquals(0, userFriends.size(), "Список друзей не пустой");
+ }
+
+ @Test
+ @DisplayName("При получении общих друзей пользователей должен возвращаться корректный список")
+ void test_getCommonFriends_WhenAddSameFriend_ShouldReturnOne() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User user2 = User.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2).birthday(USER_BIRTHDAY_2)
+ .build();
+ User user3 = User.builder().name(USER_NAME_3).email(USER_EMAIL_3).login(USER_LOGIN_3).birthday(USER_BIRTHDAY_3)
+ .build();
+ User createdUser1 = userController.addUser(user).getBody();
+ User createdUser2 = userController.addUser(user2).getBody();
+ User createdUser3 = userController.addUser(user3).getBody();
+
+ //when
+ userController.addFriend(createdUser1.getId(), createdUser3.getId());
+ userController.addFriend(createdUser2.getId(), createdUser3.getId());
+ List commonFriends = userController.getCommonFriends(createdUser1.getId(), createdUser2.getId()).getBody();
+
+ //then
+ assertNotNull(commonFriends, "У пользователя нет общих друзей");
+ assertEquals(USER_NAME_3, commonFriends.get(0).getName(), "В общих друзьях некорректный пользователь");
+ }
+
+ @Test
+ @DisplayName("При отсутствии общих друзей у пользователей должен возвращаться пустой список")
+ void test_getCommonFriends_WhenAddNotSameFriend_ShouldReturnEmpty() {
+ //given
+ User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build();
+ User user2 = User.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2).birthday(USER_BIRTHDAY_2)
+ .build();
+ User user3 = User.builder().name(USER_NAME_3).email(USER_EMAIL_3).login(USER_LOGIN_3).birthday(USER_BIRTHDAY_3)
+ .build();
+ User createdUser1 = userController.addUser(user).getBody();
+ User createdUser2 = userController.addUser(user2).getBody();
+ User createdUser3 = userController.addUser(user3).getBody();
+
+ //when
+ userController.addFriend(createdUser1.getId(), createdUser1.getId());
+ userController.addFriend(createdUser2.getId(), createdUser3.getId());
+ List commonFriends = userController.getCommonFriends(createdUser1.getId(), createdUser2.getId()).getBody();
+
+ //then
+ assertNotNull(commonFriends, "Список общих друзей не проинициализировался");
+ assertEquals(0, commonFriends.size(), "Список общих друзей не пустой");
+ }
}
\ No newline at end of file