From c0c0998f302857bf2645f50da403ac0eab2f2fea Mon Sep 17 00:00:00 2001 From: Ksenia Date: Thu, 8 Oct 2026 20:47:03 +0300 Subject: [PATCH 1/2] feat: add delete endpoints for films and users --- README.md | 9 ++++-- .../filmorate/controller/FilmController.java | 6 ++++ .../filmorate/controller/UserController.java | 6 ++++ .../filmorate/service/FilmService.java | 6 ++++ .../filmorate/service/UserService.java | 6 ++++ .../storage/film/FilmDbStorageTest.java | 31 +++++++++++++++++++ .../storage/user/UserDbStorageTest.java | 20 ++++++++++++ 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 50b4159..aec40da 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## Ключевые возможности на данном этапе -- CRUD-операции для `Film` и `User` (создание, обновление, получение по ID и списком). +- CRUD-операции для `Film` и `User` (создание, обновление, удаление, получение по ID и списком). - Хранение данных в базе H2 (файловый режим — данные переживают перезапуск), доступ через `JdbcTemplate` и `RowMapper`. - Справочники жанров и рейтингов MPA в базе, эндпоинты `/genres` и `/mpa`. @@ -67,15 +67,20 @@ - `GET /films/{id}` — получить фильм по ID. - `POST /films` — добавить новый фильм. - `PUT /films` — обновить существующий фильм. +- `DELETE /films/{id}` — удалить фильм; связанные с ним записи удаляются каскадно. - `PUT /films/{id}/like/{userId}` — поставить лайк фильму. - `DELETE /films/{id}/like/{userId}` — убрать лайк. -- `GET /films/popular?count={count}` — топ-N фильмов по лайкам (по умолчанию 10). +- `GET /films/popular?count={count}&genreId={genreId}&year={year}` — топ-N фильмов по лайкам + (по умолчанию 10); жанр и год — необязательные фильтры. +- `GET /films/common?userId={userId}&friendId={friendId}` — общие фильмы двух пользователей, + по убыванию популярности. ### Пользователи (`/users`) - `GET /users` — получить список всех пользователей. - `GET /users/{id}` — получить пользователя по ID. - `POST /users` — создать нового пользователя. - `PUT /users` — обновить данные пользователя. +- `DELETE /users/{id}` — удалить пользователя; связанные с ним записи удаляются каскадно. - `PUT /users/{id}/friends/{friendId}` — добавить в друзья (дружба односторонняя). - `DELETE /users/{id}/friends/{friendId}` — удалить из друзей. - `GET /users/{id}/friends` — список друзей пользователя. 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 961959c..a010a02 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -45,6 +45,12 @@ public Film update(@Valid @RequestBody Film film) { return filmService.update(film); } + @DeleteMapping("/{id}") + public void delete(@PathVariable long id) { + log.info("DELETE /films/{}", id); + filmService.delete(id); + } + @PutMapping("/{id}/like/{userId}") public Film addLike(@PathVariable long id, @PathVariable long userId) { log.info("PUT /films/{}/like/{}", id, userId); 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 ba0d0ec..8c154dc 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -45,6 +45,12 @@ public User update(@Valid @RequestBody User user) { return userService.update(user); } + @DeleteMapping("/{id}") + public void delete(@PathVariable long id) { + log.info("DELETE /users/{}", id); + userService.delete(id); + } + @PutMapping("/{id}/friends/{friendId}") public User addFriend(@PathVariable long id, @PathVariable long friendId) { log.info("PUT /users/{}/friends/{}", id, friendId); diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java index beee424..000f19c 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -140,6 +140,12 @@ public Film update(Film film) { return filmStorage.update(film); } + // Лайки, жанры и прочие связи фильма база удаляет сама — каскадом из schema.sql + public void delete(long id) { + filmStorage.delete(id); // нет фильма — хранилище бросит NotFoundException, это 404 + log.debug("Фильм id={} удалён", id); + } + public Collection findAll() { return filmStorage.findAll(); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java index 640cf50..cde4926 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -102,6 +102,12 @@ public User update(User user) { return userStorage.update(user); } + // Дружбу, лайки и прочие связи пользователя база удаляет сама — каскадом из schema.sql + public void delete(long id) { + userStorage.delete(id); // нет пользователя — хранилище бросит NotFoundException, это 404 + log.debug("Пользователь id={} удалён", id); + } + public Collection findAll() { return userStorage.findAll(); } diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java index b9ad5e6..9155c27 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java @@ -135,6 +135,37 @@ void shouldDeleteFilm() { assertTrue(filmStorage.findById(film.getId()).isEmpty()); } + // Без ON DELETE CASCADE удаление упало бы на внешних ключах film_genres и film_likes + @Test + void shouldDeleteFilmWithGenresAndLikes() { + Film film = newFilm("С жанром и лайком"); + film.getGenres().add(genre(1)); + film = filmStorage.add(film); + User anna = createUser("anna"); + filmStorage.addLike(film.getId(), anna.getId()); + + filmStorage.delete(film.getId()); + + assertTrue(filmStorage.findById(film.getId()).isEmpty()); + assertTrue(filmStorage.findPopular(10, null, null).isEmpty()); + } + + // Лайки удалённого пользователя пропадают из подсчёта популярных + @Test + void shouldNotCountLikesOfDeletedUser() { + Film first = createFilm("Без лайков"); + Film liked = createFilm("С лайком"); + User boris = createUser("boris"); + filmStorage.addLike(liked.getId(), boris.getId()); + assertEquals(liked.getId(), filmStorage.findPopular(10, null, null).get(0).getId()); + + userStorage.delete(boris.getId()); + + // Лайков не осталось ни у кого — фильмы идут по порядку id + List ids = filmStorage.findPopular(10, null, null).stream().map(Film::getId).toList(); + assertEquals(List.of(first.getId(), liked.getId()), ids); + } + @Test void shouldAddAndRemoveLike() { Film film = createFilm("Любимый"); diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorageTest.java index 288802c..24adb41 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/user/UserDbStorageTest.java @@ -5,6 +5,7 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.context.annotation.Import; +import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.User; import ru.yandex.practicum.filmorate.storage.mapper.UserRowMapper; @@ -80,6 +81,25 @@ void shouldDeleteUser() { assertTrue(userStorage.findById(user.getId()).isEmpty()); } + // Без ON DELETE CASCADE удаление упало бы на внешнем ключе friendship + @Test + void shouldDeleteUserWithFriendships() { + User anna = createUser("anna"); + User boris = createUser("boris"); + userStorage.addFriend(anna.getId(), boris.getId()); + userStorage.addFriend(boris.getId(), anna.getId()); + + userStorage.delete(boris.getId()); + + assertTrue(userStorage.findById(boris.getId()).isEmpty()); + assertTrue(userStorage.findFriends(anna.getId()).isEmpty()); + } + + @Test + void shouldThrowWhenDeletingUnknownUser() { + assertThrows(NotFoundException.class, () -> userStorage.delete(9999)); + } + @Test void shouldAddFriendOneWay() { User anna = createUser("anna"); From efa01e0ba32c8579338803759dd855c7f701b24e Mon Sep 17 00:00:00 2001 From: Ksenia Date: Thu, 8 Oct 2026 23:44:13 +0300 Subject: [PATCH 2/2] test: add unknown film delete test --- .../filmorate/storage/film/FilmDbStorageTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java index 9155c27..cffc96c 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java @@ -5,6 +5,7 @@ import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; import org.springframework.context.annotation.Import; +import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.model.Film; import ru.yandex.practicum.filmorate.model.Genre; import ru.yandex.practicum.filmorate.model.Mpa; @@ -150,7 +151,6 @@ void shouldDeleteFilmWithGenresAndLikes() { assertTrue(filmStorage.findPopular(10, null, null).isEmpty()); } - // Лайки удалённого пользователя пропадают из подсчёта популярных @Test void shouldNotCountLikesOfDeletedUser() { Film first = createFilm("Без лайков"); @@ -166,6 +166,11 @@ void shouldNotCountLikesOfDeletedUser() { assertEquals(List.of(first.getId(), liked.getId()), ids); } + @Test + void shouldThrowWhenDeletingUnknownFilm() { + assertThrows(NotFoundException.class, () -> filmStorage.delete(9999)); + } + @Test void shouldAddAndRemoveLike() { Film film = createFilm("Любимый");