diff --git a/pom.xml b/pom.xml
index 75a8346..23e262f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,11 @@
junit-jupiter-api
test
+
+ org.zalando
+ logbook-spring-boot-starter
+ 3.7.2
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
index dca451b..8f860a2 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
@@ -8,5 +8,4 @@ public class FilmorateApplication {
public static void main(String[] args) {
SpringApplication.run(FilmorateApplication.class, args);
}
-
-}
+}
\ 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 8e4ee87..766fff2 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -3,72 +3,59 @@
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
-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.service.FilmService;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
@Slf4j
@RestController
@RequestMapping("/films")
public class FilmController {
- private final Map films = new HashMap<>();
+ private final FilmStorage filmStorage;
+ private final FilmService filmService;
+
+ public FilmController(FilmStorage filmStorage, FilmService filmService) {
+ this.filmStorage = filmStorage;
+ this.filmService = filmService;
+ }
@GetMapping
public Collection findAll() {
- return films.values();
+ return filmStorage.findAll();
}
- @PostMapping
- public Film create(@Valid @RequestBody Film film) {
- log.info("Попытка добавления нового фильма в коллекцию: {}", film.getName());
- if (film.getId() != null && films.containsKey(film.getId())) {
- log.error("Ошибка добавления: Фильм уже имеется в коллекции");
- throw new ValidationException("Фильм уже имеется в коллекции");
- }
- film.setId(getNextId());
- films.put(film.getId(), film);
- log.info("Фильм: '{}', успешно добавлен в коллекцию", film.getName());
- return film;
+ @GetMapping("/{id}")
+ public Film getFilmById(@PathVariable Long id) {
+ return filmStorage.getFilmById(id);
}
- @PutMapping
- public Film update(@Valid @RequestBody Film filmWithNewData) {
- if (filmWithNewData.getId() == null || filmWithNewData.getId() == 0) {
- throw new ValidationException("id должен быть указан");
- }
- log.info("Попытка обновления данных о фильме: '{}'", filmWithNewData.getName());
+ @PutMapping("/{id}/like/{userId}")
+ public void addLike(@PathVariable Long id,
+ @PathVariable Long userId) {
+ filmService.addLike(id, userId);
+ }
- Film filmWithOldData = films.get(filmWithNewData.getId());
- if (filmWithOldData == null) {
- log.error("Ошибка обновления: фильм не найден");
- throw new NotFoundException("Фильм не найден");
- }
- // меняем релиз-дату
- if (filmWithNewData.isValidReleaseDate()) {
- filmWithOldData.setReleaseDate(filmWithNewData.getReleaseDate());
- }
- // меняем название
- filmWithOldData.setName(filmWithNewData.getName());
+ @DeleteMapping("/{id}/like/{userId}")
+ public void deleteLike(@PathVariable Long id,
+ @PathVariable Long userId) {
+ filmService.deleteLike(id, userId);
+ }
- //меняем описание
- filmWithOldData.setDescription(filmWithNewData.getDescription());
+ @PostMapping
+ public Film create(@Valid @RequestBody Film film) {
+ return filmStorage.create(film);
+ }
- //меняем продолжительность фильма
- filmWithOldData.setDuration(filmWithNewData.getDuration());
- log.info("Данные о фильме '{}' успешно обновлены", filmWithOldData.getName());
- return filmWithOldData;
+ @GetMapping("/popular")
+ public List getTopFilms(@RequestParam(name = "count", defaultValue = "10") int count) {
+ return filmService.getTopFilms(count);
}
- private long getNextId() {
- long currentMaxId = films.keySet()
- .stream()
- .mapToLong(id -> id)
- .max()
- .orElse(0);
- return ++currentMaxId;
+ @PutMapping
+ public Film update(@Valid @RequestBody Film filmWithNewData) {
+ return filmStorage.update(filmWithNewData);
}
-}
+}
\ 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 155657b..954ba1c 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -2,104 +2,68 @@
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
-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.UserStorage;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
@Slf4j
@RestController
@RequestMapping("/users")
public class UserController {
- // для пользователей по id
- private final Map users = new HashMap<>();
- // для быстрой проверки уникальности email
- private final Map usersByEmail = new HashMap<>();
- // для быстрой проверки уникальности login
- private final Map usersByLogin = new HashMap<>();
+private final UserStorage userStorage;
+private final UserService userService;
+ public UserController(UserStorage userStorage, UserService userService) {
+ this.userStorage = userStorage;
+ this.userService = userService;
+ }
@GetMapping
public Collection findAll() {
- return users.values();
+ return userStorage.findAll();
}
- @PostMapping
- public User create(@Valid @RequestBody User user) {
- log.info("Попытка создания пользователя с email: {} и login: {}", user.getEmail(), user.getLogin());
- if (users.containsKey(user.getId())) {
- log.error("Ошибка создания пользователя: id {} уже используется", user.getId());
- throw new ValidationException("Пользователь уже зарегистрирован");
- }
- if (usersByEmail.containsKey(user.getEmail())) {
- log.error("Ошибка создания пользователя: email {} уже используется", user.getEmail());
- throw new ValidationException("Данный email уже используется");
- }
- if (usersByLogin.containsKey(user.getLogin())) {
- log.error("Ошибка создания пользователя: login {} уже используется", user.getLogin());
- throw new ValidationException("Данный логин занят");
- }
- user.setId(getNextId());
- users.put(user.getId(), user);
- usersByEmail.put(user.getEmail(), user);
- usersByLogin.put(user.getLogin(), user);
- log.info("Пользователь успешно создан с id: {}, email: {}, login: {}",
- user.getId(), user.getEmail(), user.getLogin());
- return user;
+ @GetMapping("/{id}")
+ public User getUserById(@PathVariable Long id) {
+ return userStorage.getUserById(id);
}
- @PutMapping
- public User update(@Valid @RequestBody User userWithNewData) {
- if (userWithNewData.getId() == null || userWithNewData.getId() == 0) {
- throw new ValidationException("id должен быть указан");
- }
- log.info("Попытка обновления пользователя с id: {}", userWithNewData.getId());
-
- User userWithOldData = users.get(userWithNewData.getId());
- if (userWithOldData == null) {
- log.error("Пользователь не найден");
- throw new NotFoundException("Пользователь не найден");
- }
- if (!userWithNewData.getEmail().equals(userWithOldData.getEmail()) &&
- usersByEmail.containsKey(userWithNewData.getEmail())) {
- log.error("Ошибка обновления: email {} уже используется", userWithNewData.getEmail());
- throw new ValidationException("Этот e-mail уже используется");
- }
+ @PutMapping("/{id}/friends/{friendId}")
+ public void addFriend(@PathVariable Long id,
+ @PathVariable Long friendId) {
+ userService.addFriend(id, friendId);
+ }
- // обновляем e-mail
- usersByEmail.remove(userWithOldData.getEmail());
- userWithOldData.setEmail(userWithNewData.getEmail());
- usersByEmail.put(userWithOldData.getEmail(), userWithOldData);
+ @DeleteMapping("/{id}/friends/{friendId}")
+ public void deleteFriend(@PathVariable Long id,
+ @PathVariable Long friendId) {
+ userService.deleteFriend(id, friendId);
+ }
- // обновляем логин
- if (!userWithNewData.getLogin().equals(userWithOldData.getLogin()) &&
- usersByLogin.containsKey(userWithNewData.getLogin())) {
- log.error("Ошибка обновления: login {} уже используется", userWithNewData.getLogin());
- throw new ValidationException("Этот логин уже используется");
- }
- usersByLogin.remove(userWithOldData.getLogin());
- userWithOldData.setLogin(userWithNewData.getLogin());
- usersByLogin.put(userWithOldData.getLogin(),userWithOldData);
+ @GetMapping("/{id}/friends")
+ public List getUserFriends(@PathVariable Long id) {
+ return userService.getFriends(id);
+ }
- // обновляем имя
- userWithOldData.setName(userWithNewData.getName());
+ @GetMapping("/{id}/friends/common/{friendId}")
+ public List getCommonFriends(@PathVariable Long id,
+ @PathVariable Long friendId) {
+ return userService.getCommonFriends(id, friendId);
+ }
- // обновляем дату рождения
- userWithOldData.setBirthday(userWithNewData.getBirthday());
- log.info("Пользователь с id {} успешно обновлён", userWithOldData.getId());
- return userWithOldData;
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public User create(@Valid @RequestBody User user) {
+ return userStorage.create(user);
}
- private long getNextId() {
- long currentMaxId = users.keySet()
- .stream()
- .mapToLong(id -> id)
- .max()
- .orElse(0);
- return ++currentMaxId;
+ @PutMapping
+ public User update(@Valid @RequestBody User userWithNewData) {
+ return userStorage.update(userWithNewData);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ConditionsNotMetException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ConditionsNotMetException.java
new file mode 100644
index 0000000..c653ae4
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ConditionsNotMetException.java
@@ -0,0 +1,7 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class ConditionsNotMetException extends RuntimeException {
+ public ConditionsNotMetException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/handler/GlobalExceptionHandler.java b/src/main/java/ru/yandex/practicum/filmorate/exception/handler/GlobalExceptionHandler.java
index 845f060..f56b3fa 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/exception/handler/GlobalExceptionHandler.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/handler/GlobalExceptionHandler.java
@@ -3,6 +3,7 @@
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import ru.yandex.practicum.filmorate.dto.ErrorResponse;
@@ -26,8 +27,9 @@ public ResponseEntity handleNotFound(NotFoundException e, HttpSer
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}
- @ExceptionHandler(ValidationException.class)
- public ResponseEntity handleValidation(ValidationException e, HttpServletRequest request) {
+ @ExceptionHandler({ValidationException.class, MethodArgumentNotValidException.class})
+ public ResponseEntity handleValidation(
+ ValidationException e, HttpServletRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.BAD_REQUEST.value(),
@@ -49,4 +51,4 @@ public ResponseEntity handleOtherExceptions(Exception e, HttpServ
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}
-}
+}
\ 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 4b61a5a..987b732 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -2,12 +2,25 @@
import jakarta.validation.constraints.*;
import lombok.Data;
+import lombok.RequiredArgsConstructor;
import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Set;
@Data
+@RequiredArgsConstructor
public class Film {
private Long id;
+ private Set likes = new HashSet<>();
+
+ public Film(Long id, LocalDate releaseDate, String name, String description, int duration) {
+ this.id = id;
+ this.releaseDate = releaseDate;
+ this.name = name;
+ this.description = description;
+ this.duration = duration;
+ }
@NotNull
private LocalDate releaseDate;
@@ -28,4 +41,4 @@ public boolean isValidReleaseDate() {
LocalDate minReleaseDate = LocalDate.of(1895,12,28);
return !releaseDate.isBefore(minReleaseDate);
}
-}
+}
\ 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 5d62895..1918db7 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/User.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -4,14 +4,17 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.*;
import lombok.Data;
-import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Set;
@Data
-@NoArgsConstructor
+@RequiredArgsConstructor
public class User {
private Long id;
+ private Set friends = new HashSet<>();
@NotBlank(message = "Электронная почта не может быть пустой")
@Email(message = "Электронная почта должна быть корректной и содержать символ '@'")
@@ -43,4 +46,4 @@ public User(@JsonProperty("id") Long id,
public void setName(String name) {
this.name = (name == null || name.isBlank()) ? this.login : name;
}
-}
+}
\ 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..9a6435a
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -0,0 +1,81 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.extern.slf4j.Slf4j;
+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.Film;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.util.Comparator;
+import java.util.List;
+
+@Slf4j
+@Service
+public class FilmService {
+ private final FilmStorage filmStorage;
+ private final UserStorage userStorage;
+
+ @Autowired
+ public FilmService(FilmStorage filmStorage, UserStorage userStorage) {
+ this.filmStorage = filmStorage;
+ this.userStorage = userStorage;
+ }
+
+ public void addLike(Long filmId, Long userId) {
+ log.debug("Попытка добавить лайк фильму {} от пользователя {}.", filmId, userId);
+ validateFilmAndUserExist(filmId, userId);
+ Film film = filmStorage.getFilmById(filmId);
+
+ if (!film.getLikes().add(userId)) {
+ log.error("Пользователь с id {} уже добавил лайк фильму {}.", userId, filmId);
+ throw new ValidationException("Данный пользователь уже добавлял лайк этому фильму!");
+ }
+ log.info("Пользователь {} поставил лайк фильму {}.", userId, filmId);
+ }
+
+ public void deleteLike(Long filmId, Long userId) {
+ log.debug("Попытка удалить лайк у фильма {} от пользователя {}.", filmId, userId);
+ validateFilmAndUserExist(filmId, userId);
+ Film film = filmStorage.getFilmById(filmId);
+
+ if (!film.getLikes().remove(userId)) {
+ log.error("Пользователь с id {} не ставил лайк фильму {}.", userId, filmId);
+ throw new ValidationException("Этот пользователь не ставил лайк данному фильму");
+ }
+ log.info("Пользователь {} удалил лайк у фильма {}.", userId, filmId);
+ }
+
+ public List getTopFilms(int count) {
+ log.debug("Запрос на получение топ фильмов");
+ List topFilms = filmStorage.findAll().stream()
+ .sorted(Comparator.comparingInt(film -> -film.getLikes().size()))
+ .limit(count)
+ .toList();
+ log.info("Выведен список из {} топ фильмов", topFilms.size());
+ return topFilms;
+ }
+
+ public void validateFilmAndUserExist(Long filmId, Long userId) {
+ log.trace("Валидация существования фильма с id {} и пользователя с id {}", filmId, userId);
+ if (filmId == null || userId == null) {
+ log.error("Оба идентификатора (filmId и userId) должны быть указаны");
+ throw new ValidationException("Id должен быть указан");
+ }
+ try {
+ filmStorage.getFilmById(filmId);
+ } catch (NotFoundException e) {
+ log.error("Фильм с id {} не найден.", filmId);
+ throw e;
+ }
+ try {
+ userStorage.getUserById(userId);
+ } catch (NotFoundException e) {
+ log.error("Пользователь с id {} не найден.", userId);
+ throw e;
+ }
+ log.trace("Валидация успешна для фильма с id {} и пользователя с id {}", filmId, userId);
+ }
+}
\ No newline at end of file
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..240dbdc
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -0,0 +1,104 @@
+package ru.yandex.practicum.filmorate.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.ConditionsNotMetException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Slf4j
+@Service
+public class UserService {
+ private final UserStorage userStorage;
+
+ @Autowired
+ public UserService(UserStorage userStorage) {
+ this.userStorage = userStorage;
+ }
+
+ public void addFriend(Long id, Long friendId) {
+ log.debug("Попытка пользователя {} добавить в друзья пользователя {}", id, friendId);
+ User[] users = validateAndGetUsers(id, friendId);
+ User user1 = users[0];
+ User user2 = users[1];
+
+ if (user1.getFriends().contains(friendId)) {
+ log.error("Пользователь с id {} уже имеется в друзьях у пользователя {}", friendId, id);
+ throw new ValidationException("Данный друг уже имеется в списке друзей!");
+ }
+ user1.getFriends().add(friendId);
+ user2.getFriends().add(id);
+
+ log.info("Пользователь {} успешно добавил в друзья пользователя {}", id, friendId);
+ }
+
+ public List getFriends(Long id) {
+ log.debug("Попытка получить список друзей пользователя с id {}", id);
+ User user = userStorage.getUserById(id);
+ Set friendIds = user.getFriends();
+
+ log.info("Получен список друзей пользователя с id {}", id);
+ return friendIds.stream()
+ .map(userStorage::getUserById)
+ .toList();
+ }
+
+ public List getCommonFriends(Long id, Long friendsId) {
+ log.debug("Запрос на получение списка общих друзей");
+ User[] users = validateAndGetUsers(id, friendsId);
+ User user1 = users[0];
+ User user2 = users[1];
+
+ Set friendIdsUser1 = user1.getFriends();
+ Set friendsIdsUser2 = user2.getFriends();
+
+ Set commonFriends = new HashSet<>(friendIdsUser1);
+ commonFriends.retainAll(friendsIdsUser2);
+
+ log.info("Выведен список общих друзей");
+ return commonFriends.stream()
+ .map(userStorage::getUserById)
+ .toList();
+ }
+
+ public void deleteFriend(Long id, Long friendsId) {
+ log.debug("Попытка пользователя {} удалить из друзей пользователя {}", id, friendsId);
+ User[] users = validateAndGetUsers(id, friendsId);
+ User user1 = users[0];
+ User user2 = users[1];
+
+ if (!user1.getFriends().contains(friendsId)) {
+ log.warn("Пользователь с id {} отсутствует в списке друзей у пользователя {}", friendsId, id);
+ return;
+ }
+
+ user1.getFriends().remove(friendsId);
+ user2.getFriends().remove(id);
+
+ log.info("Пользователь {} успешно удалил из друзей пользователя {}", id, friendsId);
+ }
+
+ private User[] validateAndGetUsers(Long id, Long friendsId) {
+ log.trace("Валидация существования пользователей с id {} и {}", id, friendsId);
+ if (id == null || friendsId == null) {
+ log.error("Отсутствует информация об ID");
+ throw new ConditionsNotMetException("Id должен быть указан");
+ }
+ if (id.equals(friendsId)) {
+ log.error("ID совпадают, неверно указаны идентификаторы пользователей");
+ throw new ValidationException("Личный id, и id друга совпадают. Неверный ввод данных");
+ }
+
+ User user1 = userStorage.getUserById(id);
+ User user2 = userStorage.getUserById(friendsId);
+
+ log.trace("Валидация пользователей с ID {} и {} прошла успешно", id, friendsId);
+ return new User[]{user1, user2};
+ }
+}
\ No newline at end of file
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..0ab484c
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
@@ -0,0 +1,18 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.util.Collection;
+
+public interface FilmStorage {
+
+ Collection findAll();
+
+ Film getFilmById(Long id);
+
+ Film create(Film film);
+
+ Film update(Film filmWithNewData);
+
+ long getNextId();
+}
\ No newline at end of file
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..a057377
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
@@ -0,0 +1,92 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class InMemoryFilmStorage implements FilmStorage {
+ private final Map films = new HashMap<>();
+
+ @Override
+ public Collection findAll() {
+ return films.values();
+ }
+
+ @Override
+ public Film getFilmById(Long id) {
+ log.debug("Попытка получить фильм с id {}.", id);
+ Film film = films.get(id);
+ if (film == null) {
+ log.error("Фильм с id {} не найден.", id);
+ throw new NotFoundException("Фильм с id " + id + " не найден.");
+ }
+ log.info("Получен фильм с id {}.", id);
+ return film;
+ }
+
+ @Override
+ public Film create(Film film) {
+ log.info("Попытка добавления нового фильма в коллекцию: {}", film.getName());
+ if (film.getId() != null && films.containsKey(film.getId())) {
+ log.error("Ошибка добавления: Фильм уже имеется в коллекции");
+ throw new ValidationException("Фильм уже имеется в коллекции");
+ }
+ film.setId(getNextId());
+ films.put(film.getId(), film);
+ log.info("Фильм: '{}', успешно добавлен в коллекцию", film.getName());
+ return film;
+ }
+
+ @Override
+ public Film update(Film filmWithNewData) {
+ if (filmWithNewData.getId() == null || filmWithNewData.getId() == 0) {
+ throw new ValidationException("id должен быть указан");
+ }
+ log.info("Попытка обновления данных о фильме: '{}'", filmWithNewData.getName());
+
+ Film filmWithOldData = films.get(filmWithNewData.getId());
+ if (filmWithOldData == null) {
+ log.error("Ошибка обновления: фильм не найден");
+ throw new NotFoundException("Фильм не найден");
+ }
+
+ Film updateFilm = new Film(
+ filmWithNewData.getId(),
+ filmWithNewData.getReleaseDate(),
+ filmWithNewData.getName(),
+ filmWithNewData.getDescription(),
+ filmWithNewData.getDuration()
+ );
+ if (!updateFilm.isValidReleaseDate()) {
+ throw new ValidationException("Дата релиза должна быть не раньше 28 декабря 1895 года");
+ }
+ if (filmWithNewData.getLikes() != null && !filmWithNewData.getLikes().isEmpty()) {
+ updateFilm.setLikes(new HashSet<>(filmWithNewData.getLikes()));
+ } else {
+ updateFilm.setLikes(filmWithOldData.getLikes());
+ }
+ films.remove(filmWithOldData.getId(), filmWithOldData);
+ films.put(updateFilm.getId(), updateFilm);
+ log.info("Данные о фильме '{}' успешно обновлены", filmWithOldData.getName());
+ return updateFilm;
+ }
+
+ @Override
+ public long getNextId() {
+ long currentMaxId = films.keySet()
+ .stream()
+ .mapToLong(id -> id)
+ .max()
+ .orElse(0);
+ return ++currentMaxId;
+ }
+}
\ No newline at end of file
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..2e39cfc
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
@@ -0,0 +1,130 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.*;
+
+@Slf4j
+@Component
+public class InMemoryUserStorage implements UserStorage {
+ // для пользователей по id
+ private final Map users = new HashMap<>();
+ // для быстрой проверки уникальности email
+ private final Map usersByEmail = new HashMap<>();
+ // для быстрой проверки уникальности login
+ private final Map usersByLogin = new HashMap<>();
+
+ @Override
+ public Collection findAll() {
+ return users.values();
+ }
+
+ @Override
+ public User getUserById(Long id) {
+ log.debug("Попытка полючить пользователя с id {}.", id);
+ User user = users.get(id);
+ if (user == null) {
+ log.error("Пользователь с id {} не найден.", id);
+ throw new NotFoundException("Пользователь с id " + id + " не найден.");
+ }
+ log.info("Пользователь с id {} получен.", id);
+ return user;
+ }
+
+ @Override
+ public User create(User user) {
+ log.info("Попытка создания пользователя с email: {} и login: {}", user.getEmail(), user.getLogin());
+ if (users.containsKey(user.getId())) {
+ log.error("Ошибка создания пользователя: id {} уже используется", user.getId());
+ throw new ValidationException("Пользователь уже зарегистрирован");
+ }
+ if (usersByEmail.containsKey(user.getEmail())) {
+ log.error("Ошибка создания пользователя: email {} уже используется", user.getEmail());
+ throw new ValidationException("Данный email уже используется");
+ }
+ if (usersByLogin.containsKey(user.getLogin())) {
+ log.error("Ошибка создания пользователя: login {} уже используется", user.getLogin());
+ throw new ValidationException("Данный логин занят");
+ }
+ user.setId(getNextId());
+ users.put(user.getId(), user);
+ usersByEmail.put(user.getEmail(), user);
+ usersByLogin.put(user.getLogin(), user);
+
+ log.info("Пользователь успешно создан с id: {}, email: {}, login: {}",
+ user.getId(), user.getEmail(), user.getLogin());
+ return user;
+ }
+
+ @Override
+ public User update(User userWithNewData) {
+ log.info("Попытка обновления пользователя с id: {}", userWithNewData.getId());
+
+ if (userWithNewData.getId() == null || userWithNewData.getId() == 0) {
+ throw new ValidationException("id должен быть указан");
+ }
+ User userWithOldData = users.get(userWithNewData.getId());
+ if (userWithOldData == null) {
+ log.error("Пользователь не найден");
+ throw new NotFoundException("Пользователь не найден");
+ }
+ // проверяем доступность email
+ if (!userWithNewData.getEmail().equals(userWithOldData.getEmail())) {
+ if (usersByEmail.containsKey(userWithNewData.getEmail())) {
+ User existingUserByEmail = usersByEmail.get(userWithNewData.getEmail());
+ if (!existingUserByEmail.getId().equals(userWithNewData.getId())) {
+ log.error("Ошибка обновления: email {} уже используется", userWithNewData.getEmail());
+ throw new ValidationException("Этот e-mail уже используется");
+ }
+ }
+ }
+
+ // проверяем доступность логина
+ if (!userWithNewData.getLogin().equals(userWithOldData.getLogin())) {
+ if (usersByLogin.containsKey(userWithNewData.getLogin())) {
+ User existingUserByLogin = usersByLogin.get(userWithNewData.getLogin());
+ if (!existingUserByLogin.getId().equals(userWithNewData.getId())) {
+ log.error("Ошибка обновления: login {} уже используется", userWithNewData.getLogin());
+ throw new ValidationException("Этот логин уже используется");
+ }
+ }
+ }
+
+ User updateUser = new User(
+ userWithNewData.getId(),
+ userWithNewData.getEmail(),
+ userWithNewData.getLogin(),
+ userWithNewData.getName(),
+ userWithNewData.getBirthday()
+ );
+ if (userWithNewData.getFriends() != null && !userWithNewData.getFriends().isEmpty()) {
+ updateUser.setFriends(new HashSet<>(userWithNewData.getFriends()));
+ } else {
+ updateUser.setFriends(userWithOldData.getFriends());
+ }
+ // удаляем старого пользователя и возвращаем нового
+ users.remove(userWithOldData.getId());
+ usersByEmail.remove(userWithOldData.getEmail());
+ usersByLogin.remove(userWithOldData.getLogin());
+ users.put(userWithNewData.getId(), userWithNewData);
+ usersByLogin.put(userWithNewData.getLogin(), userWithNewData);
+ usersByEmail.put(userWithNewData.getEmail(), userWithNewData);
+
+ log.info("Пользователь с id {} успешно обновлён", userWithOldData.getId());
+ return updateUser;
+ }
+
+ @Override
+ public long getNextId() {
+ long currentMaxId = users.keySet()
+ .stream()
+ .mapToLong(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..4b011ae
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
@@ -0,0 +1,18 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.util.Collection;
+
+public interface UserStorage {
+
+ Collection findAll();
+
+ User getUserById(Long id);
+
+ User create(User user);
+
+ User update(User userWithNewData);
+
+ long getNextId();
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 4c00e40..36f6ba6 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,2 @@
server.port=8080
+logging.level.org.zalando.logbook: TRACE
diff --git a/src/test/java/ru/yandex/practicum/filmorate/service/FilmServiceTest.java b/src/test/java/ru/yandex/practicum/filmorate/service/FilmServiceTest.java
new file mode 100644
index 0000000..e058d5e
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/service/FilmServiceTest.java
@@ -0,0 +1,78 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.User;
+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.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class FilmServiceTest {
+ private FilmStorage filmStorage;
+ private FilmService filmService;
+ private UserStorage userStorage;
+
+ @BeforeEach
+ void setUp() {
+ filmStorage = new InMemoryFilmStorage();
+ userStorage = new InMemoryUserStorage();
+ filmService = new FilmService(filmStorage, userStorage);
+ }
+
+ @Test
+ void addLike() {
+ Film film = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test", "description", 120);
+
+ User user = userStorage.create(new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997,1,26)));
+ filmStorage.create(film);
+ filmService.addLike(film.getId(), user.getId());
+
+ assertEquals(1, filmStorage.getFilmById(film.getId()).getLikes().size());
+ }
+
+ @Test
+ void deleteLike() {
+ Film film = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test", "description", 120);
+
+ User user = userStorage.create(new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997,1,26)));
+ filmStorage.create(film);
+ filmService.addLike(film.getId(), user.getId());
+ filmService.deleteLike(film.getId(), user.getId());
+
+ assertEquals(0, filmStorage.getFilmById(film.getId()).getLikes().size());
+
+ }
+
+ @Test
+ void getTopFilms() {
+ Film film1 = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test1", "description1", 120);
+ Film film2 = new Film(2L, LocalDate.of(1997, 1, 26),
+ "test2", "description2", 120);
+
+ User user = userStorage.create(new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997,1,26)));
+
+ filmStorage.create(film1);
+ filmStorage.create(film2);
+
+ filmService.addLike(film1.getId(), user.getId());
+
+ List topFilms = filmService.getTopFilms(2);
+
+ assertEquals(2, topFilms.size());
+ assertEquals("test1", topFilms.getFirst().getName());
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/service/UserServiceTest.java b/src/test/java/ru/yandex/practicum/filmorate/service/UserServiceTest.java
new file mode 100644
index 0000000..9c510ee
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/service/UserServiceTest.java
@@ -0,0 +1,86 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.InMemoryUserStorage;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+class UserServiceTest {
+ private UserService userService;
+ private UserStorage userStorage;
+
+ @BeforeEach
+ void setup() {
+ userStorage = new InMemoryUserStorage() {
+ };
+ userService = new UserService(userStorage);
+ }
+
+ @Test
+ void shouldAddFriend() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 27));
+ userStorage.create(user1);
+ userStorage.create(user2);
+
+ userService.addFriend(1L, 2L);
+ assertTrue(user1.getFriends().contains(2L));
+ assertTrue(user2.getFriends().contains(1L));
+ }
+
+ @Test
+ void shouldGetFriends() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ userStorage.create(user1);
+ User theSameUser = userStorage.getUserById(1L);
+ assertEquals(user1, theSameUser);
+ }
+
+ @Test
+ void getCommonFriends() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 27));
+ User user3 = new User(3L, "user3@email.com", "user3", "user3",
+ LocalDate.of(1997, 01, 28));
+ userStorage.create(user1);
+ userStorage.create(user2);
+ userStorage.create(user3);
+
+ userService.addFriend(user1.getId(), user3.getId());
+ userService.addFriend(user2.getId(), user3.getId());
+
+ List commonFriends = userService.getCommonFriends(user1.getId(), user2.getId());
+ assertTrue(commonFriends.contains(user3));
+ }
+
+ @Test
+ void deleteFriend() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 27));
+
+ userStorage.create(user1);
+ userStorage.create(user2);
+
+ userService.addFriend(user1.getId(), user2.getId());
+ userService.deleteFriend(user1.getId(), user2.getId());
+
+ assertFalse(user1.getFriends().contains(user2.getId()));
+ assertFalse(user2.getFriends().contains(user1.getId()));
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorageTest.java
new file mode 100644
index 0000000..f475804
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorageTest.java
@@ -0,0 +1,80 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class InMemoryFilmStorageTest {
+private FilmStorage filmStorage;
+
+@BeforeEach
+ void setUp() {
+ filmStorage = new InMemoryFilmStorage() {
+ };
+ }
+
+
+ @Test
+ void findAll() {
+ Film film1 = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test1", "description1", 120);
+ Film film2 = new Film(2L, LocalDate.of(1997, 1, 26),
+ "test2", "description2", 120);
+ filmStorage.create(film1);
+ filmStorage.create(film2);
+
+ assertEquals(2, filmStorage.findAll().size());
+ assertEquals("test1", filmStorage.getFilmById(1L).getName());
+ assertEquals("test2", filmStorage.getFilmById(2L).getName());
+ }
+
+ @Test
+ void getFilmById() {
+ Film film1 = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test1", "description1", 120);
+ filmStorage.create(film1);
+
+ assertEquals(film1, filmStorage.getFilmById(1L));
+ }
+
+ @Test
+ void getFilmByIdShouldThrowException() {
+ Exception exception = assertThrows(NotFoundException.class, () -> {
+ filmStorage.getFilmById(1L);
+ });
+ assertEquals("Фильм с id 1 не найден.", exception.getMessage());
+ }
+
+ @Test
+ void create() {
+ Film film1 = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test1", "description1", 120);
+ filmStorage.create(film1);
+
+ assertEquals(1, filmStorage.findAll().size());
+ assertEquals("test1", filmStorage.getFilmById(1L).getName());
+ }
+
+ @Test
+ void update() {
+ Film film1 = new Film(1L, LocalDate.of(1997, 1, 26),
+ "test1", "description1", 120);
+
+ filmStorage.create(film1);
+
+ Film updateFilm1 = new Film(1L, LocalDate.of(1997, 1, 26),
+ "updateFilm1", "updateDescription1", 130);
+
+
+ filmStorage.update(updateFilm1);
+
+ assertEquals("updateFilm1", filmStorage.getFilmById(1L).getName());
+ assertEquals("updateDescription1", filmStorage.getFilmById(1L).getDescription());
+ assertEquals(130, filmStorage.getFilmById(1L).getDuration());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorageTest.java
new file mode 100644
index 0000000..81c5267
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorageTest.java
@@ -0,0 +1,143 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class InMemoryUserStorageTest {
+ private UserStorage userStorage;
+
+
+ @BeforeEach
+ void setUp() {
+ userStorage = new InMemoryUserStorage();
+ }
+
+ @Test
+ void findAll() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 27));
+ userStorage.create(user1);
+ userStorage.create(user2);
+
+ assertEquals(2, userStorage.findAll().size());
+ }
+
+ @Test
+ void getUserById() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+
+ userStorage.create(user1);
+ assertEquals(user1, userStorage.getUserById(user1.getId()));
+ }
+
+ @Test
+ void getUserByIdShouldThrowException() {
+ Exception exception = assertThrows(NotFoundException.class, () -> {
+ userStorage.getUserById(1L);
+ });
+ assertEquals("Пользователь с id 1 не найден.", exception.getMessage());
+ }
+
+ @Test
+ void create() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 27));
+ userStorage.create(user1);
+ userStorage.create(user2);
+
+ assertEquals(user1, userStorage.getUserById(1L));
+ assertEquals(user2, userStorage.getUserById(2L));
+ assertEquals("user1", userStorage.getUserById(1L).getName());
+ }
+
+ @Test
+ void update() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User updateUser1 = new User(1L, "user1new@email.com", "newLogin1", "newUser1",
+ LocalDate.of(1997, 01, 26));
+
+ userStorage.create(user1);
+ userStorage.update(updateUser1);
+
+ assertEquals("newUser1", userStorage.getUserById(user1.getId()).getName());
+ assertEquals("newLogin1", userStorage.getUserById(user1.getId()).getLogin());
+ }
+
+ @Test
+ void createUserWithExistLogin() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+
+ User user2 = new User(2L, "user2@email.com", "user1", "user2",
+ LocalDate.of(1997, 01, 26));
+
+ userStorage.create(user1);
+ Exception exception = assertThrows(ValidationException.class, () -> {
+ userStorage.create(user2);
+ });
+ assertEquals("Данный логин занят", exception.getMessage());
+ }
+
+ @Test
+ void createUserWithExistEmail() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user1@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 26));
+
+ userStorage.create(user1);
+ Exception exception = assertThrows(ValidationException.class, () -> {
+ userStorage.create(user2);
+ });
+ assertEquals("Данный email уже используется", exception.getMessage());
+ }
+
+ @Test
+ void updateUserWithExistLogin() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 26));
+ User updateUser1 = new User(1L, "user1new@email.com", "user2", "user1",
+ LocalDate.of(1997, 01, 26));
+
+ userStorage.create(user1);
+ userStorage.create(user2);
+
+ Exception exception = assertThrows(ValidationException.class, () -> {
+ userStorage.update(updateUser1);
+ });
+ assertEquals("Этот логин уже используется", exception.getMessage());
+ }
+
+ @Test
+ void updateUserWithExistEmail() {
+ User user1 = new User(1L, "user1@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+ User user2 = new User(2L, "user2@email.com", "user2", "user2",
+ LocalDate.of(1997, 01, 26));
+ User updateUser1 = new User(1L, "user2@email.com", "user1", "user1",
+ LocalDate.of(1997, 01, 26));
+
+ userStorage.create(user1);
+ userStorage.create(user2);
+
+ Exception exception = assertThrows(ValidationException.class, () -> {
+ userStorage.update(updateUser1);
+ });
+ assertEquals("Этот e-mail уже используется", exception.getMessage());
+ }
+}
\ No newline at end of file