diff --git a/README.md b/README.md index 2cf454a..c1f5a5f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # java-filmorate Template repository for Filmorate project. + +### Database scheme +![database_scheme.png](database_scheme.png) \ No newline at end of file diff --git a/database_scheme.png b/database_scheme.png new file mode 100644 index 0000000..fffc276 Binary files /dev/null and b/database_scheme.png differ diff --git a/pom.xml b/pom.xml index 383688d..5bffe1a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,6 @@ org.springframework.boot spring-boot-starter-web - org.projectlombok lombok @@ -42,15 +41,42 @@ logbook-spring-boot-starter 3.7.2 + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + runtime + - + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 + + checkstyle.xml + + + + + check + + compile + + + org.springframework.boot spring-boot-maven-plugin - 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 0e16103..4c0b29a 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -5,7 +5,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.dto.film.FilmDto; +import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest; +import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest; import ru.yandex.practicum.filmorate.service.FilmService; import java.util.List; @@ -23,7 +25,7 @@ public FilmController(FilmService filmService) { * Возвращает фильм по id */ @GetMapping("/{id}") - public ResponseEntity getFilmById(@PathVariable int id) { + public ResponseEntity getFilmById(@PathVariable int id) { return ResponseEntity.ok(filmService.getFilmById(id)); } @@ -31,32 +33,30 @@ public ResponseEntity getFilmById(@PathVariable int id) { * Возвращает все фильмы в виде списка */ @GetMapping - public ResponseEntity> getFilms() { + public ResponseEntity> getFilms() { return ResponseEntity.ok(filmService.getFilms()); } /** * Добавляет новый фильм. * - * @param newFilm объект фильма, который нужно добавить + * @param newFilmDto объект фильма, который нужно добавить * @return ответ, содержащий созданный фильм с присвоенным ID */ @PostMapping - public ResponseEntity addFilm(@Valid @RequestBody Film newFilm) { - Film createdFilm = filmService.addFilm(newFilm); - return ResponseEntity.status(HttpStatus.CREATED).body(createdFilm); + public ResponseEntity addFilm(@Valid @RequestBody NewFilmRequest newFilmDto) { + return ResponseEntity.status(HttpStatus.CREATED).body(filmService.addFilm(newFilmDto)); } /** * Обновляет фильм. * - * @param updatedFilm объект фильма, который нужно обновить + * @param updatedFilmDto объект фильма, который нужно обновить * @return ответ, содержащий обновлённый фильм */ @PutMapping - public ResponseEntity updateFilm(@Valid @RequestBody Film updatedFilm) { - Film savedFilm = filmService.updateFilm(updatedFilm); - return ResponseEntity.ok(savedFilm); + public ResponseEntity updateFilm(@Valid @RequestBody UpdateFilmRequest updatedFilmDto) { + return ResponseEntity.ok(filmService.updateFilm(updatedFilmDto)); } /** @@ -92,7 +92,7 @@ public ResponseEntity removeLikeByUser(@PathVariable int id, @PathVariable * @return список самых популярных фильмов */ @GetMapping("/popular") - public ResponseEntity> getMostPopularFilms( + public ResponseEntity> getMostPopularFilms( @RequestParam(required = false, defaultValue = "10") int count) { return ResponseEntity.ok(filmService.getMostPopularFilms(count)); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java new file mode 100644 index 0000000..b7efcc3 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java @@ -0,0 +1,39 @@ +package ru.yandex.practicum.filmorate.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.yandex.practicum.filmorate.dto.GenreDto; +import ru.yandex.practicum.filmorate.service.GenreService; + +import java.util.List; + +@RestController +@RequestMapping("/genres") +public class GenreController { + private final GenreService genreService; + + @Autowired + public GenreController(GenreService genreService) { + this.genreService = genreService; + } + + /** + * Возвращает жанр по id + */ + @GetMapping("/{id}") + public ResponseEntity getGenreById(@PathVariable int id) { + return ResponseEntity.ok(genreService.getGenreById(id)); + } + + /** + * Возвращает все жанры в виде списка + */ + @GetMapping + public ResponseEntity> getGenres() { + return ResponseEntity.ok(genreService.getGenres()); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java new file mode 100644 index 0000000..d87736f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java @@ -0,0 +1,36 @@ +package ru.yandex.practicum.filmorate.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.yandex.practicum.filmorate.dto.MpaDto; +import ru.yandex.practicum.filmorate.service.MpaService; + +import java.util.List; + +@RestController +@RequestMapping("/mpa") +public class MpaController { + private final MpaService mpaService; + + @Autowired + public MpaController(MpaService mpaService) { + this.mpaService = mpaService; + } + + /** + * Возвращает рейтинг MPA по id + */ + @GetMapping("/{id}") + public ResponseEntity getMpaById(@PathVariable int id) { + return ResponseEntity.ok(mpaService.getMpaById(id)); + } + + /** + * Возвращает все рейтинги MPA + */ + @GetMapping + public ResponseEntity> getMpaList() { + return ResponseEntity.ok(mpaService.getMpaList()); + } +} \ 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 a441f3a..c4b41ae 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java @@ -5,7 +5,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.dto.user.NewUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UpdateUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UserDto; import ru.yandex.practicum.filmorate.service.UserService; import java.util.List; @@ -23,7 +25,7 @@ public UserController(UserService userService) { * Возвращает пользователя по id */ @GetMapping("/{id}") - public ResponseEntity getUserById(@PathVariable int id) { + public ResponseEntity getUserById(@PathVariable int id) { return ResponseEntity.ok(userService.getUserById(id)); } @@ -31,7 +33,7 @@ public ResponseEntity getUserById(@PathVariable int id) { * Возвращает всех пользователей в виде списка */ @GetMapping - public ResponseEntity> getUsers() { + public ResponseEntity> getUsers() { return ResponseEntity.ok(userService.getUsers()); } @@ -42,9 +44,8 @@ public ResponseEntity> getUsers() { * @return ответ, содержащий созданного пользователя с присвоенным ID */ @PostMapping - public ResponseEntity addUser(@Valid @RequestBody User newUser) { - User createdUser = userService.addUser(newUser); - return ResponseEntity.status(HttpStatus.CREATED).body(createdUser); + public ResponseEntity addUser(@Valid @RequestBody NewUserRequest newUser) { + return ResponseEntity.status(HttpStatus.CREATED).body(userService.addUser(newUser)); } /** @@ -54,9 +55,8 @@ public ResponseEntity addUser(@Valid @RequestBody User newUser) { * @return ответ, содержащий обновлённого пользователя */ @PutMapping - public ResponseEntity updateUser(@Valid @RequestBody User updatedUser) { - User savedUser = userService.updateUser(updatedUser); - return ResponseEntity.ok(savedUser); + public ResponseEntity updateUser(@Valid @RequestBody UpdateUserRequest updatedUser) { + return ResponseEntity.ok(userService.updateUser(updatedUser)); } /** @@ -92,7 +92,7 @@ public ResponseEntity removeFriend(@PathVariable int id, @PathVariable int * @return список друзей-пользователей указанного пользователя. */ @GetMapping("/{id}/friends") - public ResponseEntity> getAllFriends(@PathVariable int id) { + public ResponseEntity> getAllFriends(@PathVariable int id) { return ResponseEntity.ok(userService.getUserFriendsById(id)); } @@ -104,7 +104,7 @@ public ResponseEntity> getAllFriends(@PathVariable int id) { * @return список общих друзей в виде списка пользователей. */ @GetMapping("/{id}/friends/common/{otherId}") - public ResponseEntity> getCommonFriends(@PathVariable int id, @PathVariable int 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/dto/GenreDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/GenreDto.java new file mode 100644 index 0000000..67b8ad9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/GenreDto.java @@ -0,0 +1,11 @@ +package ru.yandex.practicum.filmorate.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class GenreDto { + private Integer id; + private String name; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/MpaDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/MpaDto.java new file mode 100644 index 0000000..d0faa73 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/MpaDto.java @@ -0,0 +1,11 @@ +package ru.yandex.practicum.filmorate.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class MpaDto { + private Integer id; + private String name; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/film/FilmDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/film/FilmDto.java new file mode 100644 index 0000000..16fa38b --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/film/FilmDto.java @@ -0,0 +1,23 @@ +package ru.yandex.practicum.filmorate.dto.film; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.time.LocalDate; +import java.util.Set; + +@Data +@NoArgsConstructor +public class FilmDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private int id; + private String name; + private String description; + private LocalDate releaseDate; + private Integer duration; + private Set genres; + private Mpa mpa; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/film/NewFilmRequest.java b/src/main/java/ru/yandex/practicum/filmorate/dto/film/NewFilmRequest.java new file mode 100644 index 0000000..1b499a9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/film/NewFilmRequest.java @@ -0,0 +1,24 @@ +package ru.yandex.practicum.filmorate.dto.film; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.time.LocalDate; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class NewFilmRequest { + private String name; + private String description; + private LocalDate releaseDate; + private Integer duration; + private Set genres; + private Mpa mpa; +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/film/UpdateFilmRequest.java b/src/main/java/ru/yandex/practicum/filmorate/dto/film/UpdateFilmRequest.java new file mode 100644 index 0000000..da3f4e4 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/film/UpdateFilmRequest.java @@ -0,0 +1,49 @@ +package ru.yandex.practicum.filmorate.dto.film; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.time.LocalDate; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateFilmRequest { + private int id; + private String name; + private String description; + private LocalDate releaseDate; + private Integer duration; + private Set genres; + private Mpa mpa; + + public boolean hasName() { + return !(name == null || name.isBlank()); + } + + public boolean hasDescription() { + return !(description == null || description.isBlank()); + } + + public boolean hasReleaseDate() { + return !(releaseDate == null); + } + + public boolean hasDuration() { + return !(duration == null); + } + + public boolean hasGenres() { + return !(genres == null || genres.isEmpty()); + } + + public boolean hasMpaRating() { + return !(mpa == null); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/user/NewUserRequest.java b/src/main/java/ru/yandex/practicum/filmorate/dto/user/NewUserRequest.java new file mode 100644 index 0000000..d360111 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/user/NewUserRequest.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class NewUserRequest { + private String name; + private String email; + private String login; + private LocalDate birthday; +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/user/UpdateUserRequest.java b/src/main/java/ru/yandex/practicum/filmorate/dto/user/UpdateUserRequest.java new file mode 100644 index 0000000..d5080a5 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/user/UpdateUserRequest.java @@ -0,0 +1,35 @@ +package ru.yandex.practicum.filmorate.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.time.LocalDate; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateUserRequest { + int id; + private String name; + private String email; + private String login; + private LocalDate birthday; + + public boolean hasLogin() { + return ! (login == null || login.isBlank()); + } + + public boolean hasName() { + return ! (name == null || name.isBlank()); + } + + public boolean hasEmail() { + return ! (email == null || email.isBlank()); + } + + public boolean hasBirthday() { + return ! (birthday == null); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/user/UserDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/user/UserDto.java new file mode 100644 index 0000000..fdab327 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/dto/user/UserDto.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.dto.user; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@NoArgsConstructor +public class UserDto { + private int id; + private String name; + private String email; + private String login; + private LocalDate birthday; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/InternalServerException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/InternalServerException.java new file mode 100644 index 0000000..c9eba3a --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/exception/InternalServerException.java @@ -0,0 +1,7 @@ +package ru.yandex.practicum.filmorate.exception; + +public class InternalServerException extends RuntimeException { + public InternalServerException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java new file mode 100644 index 0000000..dadcf11 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java @@ -0,0 +1,73 @@ +package ru.yandex.practicum.filmorate.mapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.dto.film.FilmDto; +import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest; +import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest; +import ru.yandex.practicum.filmorate.model.Film; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.time.LocalDate; +import java.util.Set; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class FilmMapper { + public static Film mapToFilm(NewFilmRequest request) { + String name = request.getName(); + String description = request.getDescription(); + LocalDate releaseDate = request.getReleaseDate(); + Integer duration = request.getDuration(); + Set genres = request.getGenres(); + Mpa mpaRatingId = request.getMpa(); + + return Film.builder() + .name(name) + .description(description) + .releaseDate(releaseDate) + .duration(duration) + .genres(genres) + .mpa(mpaRatingId) + .build(); + } + + public static FilmDto mapToFilmDto(Film film) { + FilmDto dto = new FilmDto(); + dto.setId(film.getId()); + dto.setName(film.getName()); + dto.setDescription(film.getDescription()); + dto.setReleaseDate(film.getReleaseDate()); + dto.setDuration(film.getDuration()); + dto.setGenres(film.getGenres()); + dto.setMpa(film.getMpa()); + return dto; + } + + public static Film updateFilmFields(Film film, UpdateFilmRequest request) { + if (request.hasName()) { + film.setName(request.getName()); + } + + if (request.hasDescription()) { + film.setDescription(request.getDescription()); + } + + if (request.hasDuration()) { + film.setDuration(request.getDuration()); + } + + if (request.hasReleaseDate()) { + film.setReleaseDate(request.getReleaseDate()); + } + + if (request.hasGenres()) { + film.setGenres(request.getGenres()); + } + + if (request.hasMpaRating()) { + film.setMpa(request.getMpa()); + } + return film; + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/mapper/GenreMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mapper/GenreMapper.java new file mode 100644 index 0000000..2bd0ca5 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/mapper/GenreMapper.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.mapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.dto.GenreDto; +import ru.yandex.practicum.filmorate.model.Genre; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GenreMapper { + public static Genre mapToGenre(GenreDto request) { + return new Genre(request.getId(), request.getName()); + } + + public static GenreDto mapToGenreDto(Genre genre) { + GenreDto dto = new GenreDto(); + dto.setId(genre.getId()); + dto.setName(genre.getName()); + return dto; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/mapper/MpaMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mapper/MpaMapper.java new file mode 100644 index 0000000..193a41f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/mapper/MpaMapper.java @@ -0,0 +1,20 @@ +package ru.yandex.practicum.filmorate.mapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.dto.MpaDto; +import ru.yandex.practicum.filmorate.model.Mpa; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MpaMapper { + public static Mpa mapToMpa(MpaDto request) { + return new Mpa(request.getId(), request.getName()); + } + + public static MpaDto mapToMpaDto(Mpa mpa) { + MpaDto dto = new MpaDto(); + dto.setId(mpa.getId()); + dto.setName(mpa.getName()); + return dto; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/mapper/UserMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mapper/UserMapper.java new file mode 100644 index 0000000..3d5ec7f --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/mapper/UserMapper.java @@ -0,0 +1,57 @@ +package ru.yandex.practicum.filmorate.mapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import ru.yandex.practicum.filmorate.dto.user.NewUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UpdateUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UserDto; +import ru.yandex.practicum.filmorate.model.User; + +import java.time.LocalDate; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class UserMapper { + public static User mapToUser(NewUserRequest request) { + String name = request.getName(); + String email = request.getEmail(); + String login = request.getLogin(); + LocalDate birthday = request.getBirthday(); + + return User.builder() + .name(name) + .email(email) + .login(login) + .birthday(birthday) + .build(); + } + + public static UserDto mapToUserDto(User user) { + UserDto dto = new UserDto(); + dto.setId(user.getId()); + dto.setName(user.getName()); + dto.setEmail(user.getEmail()); + dto.setLogin(user.getLogin()); + dto.setBirthday(user.getBirthday()); + return dto; + } + + public static User updateUserFields(User user, UpdateUserRequest request) { + if (request.hasLogin()) { + user.setLogin(request.getLogin()); + } + + if (request.hasName()) { + user.setName(request.getName()); + } + + if (request.hasEmail()) { + user.setEmail(request.getEmail()); + } + + if (request.hasBirthday()) { + user.setBirthday(request.getBirthday()); + } + + return user; + } +} \ 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 e9b9a17..dc918a2 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java @@ -27,4 +27,6 @@ public class Film { private Integer duration; private Set likes; + private Set genres; + private Mpa mpa; } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java new file mode 100644 index 0000000..137f4b0 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.*; + +@Data +@Builder(toBuilder = true) +@EqualsAndHashCode(of = { "id" }) +@AllArgsConstructor +@NoArgsConstructor +public class Genre { + private Integer id; + private String name; +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java new file mode 100644 index 0000000..04b9dd9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java @@ -0,0 +1,13 @@ +package ru.yandex.practicum.filmorate.model; + +import lombok.*; + +@Data +@Builder(toBuilder = true) +@EqualsAndHashCode(of = { "id" }) +@AllArgsConstructor +@NoArgsConstructor +public class Mpa { + private Integer id; + private String name; +} 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 5fd80e8..4a77c66 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -5,14 +5,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.dto.film.FilmDto; +import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest; +import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest; import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.mapper.FilmMapper; 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 @@ -33,16 +36,16 @@ public FilmService(FilmStorage filmStorage, UserStorage userStorage) { * Возвращает фильм по id. * Вызывает метод хранилища по получению фильма по id. */ - public Film getFilmById(int id) { - return filmStorage.getFilmById(id); + public FilmDto getFilmById(int id) { + return FilmMapper.mapToFilmDto(filmStorage.getFilmById(id)); } /** * Возвращает все фильмы в виде списка. * Вызывает метод хранилища по получению всех фильмов */ - public List getFilms() { - return filmStorage.getFilms(); + public List getFilms() { + return filmStorage.getFilms().stream().map(FilmMapper::mapToFilmDto).toList(); } /** @@ -50,7 +53,8 @@ public List getFilms() { * Проверяет поля переданного фильма на соответствие. * Если всё хорошо, то вызывает соответствующий метод хранилища. */ - public Film addFilm(Film newFilm) { + public FilmDto addFilm(NewFilmRequest newFilmDto) { + Film newFilm = FilmMapper.mapToFilm(newFilmDto); try { checkIsValidFilm(newFilm); } catch (ValidationException exception) { @@ -58,7 +62,7 @@ public Film addFilm(Film newFilm) { throw exception; } log.trace("Данные фильма {} прошли валидацию при добавлении", newFilm.getId()); - return filmStorage.addFilm(newFilm); + return FilmMapper.mapToFilmDto(filmStorage.addFilm(newFilm)); } /** @@ -66,7 +70,8 @@ public Film addFilm(Film newFilm) { * Проверяет поля переданного фильма на соответствие. * Если всё хорошо, то вызывает соответствующий метод хранилища. */ - public Film updateFilm(Film updatedFilm) { + public FilmDto updateFilm(UpdateFilmRequest updatedFilmDto) { + Film updatedFilm = FilmMapper.updateFilmFields(filmStorage.getFilmById(updatedFilmDto.getId()), updatedFilmDto); try { checkIsValidFilm(updatedFilm); } catch (ValidationException exception) { @@ -75,7 +80,7 @@ public Film updateFilm(Film updatedFilm) { throw exception; } log.trace("Фильм {} прошёл валидацию для обновления", updatedFilm.getId()); - return filmStorage.updateFilm(updatedFilm); + return FilmMapper.mapToFilmDto(filmStorage.updateFilm(updatedFilm)); } /** @@ -86,13 +91,11 @@ public Film updateFilm(Film updatedFilm) { public void addLike(int filmId, int userId) { userStorage.getUserById(userId); log.trace("Пользователь {} найден для добавления лайка", userId); - Film film = filmStorage.getFilmById(filmId); + filmStorage.getFilmById(filmId); log.trace("Фильм {} найден для добавления лайка", filmId); - film.getLikes().add(userId); + filmStorage.addLike(filmId, userId); log.info("Добавление лайка от пользователя {} для фильма {} выполнено", userId, filmId); - - filmStorage.updateFilm(film); } /** @@ -106,22 +109,16 @@ public void removeLike(int filmId, int userId) { Film film = filmStorage.getFilmById(filmId); log.trace("Фильм {} найден для удаления лайка", filmId); - film.getLikes().remove(userId); + filmStorage.removeLike(filmId, 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(); + public List getMostPopularFilms(int count) { + return filmStorage.getMostPopularFilms(count).stream().map(FilmMapper::mapToFilmDto).toList(); } /** @@ -133,6 +130,10 @@ private void checkIsValidFilm(Film film) throws ValidationException { throw new ValidationException("Фильм для валидации входных параметров не найден"); } + if (film.getName().isBlank()) { + throw new ValidationException("Название фильма не может быть пустым"); + } + if (film.getDescription().length() > FILM_DESCRIPTION_LENGTH) { throw new ValidationException("Описание фильма не может быть больше 200 символов"); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java new file mode 100644 index 0000000..6c0e748 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/GenreService.java @@ -0,0 +1,33 @@ +package ru.yandex.practicum.filmorate.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.dto.GenreDto; +import ru.yandex.practicum.filmorate.mapper.GenreMapper; +import ru.yandex.practicum.filmorate.storage.GenreStorage; + +import java.util.List; + +@Service +public class GenreService { + private final GenreStorage genreStorage; + + @Autowired + public GenreService(GenreStorage genreStorage) { + this.genreStorage = genreStorage; + } + + /** + * Возвращает жанр по id. + */ + public GenreDto getGenreById(int id) { + return GenreMapper.mapToGenreDto(genreStorage.getGenreById(id)); + } + + /** + * Возвращает всех жанры в виде списка. + */ + public List getGenres() { + return genreStorage.getGenres().stream().map(GenreMapper::mapToGenreDto).toList(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java new file mode 100644 index 0000000..4d41d41 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/service/MpaService.java @@ -0,0 +1,33 @@ +package ru.yandex.practicum.filmorate.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.dto.MpaDto; +import ru.yandex.practicum.filmorate.mapper.MpaMapper; +import ru.yandex.practicum.filmorate.storage.MpaStorage; + +import java.util.List; + +@Service +public class MpaService { + private final MpaStorage mpaStorage; + + @Autowired + public MpaService(MpaStorage mpaStorage) { + this.mpaStorage = mpaStorage; + } + + /** + * Возвращает MPA рейтинг по id. + */ + public MpaDto getMpaById(int id) { + return MpaMapper.mapToMpaDto(mpaStorage.getMpaById(id)); + } + + /** + * Возвращает всех MPA рейтинги в виде списка. + */ + public List getMpaList() { + return mpaStorage.getMpaList().stream().map(MpaMapper::mapToMpaDto).toList(); + } +} \ 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 index 5ef5d21..b7b3a60 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java @@ -4,8 +4,12 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import ru.yandex.practicum.filmorate.dto.user.NewUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UpdateUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UserDto; import ru.yandex.practicum.filmorate.exception.NotFoundException; import ru.yandex.practicum.filmorate.exception.ValidationException; +import ru.yandex.practicum.filmorate.mapper.UserMapper; import ru.yandex.practicum.filmorate.model.User; import ru.yandex.practicum.filmorate.storage.UserStorage; @@ -26,16 +30,16 @@ public UserService(UserStorage userStorage) { * Возвращает пользователя по id. * Вызывает метод хранилища по получению пользователя по id. */ - public User getUserById(int id) { - return userStorage.getUserById(id); + public UserDto getUserById(int id) { + return UserMapper.mapToUserDto(userStorage.getUserById(id)); } /** * Возвращает всех пользователей в виде списка. * Вызывает соответствующий метод хранилища. */ - public List getUsers() { - return userStorage.getUsers(); + public List getUsers() { + return userStorage.getUsers().stream().map(UserMapper::mapToUserDto).toList(); } /** @@ -46,7 +50,8 @@ public List getUsers() { * @param newUser объект пользователя, который нужно добавить * @return созданный пользователь с присвоенным ID */ - public User addUser(User newUser) { + public UserDto addUser(NewUserRequest newUserDto) { + User newUser = UserMapper.mapToUser(newUserDto); try { checkIsValidUser(newUser); } catch (ValidationException exception) { @@ -55,7 +60,7 @@ public User addUser(User newUser) { throw exception; } log.trace("Данные новго пользователя c логином {} прошли валидацию при добавлении", newUser.getLogin()); - return userStorage.addUser(newUser); + return UserMapper.mapToUserDto(userStorage.addUser(newUser)); } /** @@ -66,7 +71,10 @@ public User addUser(User newUser) { * @param updatedUser объект пользователя, который нужно обновить * @return обновлённый пользователь */ - public User updateUser(User updatedUser) { + public UserDto updateUser(UpdateUserRequest updatedUserDto) { + User updatedUser = userStorage.getUserById(updatedUserDto.getId()); + updatedUser = UserMapper.updateUserFields(updatedUser, updatedUserDto); + try { checkIsValidUser(updatedUser); } catch (ValidationException exception) { @@ -76,7 +84,7 @@ public User updateUser(User updatedUser) { } log.info("Данные пользователя {} прошли валидацию при обновлении", updatedUser.getId()); - return userStorage.updateUser(updatedUser); + return UserMapper.mapToUserDto(userStorage.updateUser(updatedUser)); } /** @@ -89,13 +97,11 @@ public void addFriends(int firstUserId, int secondUserId) { User secondUser = userStorage.getUserById(secondUserId); log.trace("Пользователь {} найден для добавления в друзья", secondUserId); - firstUser.getFriends().add(secondUserId); - secondUser.getFriends().add(firstUser.getId()); - log.info("Пользователь {} добавлен в друзья пользователя {}.", - secondUserId, firstUserId); + if (firstUser.getFriends().contains(secondUserId)) { + throw new ValidationException("Пользователь уже добавлен в друзья"); + } - userStorage.updateUser(firstUser); - userStorage.updateUser(secondUser); + userStorage.addFriends(firstUserId, secondUserId); log.trace("Пользователи обновлены в хранилище."); } @@ -109,13 +115,7 @@ public void removeFriends(int firstUserId, int secondUserId) { 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); + userStorage.removeFriends(firstUserId, secondUserId); log.trace("Пользователи обновлены в хранилище."); } @@ -123,9 +123,9 @@ public void removeFriends(int firstUserId, int secondUserId) { * Ищет общих друзей у пользователей * Если оба пользователя существуют, то возвращает список общих друзей. */ - public List getCommonFriends(int firstUserId, int secondUserId) { - List firstFriends = getUserFriendsById(firstUserId); - List secondFriends = getUserFriendsById(secondUserId); + public List getCommonFriends(int firstUserId, int secondUserId) { + List firstFriends = getUserFriendsById(firstUserId); + List secondFriends = getUserFriendsById(secondUserId); log.trace("Списки друзей пользователей для поиска общих друзей составлены", secondUserId); return firstFriends.stream() @@ -137,11 +137,14 @@ public List getCommonFriends(int firstUserId, int secondUserId) { * Возвращает всех друзей пользователя * Если пользователь существуюет, то возвращает список по id */ - public List getUserFriendsById(int userId) { + public List getUserFriendsById(int userId) { User user = userStorage.getUserById(userId); log.trace("Пользователь {} найден для поиска всех его друзей", userId); - return user.getFriends().stream().map(id -> userStorage.getUserById(id)).toList(); + return user.getFriends().stream() + .map(id -> userStorage.getUserById(id)) + .map(UserMapper::mapToUserDto) + .toList(); } /** diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java index 8ba57f5..7517b35 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java @@ -23,4 +23,19 @@ public interface FilmStorage { * Обновляет фильм. */ Film updateFilm(Film updatedFilm); + + /** + * Добавляет лайк. + */ + void addLike(int filmId, int userId); + + /** + * Удаляет лайк. + */ + void removeLike(int filmId, int userId); + + /** + * Возвращает популярные фильмы + */ + List getMostPopularFilms(int count); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/GenreStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/GenreStorage.java new file mode 100644 index 0000000..d776e8b --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/GenreStorage.java @@ -0,0 +1,16 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.Genre; +import java.util.List; + +public interface GenreStorage { + /** + * Возвращает жанр по id + */ + Genre getGenreById(int id); + + /** + * Возвращает все жанры в виде списка + */ + List getGenres(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java index e128d75..38c1f37 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java @@ -107,4 +107,25 @@ private int getNextId() { .orElse(0); return ++currentMaxId; } + + /** + * Добавляет лайк. + */ + public void addLike(int filmId, int userId) { + throw new NotFoundException("Метод не поддерживается"); + } + + /** + * Удаляет лайк. + */ + public void removeLike(int filmId, int userId) { + throw new NotFoundException("Метод не поддерживается"); + } + + /** + * Возвращает популярные фильмы + */ + public List getMostPopularFilms(int count) { + throw new NotFoundException("Метод не поддерживается"); + } } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java index 1af6df2..56104a3 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java @@ -105,4 +105,28 @@ private int getNextId() { .orElse(0); return ++currentMaxId; } + + /** + * Заглушка для метода добавления в друзья + */ + @Override + public void addFriends(int userId, int friendId) { + throw new NotFoundException("Метод не поддерживается"); + } + + /** + * Заглушка для метода удаления из друзей + */ + @Override + public void removeFriends(int userId, int friendId) { + throw new NotFoundException("Метод не поддерживается"); + } + + /** + * Заглушка для метода получения друзей пользователя в виде списка id + */ + @Override + public Set getUserFriends(Integer userId) { + throw new NotFoundException("Метод не поддерживается"); + } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/MpaStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/MpaStorage.java new file mode 100644 index 0000000..ee7b2f9 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/MpaStorage.java @@ -0,0 +1,17 @@ +package ru.yandex.practicum.filmorate.storage; + +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.util.List; + +public interface MpaStorage { + /** + * Возвращает MPA рейтинг по id + */ + Mpa getMpaById(int id); + + /** + * Возвращает все MPA рейтинги в виде списка + */ + List getMpaList(); +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java index 7a01079..f40c5b6 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java @@ -1,7 +1,9 @@ package ru.yandex.practicum.filmorate.storage; import ru.yandex.practicum.filmorate.model.User; + import java.util.List; +import java.util.Set; public interface UserStorage { /** @@ -33,4 +35,19 @@ public interface UserStorage { * @return копия обновлённого пользователя */ User updateUser(User updatedUser); + + /** + * Добавляет пользователя в друзья + */ + void addFriends(int userId, int friendId); + + /** + * Удаляет пользователя из друзей + */ + void removeFriends(int userId, int friendId); + + /** + * Возвращает друзей пользователя в виде списка id + */ + Set getUserFriends(Integer userId); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/BaseStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/BaseStorage.java new file mode 100644 index 0000000..1a3fa1c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/BaseStorage.java @@ -0,0 +1,65 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import lombok.RequiredArgsConstructor; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import ru.yandex.practicum.filmorate.exception.InternalServerException; + +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +public class BaseStorage { + protected final JdbcTemplate jdbc; + protected final RowMapper mapper; + + protected Optional findOne(String query, Object... params) { + try { + T result = jdbc.queryForObject(query, mapper, params); + return Optional.ofNullable(result); + } catch (EmptyResultDataAccessException ignored) { + return Optional.empty(); + } + } + + protected List findMany(String query, Object... params) { + return jdbc.query(query, mapper, params); + } + + protected boolean delete(String query, int id) { + int rowsDeleted = jdbc.update(query, id); + return rowsDeleted > 0; + } + + protected void update(String query, Object... params) { + int rowsUpdated = jdbc.update(query, params); + if (rowsUpdated == 0) { + throw new InternalServerException("Не удалось обновить данные"); + } + } + + protected int insert(String query, Object... params) { + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + jdbc.update(connection -> { + PreparedStatement ps = connection + .prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + for (int idx = 0; idx < params.length; idx++) { + ps.setObject(idx + 1, params[idx]); + } + return ps; + }, keyHolder); + + Integer id = keyHolder.getKeyAs(Integer.class); + + // Возвращаем id нового пользователя + if (id != null) { + return id; + } else { + throw new InternalServerException("Не удалось сохранить данные"); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStorage.java new file mode 100644 index 0000000..eab7ab1 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStorage.java @@ -0,0 +1,271 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.InternalServerException; +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; +import ru.yandex.practicum.filmorate.storage.FilmStorage; +import ru.yandex.practicum.filmorate.storage.GenreStorage; +import ru.yandex.practicum.filmorate.storage.MpaStorage; +import ru.yandex.practicum.filmorate.storage.dao.mapper.FilmRowMapper; + +import java.util.*; + +@Primary +@Repository +public class FilmDbStorage extends BaseStorage implements FilmStorage { + private final MpaStorage mpaStorage; + private final GenreStorage genreStorage; + + private static final Logger log = LoggerFactory.getLogger(FilmDbStorage.class); + + private static final String GET_FILM_BY_ID = "SELECT * FROM films WHERE id = ?;"; + private static final String GET_FILMS = "SELECT * FROM films;"; + private static final String ADD_FILM = "INSERT INTO films (name, description, duration, release_date, " + + "mpa_rating_id) VALUES (?, ?, ?, ?, ?);"; + private static final String ADD_FILM_SIMPLE = "INSERT INTO films (name, description, duration, release_date) " + + "VALUES (?, ?, ?, ?);"; + private static final String UPDATE_FILM = "UPDATE films SET name = ?,description = ?, duration = ?, " + + "release_date = ?, mpa_rating_id = ? WHERE id = ?;"; + private static final String UPDATE_FILM_SHORT = "UPDATE films SET name = ?,description = ?, duration = ?, " + + "release_date = ? WHERE id = ?;"; + private static final String GET_POPULAR_FILMS = "SELECT f.*, COUNT(fl.user_id) as likes_count FROM films f " + + "LEFT JOIN films_likes fl ON f.id = fl.film_id " + + "GROUP BY f.id " + + "ORDER BY likes_count DESC " + + "LIMIT ?;"; + + private static final String GET_GENRES_ID = "SELECT genre_id FROM films_genres WHERE film_id = ?;"; + private static final String GET_GENRE_NAME = "SELECT name FROM genres WHERE id = ?;"; + private static final String ADD_GENRE = "INSERT INTO films_genres (film_id, genre_id) " + + "VALUES (?, ?);"; + private static final String DELETE_GENRES = "DELETE FROM films_genres WHERE film_id = ?;"; + + private static final String GET_MPA_ID = "SELECT mpa_rating_id FROM films WHERE id = ?;"; + private static final String GET_MPA_NAME = "SELECT name FROM mpa_ratings WHERE id = ?;"; + + private static final String ADD_USER_LIKE = "INSERT INTO films_likes (film_id, user_id) VALUES (?, ?);"; + private static final String DELETE_USER_LIKE = "DELETE FROM films_likes WHERE film_id = ? " + + "AND user_id = ?;"; + + + public FilmDbStorage(JdbcTemplate jdbc, FilmRowMapper mapper, MpaStorage mpaStorage, GenreStorage genreStorage) { + super(jdbc, mapper); + this.mpaStorage = mpaStorage; + this.genreStorage = genreStorage; + } + + /** + * Возвращает фильм по id из БД + */ + @Override + public Film getFilmById(int id) { + Film film = findOne(GET_FILM_BY_ID, id).orElseThrow(() -> { + String filmNotFound = "Фильм с id = " + id + " не найден"; + log.error(filmNotFound); + return new NotFoundException(filmNotFound); + }); + enrichFilmByAdditionalInfo(film); + return film; + } + + /** + * Возвращает все фильмы из БД в виде списка + */ + @Override + public List getFilms() { + return findMany(GET_FILMS).stream().peek(this::enrichFilmByAdditionalInfo).toList(); + } + + /** + * Добавляет новый фильм в БД + * + * @param newFilm объект фильма, который нужно добавить + * @return созданный фильм с присвоенным ID + */ + @Override + public Film addFilm(Film newFilm) { + if (newFilm == null) { + String filmNotFound = "Фильм для добавления не найден"; + log.error(filmNotFound); + throw new NotFoundException(filmNotFound); + } + checkFilmMpaValid(newFilm); + checkFilmGenresValid(newFilm); + + int id; + if (newFilm.getMpa() == null) { + id = insert(ADD_FILM_SIMPLE, newFilm.getName(), newFilm.getDescription(), newFilm.getDuration(), + newFilm.getReleaseDate()); + } else { + id = insert(ADD_FILM, newFilm.getName(), newFilm.getDescription(), newFilm.getDuration(), + newFilm.getReleaseDate(), newFilm.getMpa().getId()); + } + newFilm.setId(id); + + log.info("Фильму {} присвоен id {}", newFilm.getName(), newFilm.getId()); + if (newFilm.getGenres() != null && !newFilm.getGenres().isEmpty()) { + newFilm.getGenres().forEach(genre -> insert(ADD_GENRE, newFilm.getId(), genre.getId())); + } + + enrichFilmByAdditionalInfo(newFilm); + return newFilm; + } + + /** + * Обновляет фильм. + * Проверяет поля переданного фильма на соответствие. + * Если всё хорошо и переданный фильм существует в контроллере, то создаёт копию и сохраняет в контроллер. + * + * @param updatedFilm объект фильма, который нужно обновить + * @return копия обновлённого фильма + */ + @Override + public Film updateFilm(Film updatedFilm) { + try { + checkFilmMpaValid(updatedFilm); + checkFilmGenresValid(updatedFilm); + + if (updatedFilm.getMpa() == null) { + update(UPDATE_FILM_SHORT, updatedFilm.getName(), updatedFilm.getDescription(), updatedFilm.getDuration(), + updatedFilm.getReleaseDate(), updatedFilm.getId()); + } else { + update(UPDATE_FILM, updatedFilm.getName(), updatedFilm.getDescription(), updatedFilm.getDuration(), + updatedFilm.getReleaseDate(), updatedFilm.getMpa().getId(), updatedFilm.getId()); + } + + if (updatedFilm.getGenres() != null && !updatedFilm.getGenres().isEmpty()) { + update(DELETE_GENRES, updatedFilm.getId()); + updatedFilm.getGenres().forEach(genre -> + update(ADD_GENRE, updatedFilm.getId(), genre.getId())); + } + enrichFilmByAdditionalInfo(updatedFilm); + log.info("Фильм {} обновлён в хранилище", updatedFilm.getId()); + return updatedFilm; + } catch (InternalServerException exception) { + log.error("Фильм {} не обновлён из-за внутренней ошибки", updatedFilm.getId()); + throw exception; + } + } + + /** + * Добавляет лайк от пользователя. + * Делает запись в БД. + */ + public void addLike(int filmId, int userId) { + try { + update(ADD_USER_LIKE, filmId, userId); + } catch (InternalServerException exception) { + log.error("Не удалось добавить лайк для фильма {} от пользователя {}", filmId, userId); + throw exception; + } + } + + /** + * Удаляет лайк от пользователя. + * Делает запись в БД. + */ + public void removeLike(int filmId, int userId) { + try { + update(DELETE_USER_LIKE, filmId, userId); + } catch (InternalServerException exception) { + log.error("Не удалось удалить лайк для фильма {} от пользователя {}", filmId, userId); + throw exception; + } + } + + /** + * Возвращает ТОП фильмов по числу лайков + */ + public List getMostPopularFilms(int count) { + return findMany(GET_POPULAR_FILMS, count).stream().peek(this::enrichFilmByAdditionalInfo).toList(); + } + + /** + * Обогащает фильм дополнительной информацией. + */ + private Film enrichFilmByAdditionalInfo(Film film) { + if (film == null) { + String filmNotFound = "Фильм для обогащения не найден"; + log.error(filmNotFound); + throw new NotFoundException(filmNotFound); + } + film.setGenres(new LinkedHashSet<>(getFilmGenres(film.getId()))); + film.setMpa(getFilmMpaRating(film.getId())); + + if (film.getLikes() == null) { + film.setLikes(new HashSet<>()); + } + + return film; + } + + /** + * Формирует список жанров фильма + */ + private List getFilmGenres(int filmId) { + List genreIdList = jdbc.queryForList(GET_GENRES_ID, Integer.class, filmId); + + if (genreIdList.isEmpty()) { + return new ArrayList<>(); + } + + return genreIdList.stream() + .map(genreId -> { + Genre genre = new Genre(); + genre.setId(genreId); + return genre; + }).peek(genre -> { + String genreName = jdbc.queryForObject(GET_GENRE_NAME, String.class, genre.getId()); + genre.setName(genreName); + }) + .sorted(Comparator.comparing(Genre::getId)) + .toList(); + } + + /** + * Формирует объект MPA рейтинга + */ + private Mpa getFilmMpaRating(int filmId) { + Integer mpaRatingId = jdbc.queryForObject(GET_MPA_ID, Integer.class, filmId); + if (mpaRatingId == null) { + return null; + } + String mpaName = jdbc.queryForObject(GET_MPA_NAME, String.class, mpaRatingId); + return new Mpa(mpaRatingId, mpaName); + } + + /** + * Проверяет, что MPA рейтинг фильма существует + */ + private void checkFilmMpaValid(Film film) { + Mpa filmMpa = film.getMpa(); + if (filmMpa == null) { + return; + } + if (!mpaStorage.getMpaList().contains(filmMpa)) { + throw new NotFoundException("MPA рейтинг " + filmMpa.getId() + " не найден"); + } + } + + /** + * Проверяет, что каждый жанр фильма существует + */ + private void checkFilmGenresValid(Film film) { + Set filmGenres = film.getGenres(); + if (filmGenres == null || filmGenres.isEmpty()) { + return; + } + filmGenres.forEach(genre -> { + if (!genreStorage.getGenres().contains(genre)) { + throw new NotFoundException("MPA рейтинг " + genre.getId() + " не найден"); + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorage.java new file mode 100644 index 0000000..0bd4ab1 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorage.java @@ -0,0 +1,41 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.GenreStorage; +import ru.yandex.practicum.filmorate.storage.dao.mapper.GenreRowMapper; + +import java.util.List; + +@Repository +public class GenreDbStorage extends BaseStorage implements GenreStorage { + private static final Logger log = LoggerFactory.getLogger(GenreDbStorage.class); + private static final String GET_GENRE_BY_ID = "SELECT * FROM genres WHERE id = ?;"; + private static final String GET_ALL_GENRES = "SELECT * FROM genres ORDER BY id;"; + + public GenreDbStorage(JdbcTemplate jdbc, GenreRowMapper mapper) { + super(jdbc, mapper); + } + + /** + * Возвращает жанр по id + */ + public Genre getGenreById(int id) { + return findOne(GET_GENRE_BY_ID, id).orElseThrow(() -> { + String genreNotFound = "Жанр с id = " + id + " не найден"; + log.error(genreNotFound); + return new NotFoundException(genreNotFound); + }); + } + + /** + * Возвращает все жанры в виде списка + */ + public List getGenres() { + return findMany(GET_ALL_GENRES); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorage.java new file mode 100644 index 0000000..56abb51 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorage.java @@ -0,0 +1,41 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.storage.MpaStorage; +import ru.yandex.practicum.filmorate.storage.dao.mapper.MpaRowMapper; + +import java.util.List; + +@Repository +public class MpaDbStorage extends BaseStorage implements MpaStorage { + private static final Logger log = LoggerFactory.getLogger(MpaDbStorage.class); + private static final String GET_MPA_BY_ID = "SELECT * FROM mpa_ratings WHERE id = ?;"; + private static final String GET_ALL_MPA = "SELECT * FROM mpa_ratings ORDER BY id;"; + + public MpaDbStorage(JdbcTemplate jdbc, MpaRowMapper mapper) { + super(jdbc, mapper); + } + + /** + * Возвращает MPA рейтинг по id + */ + public Mpa getMpaById(int id) { + return findOne(GET_MPA_BY_ID, id).orElseThrow(() -> { + String mpaNotFound = "Фильм с id = " + id + " не найден"; + log.error(mpaNotFound); + return new NotFoundException(mpaNotFound); + }); + } + + /** + * Возвращает все MPA рейтинги в виде списка + */ + public List getMpaList() { + return findMany(GET_ALL_MPA); + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorage.java new file mode 100644 index 0000000..6130631 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorage.java @@ -0,0 +1,154 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import ru.yandex.practicum.filmorate.exception.InternalServerException; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.UserStorage; +import ru.yandex.practicum.filmorate.storage.dao.mapper.UserRowMapper; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Primary +@Repository("userDbStorage") +public class UserDbStorage extends BaseStorage implements UserStorage { + private static final Logger log = LoggerFactory.getLogger(UserDbStorage.class); + + private static final String GET_USER_BY_ID_QUERY = "SELECT * FROM users WHERE id = ?;"; + private static final String GET_USERS_QUERY = "SELECT * FROM users;"; + private static final String ADD_USER_QUERY = "INSERT INTO users (name, email, login, birthday)" + + "VALUES (?, ?, ?, ?);"; + private static final String UPDATE_USER_QUERY = "UPDATE users SET name = ?, email = ?, login = ?, birthday = ? " + + "WHERE id = ?;"; + private static final String GET_USER_FRIENDS = "SELECT friend_id FROM users_friendship WHERE user_id = ?;"; + private static final String ADD_USER_FRIEND = "INSERT INTO users_friendship (user_id, friend_id) VALUES (?, ?);"; + private static final String DELETE_USER_FRIEND = "DELETE FROM users_friendship WHERE user_id = ? " + + "AND friend_id = ?;"; + + @Autowired + public UserDbStorage(JdbcTemplate jdbc, UserRowMapper mapper) { + super(jdbc, mapper); + } + + /** + * Возвращает пользователя по id из БД + */ + @Override + public User getUserById(int id) { + User user = findOne(GET_USER_BY_ID_QUERY, id).orElseThrow(() -> { + String userNotFound = "Пользователь с id = " + id + " не найден"; + log.error(userNotFound); + return new NotFoundException(userNotFound); + }); + user.setFriends(getUserFriends(id)); + return user; + } + + /** + * Возвращает из БД всех пользователей в виде списка + */ + @Override + public List getUsers() { + try { + return findMany(GET_USERS_QUERY); + } catch (InternalServerException exception) { + log.error("Пользователи не найдены из-за внутренней ошибки"); + throw exception; + } + } + + /** + * Добавляет нового пользователя. + * Присваивает уникальный ID и сохраняет в БД. + * + * @param newUser объект пользователя, который нужно добавить + * @return созданный пользователь с присвоенным ID + */ + @Override + public User addUser(User newUser) { + int id = insert(ADD_USER_QUERY, newUser.getName(), newUser.getEmail(), newUser.getLogin(), + newUser.getBirthday()); + newUser.setId(id); + log.info("Пользователю с логином {} присвоен id {}", newUser.getLogin(), newUser.getId()); + + if (newUser.getFriends() == null) { + newUser.setFriends(new HashSet<>()); + } + + log.info("Пользователь {} добавлен в базу", newUser.getId()); + return newUser; + } + + /** + * Обновляет пользователя в БД, если он существует. + * + * @param updatedUser объект пользователя, который нужно обновить + * @return обновлённый пользователь + */ + @Override + public User updateUser(User updatedUser) { + try { + update(UPDATE_USER_QUERY, updatedUser.getName(), updatedUser.getEmail(), updatedUser.getLogin(), + updatedUser.getBirthday(), updatedUser.getId()); + + if (updatedUser.getFriends() == null) { + updatedUser.setFriends(new HashSet<>()); + } + + log.info("Пользователь {} обновлён", updatedUser.getId()); + return updatedUser; + } catch (InternalServerException exception) { + log.error("Пользователь {} не обновлён из-за внутренней ошибки", updatedUser.getId()); + throw exception; + } + } + + /** + * Добавляет в друзья пользователя другому пользователю. + * Делает запись в БД. + */ + @Override + public void addFriends(int userId, int friendId) { + try { + jdbc.update(ADD_USER_FRIEND, userId, friendId); + } catch (InternalServerException exception) { + log.error("Не удалось добавить пользователя {} в друзья {}", friendId, userId); + throw exception; + } + } + + /** + * Удаляет одного пользователя из друзей второго пользователя. + * Делает запись в БД. + */ + @Override + public void removeFriends(int userId, int friendId) { + try { + jdbc.update(DELETE_USER_FRIEND, userId, friendId); + } catch (InternalServerException exception) { + log.error("Не удалось удалить пользователя {} из друзей {}", friendId, userId); + throw exception; + } + } + + /** + * Возвращает список друзей пользователя в виде списка id + */ + @Override + public Set getUserFriends(Integer userId) { + try { + return new HashSet<>(jdbc.queryForList(GET_USER_FRIENDS, Integer.class, userId)); + } catch (InternalServerException exception) { + String getFriendsError = "Не удалось получить друзей пользователя " + userId; + log.error(getFriendsError); + throw exception; + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/FilmRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/FilmRowMapper.java new file mode 100644 index 0000000..6529d3e --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/FilmRowMapper.java @@ -0,0 +1,29 @@ +package ru.yandex.practicum.filmorate.storage.dao.mapper; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Film; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; + +@Component +public class FilmRowMapper implements RowMapper { + @Override + public Film mapRow(ResultSet resultSet, int rowNum) throws SQLException { + int id = resultSet.getInt("id"); + String name = resultSet.getString("name"); + String description = resultSet.getString("description"); + int duration = resultSet.getInt("duration"); + LocalDate releaseDate = resultSet.getDate("release_date").toLocalDate(); + + return Film.builder() + .id(id) + .name(name) + .description(description) + .duration(duration) + .releaseDate(releaseDate) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/GenreRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/GenreRowMapper.java new file mode 100644 index 0000000..e8257b2 --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/GenreRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.storage.dao.mapper; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Genre; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class GenreRowMapper implements RowMapper { + @Override + public Genre mapRow(ResultSet resultSet, int rowNum) throws SQLException { + Genre genre = new Genre(); + genre.setId(resultSet.getInt("id")); + genre.setName(resultSet.getString("name")); + return genre; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/MpaRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/MpaRowMapper.java new file mode 100644 index 0000000..c84987c --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/MpaRowMapper.java @@ -0,0 +1,19 @@ +package ru.yandex.practicum.filmorate.storage.dao.mapper; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.Mpa; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class MpaRowMapper implements RowMapper { + @Override + public Mpa mapRow(ResultSet resultSet, int rowNum) throws SQLException { + Mpa mpa = new Mpa(); + mpa.setId(resultSet.getInt("id")); + mpa.setName(resultSet.getString("name")); + return mpa; + } +} \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/UserRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/UserRowMapper.java new file mode 100644 index 0000000..99e93ab --- /dev/null +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/dao/mapper/UserRowMapper.java @@ -0,0 +1,33 @@ +package ru.yandex.practicum.filmorate.storage.dao.mapper; + +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; +import ru.yandex.practicum.filmorate.model.User; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; + +@Component +public class UserRowMapper implements RowMapper { + @Override + public User mapRow(ResultSet resultSet, int rowNum) throws SQLException { + int id = resultSet.getInt("id"); + String name = resultSet.getString("name"); + String email = resultSet.getString("email"); + String login = resultSet.getString("login"); + LocalDate birthday = resultSet.getDate("birthday").toLocalDate(); + Set friends = new HashSet<>(); + + return User.builder() + .id(id) + .name(name) + .email(email) + .login(login) + .birthday(birthday) + .friends(friends) + .build(); + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3eb70b5..bb52638 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,7 @@ -logging.level.org.zalando.logbook: TRACE - +spring.sql.init.mode=always +spring.datasource.url=jdbc:h2:file:./db/filmorate +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +sql.init.mode=always +h2.console.enabled=true \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..7bca888 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,28 @@ +DELETE FROM users_friendship; +DELETE FROM films_likes; +DELETE FROM films_genres; +DELETE FROM films; +DELETE FROM users; +DELETE FROM genres; +DELETE FROM mpa_ratings; + +ALTER TABLE mpa_ratings ALTER COLUMN id RESTART WITH 1; +INSERT INTO mpa_ratings (name) VALUES +('G'), ('PG'), ('PG-13'), ('R'), ('NC-17'); + +ALTER TABLE genres ALTER COLUMN id RESTART WITH 1; +INSERT INTO genres (name) VALUES +('Комедия'), ('Драма'), ('Мультфильм'), ('Триллер'), ('Документальный'), ('Боевик'); + +ALTER TABLE users ALTER COLUMN id RESTART WITH 1; +INSERT INTO users (name, email, login, birthday) VALUES +('Иван Иванов', 'ivan@mail.com', 'ivan_ivanov', '1990-05-15'), +('Мария Петрова', 'maria@mail.com', 'maria_pet', '1992-08-22'), +('Алексей Сидоров', 'alex@mail.com', 'alex_sid', '1988-11-30'), +('Елена Сидорова', 'elena@mail.com', 'elena_sid', '1988-11-30'); + +ALTER TABLE films ALTER COLUMN id RESTART WITH 1; +ALTER TABLE users_friendship ALTER COLUMN id RESTART WITH 1; +ALTER TABLE films_likes ALTER COLUMN id RESTART WITH 1; +ALTER TABLE films_genres ALTER COLUMN id RESTART WITH 1; + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..91cce65 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,49 @@ + +CREATE TABLE IF NOT EXISTS users ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255), + email VARCHAR(255) NOT NULL, + login VARCHAR(255) NOT NULL, + birthday DATE, + CONSTRAINT uq_user_email UNIQUE (email), + CONSTRAINT uq_user_login UNIQUE (login) +); + +CREATE TABLE IF NOT EXISTS genres ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(100) NOT NULL +); + +CREATE TABLE IF NOT EXISTS mpa_ratings ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(50) NOT NULL +); + +CREATE TABLE IF NOT EXISTS films ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description VARCHAR(1000), + duration INTEGER NOT NULL CHECK (duration > 0), + release_date DATE NOT NULL, + mpa_rating_id INTEGER REFERENCES mpa_ratings(id) ON DELETE SET NULL, + CONSTRAINT release_date_chk CHECK (release_date >= '1895-12-28') +); + +CREATE TABLE IF NOT EXISTS films_genres ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + film_id INTEGER NOT NULL REFERENCES films(id) ON DELETE CASCADE, + genre_id INTEGER NOT NULL REFERENCES genres(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS films_likes ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + film_id INTEGER NOT NULL REFERENCES films(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS users_friendship ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + friend_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT no_self_friendship_chk CHECK (user_id != friend_id) +); \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java index 660412e..a2ce107 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java +++ b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java @@ -1,13 +1,34 @@ package ru.yandex.practicum.filmorate; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.dao.UserDbStorage; -@SpringBootTest +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") class FilmorateApplicationTests { + private final UserDbStorage userStorage; @Test - void contextLoads() { - } + public void testFindUserById() { -} + Optional userOptional = Optional.of(userStorage.getUserById(1)); + + assertThat(userOptional) + .isPresent() + .hasValueSatisfying(user -> + assertThat(user).hasFieldOrPropertyWithValue("id", 1) + ); + } +} \ No newline at end of file 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 4a321e2..b0850cd 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java @@ -1,27 +1,34 @@ package ru.yandex.practicum.filmorate.controller; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.yandex.practicum.filmorate.dto.film.FilmDto; +import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest; +import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest; +import ru.yandex.practicum.filmorate.dto.user.NewUserRequest; 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; import java.util.List; - +import java.util.Objects; import static org.junit.jupiter.api.Assertions.*; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") class FilmControllerTest { private static final String FILM_NAME = "Фильм"; private static final String FILM_NAME_2 = "Второй фильм"; @@ -44,38 +51,41 @@ class FilmControllerTest { private static final String USER_NAME = "Вася"; private static final String USER_EMAIL = "mail@mail.ru"; + private static final String USER_EMAIL_2 = "mail2@mail.ru"; private static final String USER_LOGIN = "user"; + private static final String USER_LOGIN_2 = "user2"; private static final LocalDate USER_BIRTHDAY = LocalDate.of(1992, Month.DECEMBER, 12); private static final Integer USER_ID = 567; - FilmController filmController; - UserController userController; + private static final String DELETE_FILMS = "DELETE FROM films;"; + private static final String DELETE_USERS = "DELETE FROM users;"; + + private final FilmController filmController; + private final UserController userController; + private final JdbcTemplate jdbcTemplate; @BeforeEach - @DisplayName("Инициализирует контроллер") - void setUp() { - 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); + @DisplayName("Чистим БД") + void cleanDatabase() { + jdbcTemplate.update(DELETE_FILMS); + jdbcTemplate.update(DELETE_USERS); } @Test @DisplayName("При добавлении двух фильмов контроллер должен возвращать их") void test_getFilms_WhenAddedFilms_ShouldReturnAll() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.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) + NewFilmRequest film2 = NewFilmRequest.builder().name(FILM_NAME_2).description(FILM_DESCRIPTION_2) + .releaseDate(FILM_RELEASE_DATE_2) .duration(FILM_DURATION_2).build(); //when filmController.addFilm(film); filmController.addFilm(film2); - List filmsByController = filmController.getFilms().getBody(); + List filmsByController = filmController.getFilms().getBody(); //then assertNotNull(filmsByController, "В контроллере нет фильмов"); @@ -88,13 +98,13 @@ void test_getFilms_WhenAddedFilms_ShouldReturnAll() { @DisplayName("При добавлении фильма с корректными полями контроллер должен добавить его") void test_addFilm_WhenCorrectFields_ShouldAddToController() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); //when filmController.addFilm(film); - List filmsByController = filmController.getFilms().getBody(); + List filmsByController = filmController.getFilms().getBody(); //then assertNotNull(filmsByController, "В контроллере нет фильмов"); @@ -106,7 +116,7 @@ void test_addFilm_WhenCorrectFields_ShouldAddToController() { @DisplayName("При добавлении фильма с некорректным описанием контроллер должен выбросить ошибку") void test_addFilm_WhenIncorrectDescription_ShouldThrowsError() { //given && when - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION_INCORRECT) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION_INCORRECT) .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); @@ -120,7 +130,7 @@ void test_addFilm_WhenIncorrectDescription_ShouldThrowsError() { @DisplayName("При добавлении фильма с некорректной длительностью контроллер должен выбросить ошибку") void test_addFilm_WhenIncorrectDuration_ShouldThrowsError() { //given && when - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION_INCORRECT).build(); @@ -134,7 +144,7 @@ void test_addFilm_WhenIncorrectDuration_ShouldThrowsError() { @DisplayName("При добавлении фильма с некорректной датой релиза контроллер должен выбросить ошибку") void test_addFilm_WhenIncorrectReleaseDate_ShouldThrowsError() { //given && when - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) .releaseDate(FILM_RELEASE_DATE_INCORRECT) .duration(FILM_DURATION).build(); @@ -148,14 +158,18 @@ void test_addFilm_WhenIncorrectReleaseDate_ShouldThrowsError() { @DisplayName("При обновлении фильма с корректными данными контроллер должен обновить его") void test_updateFilm_WhenCorrectFields_ShouldUpdateInController() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - filmController.addFilm(film); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //when - Film updatedFilm = film.toBuilder().name(FILM_NAME_2).build(); + UpdateFilmRequest updatedFilm = UpdateFilmRequest.builder().id(filmId).name(FILM_NAME_2) + .description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); filmController.updateFilm(updatedFilm); - List filmsByController = filmController.getFilms().getBody(); + List filmsByController = filmController.getFilms().getBody(); //then assertNotNull(filmsByController, "В контроллере нет фильмов"); @@ -167,12 +181,16 @@ void test_updateFilm_WhenCorrectFields_ShouldUpdateInController() { @DisplayName("При обновлении фильма с некорректным описанием контроллер должен выбросить ошибку") void test_updateFilm_WhenIncorrectDescription_ShouldThrowsError() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - filmController.addFilm(film); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //when - Film updatedFilm = film.toBuilder().description(FILM_DESCRIPTION_INCORRECT).build(); + UpdateFilmRequest updatedFilm = UpdateFilmRequest.builder().id(filmId).name(FILM_NAME_2) + .description(FILM_DESCRIPTION_INCORRECT) + .releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); //then assertThrows(ValidationException.class, @@ -184,13 +202,16 @@ void test_updateFilm_WhenIncorrectDescription_ShouldThrowsError() { @DisplayName("При обновлении фильма с некорректной длительностью контроллер должен выбросить ошибку") void test_updateFilm_WhenIncorrectDuration_ShouldThrowsError() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - filmController.addFilm(film); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //when - Film updatedFilm = film.toBuilder().duration(FILM_DURATION_INCORRECT).build(); - + UpdateFilmRequest updatedFilm = UpdateFilmRequest.builder().id(filmId).name(FILM_NAME_2) + .description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION_INCORRECT).build(); //then assertThrows(ValidationException.class, () -> filmController.updateFilm(updatedFilm), @@ -201,12 +222,16 @@ void test_updateFilm_WhenIncorrectDuration_ShouldThrowsError() { @DisplayName("При добавлении фильма с некорректной датой релиза контроллер должен выбросить ошибку") void test_updateFilm_WhenIncorrectReleaseDate_ShouldThrowsError() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - filmController.addFilm(film); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //when - Film updatedFilm = film.toBuilder().releaseDate(FILM_RELEASE_DATE_INCORRECT).build(); + UpdateFilmRequest updatedFilm = UpdateFilmRequest.builder().id(filmId).name(FILM_NAME_2) + .description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE_INCORRECT) + .duration(FILM_DURATION).build(); //then assertThrows(ValidationException.class, @@ -218,12 +243,13 @@ void test_updateFilm_WhenIncorrectReleaseDate_ShouldThrowsError() { @DisplayName("При получении существующего фильма по id контроллер должен вернуть его") void test_getFilmById_WhenFilmExists_ShouldReturnOne() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - Film createdFilm = filmController.addFilm(film).getBody(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //when - Film findFilm = filmController.getFilmById(createdFilm.getId()).getBody(); + FilmDto findFilm = filmController.getFilmById(filmId).getBody(); //then assertNotNull(findFilm, "В контроллере нет фильмов"); @@ -235,7 +261,7 @@ void test_getFilmById_WhenFilmExists_ShouldReturnOne() { void test_getFilmById_WhenFilmNotExists_ShouldThrowsError() { //given && when && then assertThrows(NotFoundException.class, - () -> filmController.getFilmById(FILM_ID).getBody(), + () -> filmController.getFilmById(FILM_ID), "Из контроллера получен несуществующий фильм"); } @@ -243,16 +269,18 @@ void test_getFilmById_WhenFilmNotExists_ShouldThrowsError() { @DisplayName("При добавлении лайка существующему фильму от существующего пользователя должен вернуться OK 200") void test_addLikeByUser_WhenFilmAndUserExist_ShouldReturn200() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - Film createdFilm = filmController.addFilm(film).getBody(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); - User createdUser = userController.addUser(user).getBody(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY).build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //when - ResponseEntity response = filmController.addLikeByUser(createdFilm.getId(), createdUser.getId()); + ResponseEntity response = filmController.addLikeByUser(filmId, userId); //then assertEquals(HttpStatus.OK, response.getStatusCode(), "Вернулся не успешный ответ"); @@ -262,12 +290,13 @@ void test_addLikeByUser_WhenFilmAndUserExist_ShouldReturn200() { @DisplayName("При добавлении лайка существующему фильму от несуществующего пользователя должен вернуть ошибку") void test_addLikeByUser_WhenUserNotExists_ShouldThrowsError() { //given && when - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - Film createdFilm = filmController.addFilm(film).getBody(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //then - assertThrows(NotFoundException.class, () -> filmController.addLikeByUser(createdFilm.getId(), USER_ID), + assertThrows(NotFoundException.class, () -> filmController.addLikeByUser(filmId, USER_ID), "Удалось поставить лайк от несуществующего пользователя"); } @@ -275,11 +304,12 @@ void test_addLikeByUser_WhenUserNotExists_ShouldThrowsError() { @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(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY).build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //then - assertThrows(NotFoundException.class, () -> filmController.addLikeByUser(FILM_ID, createdUser.getId()), + assertThrows(NotFoundException.class, () -> filmController.addLikeByUser(FILM_ID, userId), "Удалось поставить лайк несуществующему фильму"); } @@ -287,16 +317,18 @@ void test_addLikeByUser_WhenFilmNotExists_ShouldThrowsError() { @DisplayName("При удалении лайка с существующего фильма от существующего пользователя должен вернуть OK 200") void test_removeLikeByUser_WhenFilmAndUserExist_ShouldReturn200() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - Film createdFilm = filmController.addFilm(film).getBody(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); - User createdUser = userController.addUser(user).getBody(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY).build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //when - filmController.addLikeByUser(createdFilm.getId(), createdUser.getId()); - ResponseEntity response = filmController.removeLikeByUser(createdFilm.getId(), createdUser.getId()); + filmController.addLikeByUser(filmId, userId); + ResponseEntity response = filmController.removeLikeByUser(filmId, userId); //then assertEquals(HttpStatus.OK, response.getStatusCode(), "Вернулся не успешный ответ"); @@ -306,12 +338,13 @@ void test_removeLikeByUser_WhenFilmAndUserExist_ShouldReturn200() { @DisplayName("При удалении лайка с существующего фильма от несуществующего пользователя должен вернуть ошибку") void test_removeLikeByUser_WhenUserNotExists_ShouldThrowsError() { //given && when - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) .duration(FILM_DURATION).build(); - Film createdFilm = filmController.addFilm(film).getBody(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); //then - assertThrows(NotFoundException.class, () -> filmController.removeLikeByUser(createdFilm.getId(), USER_ID), + assertThrows(NotFoundException.class, () -> filmController.removeLikeByUser(filmId, USER_ID), "Удалось убрать лайк от несуществующего пользователя"); } @@ -319,11 +352,12 @@ void test_removeLikeByUser_WhenUserNotExists_ShouldThrowsError() { @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(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY).build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //then - assertThrows(NotFoundException.class, () -> filmController.removeLikeByUser(FILM_ID, createdUser.getId()), + assertThrows(NotFoundException.class, () -> filmController.removeLikeByUser(FILM_ID, userId), "Удалось убрать лайк с несуществующего фильма"); } @@ -331,23 +365,29 @@ void test_removeLikeByUser_WhenFilmNotExists_ShouldThrowsError() { @DisplayName("При запросе одного фильма должен вернуть самый популярный") void test_getMostPopularFilms_WhenRequestOne_ShouldReturnOne() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.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) + NewFilmRequest film2 = NewFilmRequest.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(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); + int filmId2 = Objects.requireNonNull(filmController.addFilm(film2).getBody()).getId(); + + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY).build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL_2).login(USER_LOGIN_2) + .birthday(USER_BIRTHDAY).build(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); //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(); + filmController.addLikeByUser(filmId, userId); + filmController.addLikeByUser(filmId2, userId2); + filmController.addLikeByUser(filmId2, userId2); + List popularFilms = filmController.getMostPopularFilms(FILM_COUNT_1).getBody(); //then assertNotNull(popularFilms, "В контроллере нет популярных фильмов"); @@ -359,23 +399,29 @@ void test_getMostPopularFilms_WhenRequestOne_ShouldReturnOne() { @DisplayName("Если передано дефолтное количество фильмов, то должен вернуть все (до дефолтного включительно)") void test_getMostPopularFilms_WhenRequestNothing_ShouldReturnAll() { //given - Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + NewFilmRequest film = NewFilmRequest.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) + NewFilmRequest film2 = NewFilmRequest.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(); + int filmId = Objects.requireNonNull(filmController.addFilm(film).getBody()).getId(); + int filmId2 = Objects.requireNonNull(filmController.addFilm(film2).getBody()).getId(); + + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY).build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL_2).login(USER_LOGIN_2) + .birthday(USER_BIRTHDAY).build(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); //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(); + filmController.addLikeByUser(filmId, userId); + filmController.addLikeByUser(filmId2, userId2); + filmController.addLikeByUser(filmId2, userId2); + List popularFilms = filmController.getMostPopularFilms(FILM_DEFAULT_COUNT).getBody(); //then assertNotNull(popularFilms, "В контроллере нет популярных фильмов"); diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/GenreControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/GenreControllerTest.java new file mode 100644 index 0000000..0dbf263 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/GenreControllerTest.java @@ -0,0 +1,48 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import ru.yandex.practicum.filmorate.dto.GenreDto; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") +public class GenreControllerTest { + private static final int GENRE_ID = 1; + private static final String GENRE_NAME = "Комедия"; + private static final int GENRES_COUNT = 6; + private final GenreController genreController; + + @Test + @DisplayName("При запросе по id должен вернуть жанр") + void test_getGenreById_ShouldReturnById() { + //given && when + GenreDto genre = genreController.getGenreById(GENRE_ID).getBody(); + + //then + assertNotNull(genre, "В контроллере нет жанров"); + assertEquals(GENRE_NAME, genre.getName(), "В контроллере не верный жанр"); + } + + @Test + @DisplayName("При запросе должен вернуть все жанры") + void test_getUsers_ShouldReturnAll() { + //given && when + List genres = genreController.getGenres().getBody(); + + //then + assertNotNull(genres, "В контроллере нет жанров"); + assertEquals(GENRES_COUNT, genres.size(), "В контроллере не верное количество жанров"); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/controller/MpaControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/controller/MpaControllerTest.java new file mode 100644 index 0000000..a4e0385 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/MpaControllerTest.java @@ -0,0 +1,48 @@ +package ru.yandex.practicum.filmorate.controller; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import ru.yandex.practicum.filmorate.dto.MpaDto; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") +public class MpaControllerTest { + private static final int MPA_ID = 1; + private static final String MPA_NAME = "G"; + private static final int MPA_COUNT = 5; + private final MpaController mpaController; + + @Test + @DisplayName("При запросе по id должен вернуть рейтинг") + void test_getMpaById_ShouldReturnById() { + //given && when + MpaDto mpa = mpaController.getMpaById(MPA_ID).getBody(); + + //then + assertNotNull(mpa, "В контроллере нет рейтингом"); + assertEquals(MPA_NAME, mpa.getName(), "В контроллере не верный рейтинг"); + } + + @Test + @DisplayName("При запросе должен вернуть все рейтинги") + void test_getMpaList_ShouldReturnAll() { + //given && when + List mpaList = mpaController.getMpaList().getBody(); + + //then + assertNotNull(mpaList, "В контроллере нет рейтингов"); + assertEquals(MPA_COUNT, mpaList.size(), "В контроллере не верное количество рейтингов"); + } +} \ 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 3c76ba9..e68b1c5 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java @@ -1,23 +1,33 @@ package ru.yandex.practicum.filmorate.controller; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.yandex.practicum.filmorate.dto.user.NewUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UpdateUserRequest; +import ru.yandex.practicum.filmorate.dto.user.UserDto; 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; import java.util.List; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.*; +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") class UserControllerTest { private static final String USER_NAME = "Вася"; private static final String USER_NAME_2 = "Петя"; @@ -39,28 +49,32 @@ class UserControllerTest { private static final Integer USER_ID = 567; - UserController userController; + private static final String DELETE_USERS = "DELETE FROM users;"; + + private final UserController userController; + private final JdbcTemplate jdbcTemplate; @BeforeEach - @DisplayName("Инициализирует контроллер") - void setUp() { - UserStorage userStorage = new InMemoryUserStorage(); - UserService userService = new UserService(userStorage); - userController = new UserController(userService); + @DisplayName("Чистим БД") + void cleanDatabase() { + jdbcTemplate.update(DELETE_USERS); } @Test @DisplayName("При добавлении двух пользователей контроллер должен возвращать их") void test_getUsers_WhenAddedUsers_ShouldReturnAll() { //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(); - //when + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); userController.addUser(user); + + NewUserRequest user2 = NewUserRequest.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2) + .birthday(USER_BIRTHDAY_2) + .build(); userController.addUser(user2); - List usersByController = userController.getUsers().getBody(); + List usersByController = userController.getUsers().getBody(); //then assertNotNull(usersByController, "В контроллере нет пользователей"); @@ -75,11 +89,13 @@ void test_getUsers_WhenAddedUsers_ShouldReturnAll() { @DisplayName("При добавлении пользователя с корректными полями контроллер должен добавить его") void test_addUser_WhenCorrectFields_ShouldAddToController() { //given - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); //when userController.addUser(user); - List usersByController = userController.getUsers().getBody(); + List usersByController = userController.getUsers().getBody(); //then assertNotNull(usersByController, "В контроллере нет пользователей"); @@ -91,7 +107,7 @@ void test_addUser_WhenCorrectFields_ShouldAddToController() { @DisplayName("При добавлении пользователя с некорректным логином контроллер должен выбросить ошибку") void test_addUser_WhenIncorrectLogin_ShouldThrowsError() { //given && when - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN_INCORRECT) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN_INCORRECT) .birthday(USER_BIRTHDAY).build(); //then @@ -104,11 +120,13 @@ void test_addUser_WhenIncorrectLogin_ShouldThrowsError() { @DisplayName("При добавлении пользователя без имени в контроллере имя будет равно логину") void test_addUser_WhenNameIsNull_ShouldReturnNameWithLoginValue() { //given && when - User user = User.builder().email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + NewUserRequest user = NewUserRequest.builder().email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); //when userController.addUser(user); - List usersByController = userController.getUsers().getBody(); + List usersByController = userController.getUsers().getBody(); //then assertNotNull(usersByController, "В контроллере нет пользователей"); @@ -120,7 +138,7 @@ void test_addUser_WhenNameIsNull_ShouldReturnNameWithLoginValue() { @DisplayName("При добавлении пользователя с датой рождения в будущем контроллер должен выбросить ошибку") void test_addUser_WhenBirthdayInFuture_ShouldThrowsError() { //given && when - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) .birthday(USER_BIRTHDAY_INCORRECT).build(); //then @@ -133,13 +151,18 @@ void test_addUser_WhenBirthdayInFuture_ShouldThrowsError() { @DisplayName("При обновлении пользователя с корректными полями контроллер должен обновить его") void test_updateUser_WhenCorrectFields_ShouldUpdateInController() { //given - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); - userController.addUser(user); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //when - User updateUser = user.toBuilder().name(USER_NAME_2).build(); + UpdateUserRequest updateUser = UpdateUserRequest.builder().id(userId).name(USER_NAME_2).email(USER_EMAIL) + .login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); userController.updateUser(updateUser); - List usersByController = userController.getUsers().getBody(); + List usersByController = userController.getUsers().getBody(); //then assertNotNull(usersByController, "В контроллере нет пользователей"); @@ -151,11 +174,16 @@ void test_updateUser_WhenCorrectFields_ShouldUpdateInController() { @DisplayName("При обновлении пользователя с некорректным логином контроллер должен выбросить ошибку") void test_updateUser_WhenIncorrectLogin_ShouldThrowsError() { //given && when - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); - userController.addUser(user); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //when - User updateUser = user.toBuilder().login(USER_LOGIN_INCORRECT).build(); + UpdateUserRequest updateUser = UpdateUserRequest.builder().id(userId).name(USER_NAME).email(USER_EMAIL) + .login(USER_LOGIN_INCORRECT) + .birthday(USER_BIRTHDAY) + .build(); //then assertThrows(ValidationException.class, @@ -163,33 +191,21 @@ void test_updateUser_WhenIncorrectLogin_ShouldThrowsError() { "Пользователь обновлён с некорректным логином"); } - @Test - @DisplayName("При обновлении пользователя без имени в контроллере имя будет равно логину") - void test_updateUser_WhenNameIsNull_ShouldReturnNameWithLoginValue() { - //given && when - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); - userController.addUser(user); - - //when - User updateUser = user.toBuilder().name("").build(); - userController.updateUser(updateUser); - List usersByController = userController.getUsers().getBody(); - - //then - assertNotNull(usersByController, "В контроллере нет пользователей"); - assertEquals(1, usersByController.size(), "В контроллере не верное количество пользователей"); - assertEquals(USER_LOGIN, usersByController.get(0).getName(), "Имя пользователя не равно логину"); - } @Test @DisplayName("При обновлении пользователя с датой рождения в будущем контроллер должен выбросить ошибку") void test_updateUser_WhenBirthdayInFuture_ShouldThrowsError() { //given && when - User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); - userController.addUser(user); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //when - User updateUser = user.toBuilder().birthday(USER_BIRTHDAY_INCORRECT).build(); + UpdateUserRequest updateUser = UpdateUserRequest.builder().id(userId).name(USER_NAME).email(USER_EMAIL) + .login(USER_LOGIN) + .birthday(USER_BIRTHDAY_INCORRECT) + .build(); //then assertThrows(ValidationException.class, @@ -201,11 +217,14 @@ void test_updateUser_WhenBirthdayInFuture_ShouldThrowsError() { @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(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + //when - User findUser = userController.getUserById(createdUser.getId()).getBody(); + UserDto findUser = userController.getUserById(userId).getBody(); //then assertNotNull(findUser, "В контроллере нет пользователей"); @@ -225,14 +244,18 @@ void test_getUserById_WhenUserNotExists_ShouldThrowsError() { @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) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.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(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); //when - ResponseEntity response = userController.addFriend(createdUser1.getId(), createdUser2.getId()); + ResponseEntity response = userController.addFriend(userId, userId2); //then assertEquals(HttpStatus.OK, response.getStatusCode(), @@ -243,12 +266,14 @@ void test_addFriend_WhenUsersExist_ShouldReturn200() { @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(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //then assertThrows(NotFoundException.class, - () -> userController.addFriend(createdUser1.getId(), USER_ID), + () -> userController.addFriend(userId, USER_ID), "Удалось добавить в друзья не существующего пользователя"); } @@ -256,15 +281,19 @@ void test_addFriend_WhenOneUserNotExists_ShouldThrowsError() { @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) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) .build(); - User createdUser1 = userController.addUser(user).getBody(); - User createdUser2 = userController.addUser(user2).getBody(); - userController.addFriend(createdUser1.getId(), createdUser2.getId()); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2) + .birthday(USER_BIRTHDAY_2) + .build(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); + userController.addFriend(userId, userId2); //when - ResponseEntity response = userController.removeFriend(createdUser1.getId(), createdUser2.getId()); + ResponseEntity response = userController.removeFriend(userId, userId2); //then assertEquals(HttpStatus.OK, response.getStatusCode(), @@ -275,12 +304,14 @@ void test_removeFriend_WhenFriendsExist_ShouldReturn200() { @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(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //then assertThrows(NotFoundException.class, - () -> userController.removeFriend(createdUser1.getId(), USER_ID), + () -> userController.removeFriend(userId, USER_ID), "Удалось удалить из друзей несуществующего пользователя"); } @@ -288,15 +319,19 @@ void test_removeFriend_WhenOneFriendNotExist_ShouldReturn200() { @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) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.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(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); //when - userController.addFriend(createdUser1.getId(), createdUser2.getId()); - List userFriends = userController.getAllFriends(createdUser1.getId()).getBody(); + userController.addFriend(userId, userId2); + List userFriends = userController.getAllFriends(userId).getBody(); //then assertNotNull(userFriends, "У пользователя нет друзей"); @@ -307,11 +342,13 @@ void test_getAllFriends_WhenAddFriends_ShouldReturnOne() { @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(); + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) + .build(); + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); //when - List userFriends = userController.getAllFriends(createdUser1.getId()).getBody(); + List userFriends = userController.getAllFriends(userId).getBody(); //then assertNotNull(userFriends, "Список друзей не проинициализировался"); @@ -322,19 +359,25 @@ void test_getAllFriends_WhenNoFriends_ShouldReturnEmpty() { @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) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) .build(); - User user3 = User.builder().name(USER_NAME_3).email(USER_EMAIL_3).login(USER_LOGIN_3).birthday(USER_BIRTHDAY_3) + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.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(); - User createdUser3 = userController.addUser(user3).getBody(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); + + NewUserRequest user3 = NewUserRequest.builder().name(USER_NAME_3).email(USER_EMAIL_3).login(USER_LOGIN_3) + .birthday(USER_BIRTHDAY_3) + .build(); + int userId3 = Objects.requireNonNull(userController.addUser(user3).getBody()).getId(); //when - userController.addFriend(createdUser1.getId(), createdUser3.getId()); - userController.addFriend(createdUser2.getId(), createdUser3.getId()); - List commonFriends = userController.getCommonFriends(createdUser1.getId(), createdUser2.getId()).getBody(); + userController.addFriend(userId, userId3); + userController.addFriend(userId2, userId3); + List commonFriends = userController.getCommonFriends(userId, userId2).getBody(); //then assertNotNull(commonFriends, "У пользователя нет общих друзей"); @@ -345,19 +388,25 @@ void test_getCommonFriends_WhenAddSameFriend_ShouldReturnOne() { @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) + NewUserRequest user = NewUserRequest.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN) + .birthday(USER_BIRTHDAY) .build(); - User user3 = User.builder().name(USER_NAME_3).email(USER_EMAIL_3).login(USER_LOGIN_3).birthday(USER_BIRTHDAY_3) + int userId = Objects.requireNonNull(userController.addUser(user).getBody()).getId(); + + NewUserRequest user2 = NewUserRequest.builder().name(USER_NAME_2).email(USER_EMAIL_2).login(USER_LOGIN_2) + .birthday(USER_BIRTHDAY_2) + .build(); + int userId2 = Objects.requireNonNull(userController.addUser(user2).getBody()).getId(); + + NewUserRequest user3 = NewUserRequest.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(); + int userId3 = Objects.requireNonNull(userController.addUser(user3).getBody()).getId(); //when - userController.addFriend(createdUser1.getId(), createdUser1.getId()); - userController.addFriend(createdUser2.getId(), createdUser3.getId()); - List commonFriends = userController.getCommonFriends(createdUser1.getId(), createdUser2.getId()).getBody(); + userController.addFriend(userId, userId2); + userController.addFriend(userId2, userId3); + List commonFriends = userController.getCommonFriends(userId, userId2).getBody(); //then assertNotNull(commonFriends, "Список общих друзей не проинициализировался"); diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStorageTest.java new file mode 100644 index 0000000..18c2e09 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/FilmDbStorageTest.java @@ -0,0 +1,346 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.yandex.practicum.filmorate.exception.InternalServerException; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +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.UserStorage; + +import java.time.LocalDate; +import java.time.Month; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") +class FilmDbStorageTest { + private static final String FILM_NAME = "Фильм"; + private static final String FILM_NAME_2 = "Второй фильм"; + + private static final String FILM_DESCRIPTION = "Описание фильма"; + private static final String FILM_DESCRIPTION_2 = "Описание второго фильма"; + private static final String FILM_DESCRIPTION_INCORRECT = "Очень " + "очень".repeat(199) + "длинное описание"; + + private static final LocalDate FILM_RELEASE_DATE = LocalDate.of(1992, Month.DECEMBER, 12); + private static final LocalDate FILM_RELEASE_DATE_2 = LocalDate.of(2005, Month.APRIL, 28); + private static final LocalDate FILM_RELEASE_DATE_INCORRECT = LocalDate.of(1600, Month.DECEMBER, 5); + + private static final int FILM_DURATION = 100; + 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 String USER_NAME = "Вася"; + private static final String USER_EMAIL = "mail@mail.ru"; + private static final String USER_EMAIL_2 = "newMail@mail.ru"; + private static final String USER_LOGIN = "user"; + private static final String USER_LOGIN_2 = "newUser"; + private static final LocalDate USER_BIRTHDAY = LocalDate.of(1992, Month.DECEMBER, 12); + private static final Integer USER_ID = 567; + + private static final String DELETE_FILMS = "DELETE FROM films;"; + private static final String DELETE_USERS = "DELETE FROM users;"; + + private final FilmStorage filmStorage; + private final UserStorage userStorage; + private final JdbcTemplate jdbcTemplate; + + @BeforeEach + @DisplayName("Чистим БД") + void cleanDatabase() { + jdbcTemplate.update(DELETE_FILMS); + jdbcTemplate.update(DELETE_USERS); + } + + @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 = filmStorage.addFilm(film); + + //when + Film findFilm = filmStorage.getFilmById(createdFilm.getId()); + + //then + assertNotNull(findFilm, "В БД нет фильмов"); + assertEquals(FILM_NAME, findFilm.getName(), "В БД некорректный фильм"); + } + + @Test + @DisplayName("При получении несуществующего фильма по id хранилище должно вернуть ошибку") + void test_getFilmById_WhenFilmNotExists_ShouldThrowsError() { + //given && when && then + assertThrows(NotFoundException.class, + () -> filmStorage.getFilmById(FILM_ID), + "Из БД получен несуществующий фильм"); + } + + @Test + @DisplayName("При добавлении двух фильмов хранилище должно возвращать их") + void test_getFilms_WhenAddedFilms_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(); + + //when + filmStorage.addFilm(film); + filmStorage.addFilm(film2); + List filmsByController = filmStorage.getFilms(); + + //then + assertNotNull(filmsByController, "В БД нет фильмов"); + assertEquals(2, filmsByController.size(), "В БД не верное количество фильмов"); + assertEquals(FILM_NAME, filmsByController.get(0).getName(), "В БД не корректный 1-ый фильм"); + assertEquals(FILM_NAME_2, filmsByController.get(1).getName(), "В БД не корректный 2-ой фильм"); + } + + @Test + @DisplayName("При добавлении фильма с корректными полями хранилище должно добавить его") + void test_addFilm_WhenCorrectFields_ShouldAddToController() { + //given + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + + //when + filmStorage.addFilm(film); + List filmsByStorage = filmStorage.getFilms(); + + //then + assertNotNull(filmsByStorage, "В БД нет фильмов"); + assertEquals(1, filmsByStorage.size(), "В БД не верное количество фильмов"); + assertEquals(FILM_NAME, filmsByStorage.get(0).getName(), "В БД некорректный фильм"); + } + + @Test + @DisplayName("При добавлении фильма с некорректным описанием хранилище должно выбросить ошибку") + void test_addFilm_WhenIncorrectDescription_ShouldThrowsError() { + //given && when + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION_INCORRECT) + .releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + + //then + assertThrows(DataIntegrityViolationException.class, + () -> filmStorage.addFilm(film), + "Фильм может быть добавлен с некорректным описанием"); + } + + @Test + @DisplayName("При добавлении фильма с некорректной длительностью хранилище должно выбросить ошибку") + void test_addFilm_WhenIncorrectDuration_ShouldThrowsError() { + //given && when + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION_INCORRECT).build(); + + //then + assertThrows(DataIntegrityViolationException.class, + () -> filmStorage.addFilm(film), + "Фильм может быть добавлен с некорректной длительностью"); + } + + @Test + @DisplayName("При добавлении фильма с некорректной датой релиза хранилище должно выбросить ошибку") + void test_addFilm_WhenIncorrectReleaseDate_ShouldThrowsError() { + //given && when + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION) + .releaseDate(FILM_RELEASE_DATE_INCORRECT) + .duration(FILM_DURATION).build(); + + //then + assertThrows(DataIntegrityViolationException.class, + () -> filmStorage.addFilm(film), + "Фильм может быть добавлен с некорректной датой релиза"); + } + + @Test + @DisplayName("При обновлении фильма с корректными данными хранилище должно обновить его") + void test_updateFilm_WhenCorrectFields_ShouldUpdateInController() { + //given + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + filmStorage.addFilm(film); + + //when + Film updatedFilm = film.toBuilder().name(FILM_NAME_2).build(); + filmStorage.updateFilm(updatedFilm); + List filmsByController = filmStorage.getFilms(); + + //then + assertNotNull(filmsByController, "В БД нет фильмов"); + assertEquals(1, filmsByController.size(), "В БД не верное количество фильмов"); + assertEquals(FILM_NAME_2, filmsByController.get(0).getName(), "В БД некорректный фильм"); + } + + @Test + @DisplayName("При обновлении фильма с некорректным описанием хранилище должно выбросить ошибку") + void test_updateFilm_WhenIncorrectDescription_ShouldThrowsError() { + //given + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + filmStorage.addFilm(film); + + //when + Film updatedFilm = film.toBuilder().description(FILM_DESCRIPTION_INCORRECT).build(); + + //then + assertThrows(DataIntegrityViolationException.class, + () -> filmStorage.updateFilm(updatedFilm), + "Фильм может быть обновлён с некорректным описанием"); + } + + @Test + @DisplayName("При обновлении фильма с некорректной длительностью хранилище должно выбросить ошибку") + void test_updateFilm_WhenIncorrectDuration_ShouldThrowsError() { + //given + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + filmStorage.addFilm(film); + + //when + Film updatedFilm = film.toBuilder().duration(FILM_DURATION_INCORRECT).build(); + + //then + assertThrows(DataIntegrityViolationException.class, + () -> filmStorage.updateFilm(updatedFilm), + "Фильм может быть обновлён с некорректной длительностью"); + } + + @Test + @DisplayName("При добавлении фильма с некорректной датой релиза хранилище должно выбросить ошибку") + void test_updateFilm_WhenIncorrectReleaseDate_ShouldThrowsError() { + //given + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + filmStorage.addFilm(film); + + //when + Film updatedFilm = film.toBuilder().releaseDate(FILM_RELEASE_DATE_INCORRECT).build(); + + //then + assertThrows(DataIntegrityViolationException.class, + () -> filmStorage.updateFilm(updatedFilm), + "Фильм может быть обновлён с некорректной датой релиза"); + } + + @Test + @DisplayName("При добавлении лайка существующему фильму от существующего пользователя должен вернуться OK") + void test_addLikeByUser_WhenFilmAndUserExist_ShouldReturn200() { + //given && when + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + Film createdFilm = filmStorage.addFilm(film); + + User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + User createdUser = userStorage.addUser(user); + + //then + assertDoesNotThrow(() -> filmStorage.addLike(createdFilm.getId(), createdUser.getId()), + "Не удалось поставить лайк существующему фильму"); + } + + @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 = userStorage.addUser(user); + + //then + assertThrows(DataIntegrityViolationException.class, () -> filmStorage.addLike(FILM_ID, createdUser.getId()), + "Удалось поставить лайк несуществующему фильму"); + } + + @Test + @DisplayName("При удалении лайка с существующего фильма от существующего пользователя должен вернуть OK") + void test_removeLikeByUser_WhenFilmAndUserExist_ShouldReturnOk() { + //given + Film film = Film.builder().name(FILM_NAME).description(FILM_DESCRIPTION).releaseDate(FILM_RELEASE_DATE) + .duration(FILM_DURATION).build(); + Film createdFilm = filmStorage.addFilm(film); + + User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + User createdUser = userStorage.addUser(user); + + //when + filmStorage.addLike(createdFilm.getId(), createdUser.getId()); + + //then + assertDoesNotThrow(() -> filmStorage.removeLike(createdFilm.getId(), createdUser.getId()), + "Не удалось поставить лайк существующему фильму"); + } + + @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 = filmStorage.addFilm(film); + + //then + assertThrows(InternalServerException.class, () -> filmStorage.removeLike(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 = userStorage.addUser(user); + + //then + assertThrows(InternalServerException.class, () -> filmStorage.removeLike(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 = filmStorage.addFilm(film); + Film createdFilm2 = filmStorage.addFilm(film2); + + User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + User createdUser = userStorage.addUser(user); + User user2 = User.builder().name(USER_NAME).email(USER_EMAIL_2).login(USER_LOGIN_2).birthday(USER_BIRTHDAY).build(); + User createdUser2 = userStorage.addUser(user2); + + //when + filmStorage.addLike(createdFilm.getId(), createdUser.getId()); + filmStorage.addLike(createdFilm2.getId(), createdUser.getId()); + filmStorage.addLike(createdFilm2.getId(), createdUser2.getId()); + List popularFilms = filmStorage.getMostPopularFilms(FILM_COUNT_1); + + //then + assertNotNull(popularFilms, "В БД нет популярных фильмов"); + assertEquals(1, popularFilms.size(), "В БД не верное количество фильмов"); + assertEquals(FILM_NAME_2, popularFilms.get(0).getName(), "В БД не корректный фильм"); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorageTest.java new file mode 100644 index 0000000..012ed08 --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/GenreDbStorageTest.java @@ -0,0 +1,48 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import ru.yandex.practicum.filmorate.model.Genre; +import ru.yandex.practicum.filmorate.storage.GenreStorage; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") +class GenreDbStorageTest { + private static final int GENRE_ID = 1; + private static final String GENRE_NAME = "Комедия"; + private static final int GENRES_COUNT = 6; + private final GenreStorage genreStorage; + + @Test + @DisplayName("При запросе по id должен вернуть жанр") + void test_getGenreById_ShouldReturnById() { + //given && when + Genre genre = genreStorage.getGenreById(GENRE_ID); + + //then + assertNotNull(genre, "В контроллере нет жанров"); + assertEquals(GENRE_NAME, genre.getName(), "В контроллере не верный жанр"); + } + + @Test + @DisplayName("При запросе должен вернуть все жанры") + void test_getUsers_ShouldReturnAll() { + //given && when + List genres = genreStorage.getGenres(); + + //then + assertNotNull(genres, "В контроллере нет жанров"); + assertEquals(GENRES_COUNT, genres.size(), "В контроллере не верное количество жанров"); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorageTest.java new file mode 100644 index 0000000..d255b4c --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/MpaDbStorageTest.java @@ -0,0 +1,48 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import ru.yandex.practicum.filmorate.model.Mpa; +import ru.yandex.practicum.filmorate.storage.MpaStorage; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") +class MpaDbStorageTest { + private static final int MPA_ID = 1; + private static final String MPA_NAME = "G"; + private static final int MPA_COUNT = 5; + private final MpaStorage mpaStorage; + + @Test + @DisplayName("При запросе по id должен вернуть жанр") + void test_getMpaById_ShouldReturnById() { + //given && when + Mpa mpa = mpaStorage.getMpaById(MPA_ID); + + //then + assertNotNull(mpa, "В контроллере нет рейтингом"); + assertEquals(MPA_NAME, mpa.getName(), "В контроллере не верный рейтинг"); + } + + @Test + @DisplayName("При запросе должен вернуть все жанры") + void test_getMpaList_ShouldReturnAll() { + //given && when + List mpaList = mpaStorage.getMpaList(); + + //then + assertNotNull(mpaList, "В контроллере нет рейтингов"); + assertEquals(MPA_COUNT, mpaList.size(), "В контроллере не верное количество рейтингов"); + } +} \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorageTest.java new file mode 100644 index 0000000..98d5fbe --- /dev/null +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/dao/UserDbStorageTest.java @@ -0,0 +1,186 @@ +package ru.yandex.practicum.filmorate.storage.dao; + +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.yandex.practicum.filmorate.exception.NotFoundException; +import ru.yandex.practicum.filmorate.model.User; +import ru.yandex.practicum.filmorate.storage.UserStorage; + +import java.time.LocalDate; +import java.time.Month; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@JdbcTest +@AutoConfigureTestDatabase +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@ComponentScan(basePackages = "ru.yandex.practicum.filmorate") +class UserDbStorageTest { + private static final String USER_NAME = "Вася"; + private static final String USER_NAME_2 = "Петя"; + + private static final String USER_EMAIL = "mail@mail.ru"; + private static final String USER_EMAIL_2 = "newMail@mail.ru"; + + private static final String USER_LOGIN = "user"; + private static final String USER_LOGIN_2 = "newUser"; + + 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 Integer USER_ID = 567; + private static final String DELETE_USERS = "DELETE FROM users;"; + + private final UserStorage userStorage; + private final JdbcTemplate jdbcTemplate; + + @BeforeEach + @DisplayName("Чистим БД") + void cleanDatabase() { + jdbcTemplate.update(DELETE_USERS); + } + + @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 = userStorage.addUser(user); + + //when + User findUser = userStorage.getUserById(createdUser.getId()); + + //then + assertNotNull(findUser, "В БД нет пользователей"); + assertEquals(USER_NAME, findUser.getName(), "В БД некорректный пользователь"); + } + + @Test + @DisplayName("При получении несуществующего пользователя по id хранилище должно выбросить ошибку") + void test_getUserById_WhenUserNotExists_ShouldThrowsError() { + //given && when && then + assertThrows(NotFoundException.class, + () -> userStorage.getUserById(USER_ID), + "Из контроллера получен несуществующий пользователь"); + } + + @Test + @DisplayName("При добавлении двух пользователей хранилище должно возвращать их") + void test_getUsers_WhenAddedUsers_ShouldReturnAll() { + //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(); + + //when + userStorage.addUser(user); + userStorage.addUser(user2); + List usersByController = userStorage.getUsers(); + + //then + assertNotNull(usersByController, "В БД нет пользователей"); + assertEquals(2, usersByController.size(), "В БД не верное количество пользователей"); + assertEquals(USER_NAME, usersByController.get(0).getName(), + "В БД не корректный 1-ый пользователь"); + assertEquals(USER_NAME_2, usersByController.get(1).getName(), + "В БД не корректный 2-ой пользователь"); + } + + @Test + @DisplayName("При добавлении пользователя с корректными полями хранилище должно добавить его") + void test_addUser_WhenCorrectFields_ShouldAddToStorage() { + //given + User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + + //when + userStorage.addUser(user); + List usersByController = userStorage.getUsers(); + + //then + assertNotNull(usersByController, "В БД нет пользователей"); + assertEquals(1, usersByController.size(), "В БД не верное количество пользователей"); + assertEquals(USER_NAME, usersByController.get(0).getName(), "В БД некорректный пользователь"); + } + + @Test + @DisplayName("При добавлении в друзья существующих пользователей список друзей должен обновиться") + void test_addFriends_WhenUsersExist_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 = userStorage.addUser(user); + User createdUser2 = userStorage.addUser(user2); + + //when + userStorage.addFriends(createdUser1.getId(), createdUser2.getId()); + List friendsId = new ArrayList<>(userStorage.getUserFriends(createdUser1.getId())); + + //then + assertEquals(1, friendsId.size(), + "При добавлении в друзья список друзей остался пуст"); + } + + @Test + @DisplayName("При удалении из друзей пользователя список друзей должен обновиться") + void test_removeFriends_WhenFriendsExist_ShouldReturnZero() { + //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 = userStorage.addUser(user); + User createdUser2 = userStorage.addUser(user2); + userStorage.addFriends(createdUser1.getId(), createdUser2.getId()); + + //when + userStorage.removeFriends(createdUser1.getId(), createdUser2.getId()); + List friendsId = new ArrayList<>(userStorage.getUserFriends(createdUser1.getId())); + + //then + assertEquals(0, friendsId.size(), + "При удалении из друзей пользователь остался в друзьях"); + } + + @Test + @DisplayName("При получении всех друзей пользователя должен возвращаться корректный список") + void test_getUserFriends_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 = userStorage.addUser(user); + User createdUser2 = userStorage.addUser(user2); + + //when + userStorage.addFriends(createdUser1.getId(), createdUser2.getId()); + List friendsId = new ArrayList<>(userStorage.getUserFriends(createdUser1.getId())); + + //then + assertEquals(1, friendsId.size(), + "При добавлении в друзья список друзей остался пуст"); + } + + @Test + @DisplayName("При отсутствии друзей у пользователя должен возвращаться пустой список") + void test_getUserFriends_WhenNoFriends_ShouldReturnEmpty() { + //given + User user = User.builder().name(USER_NAME).email(USER_EMAIL).login(USER_LOGIN).birthday(USER_BIRTHDAY).build(); + User createdUser1 = userStorage.addUser(user); + + //when + List userFriends = new ArrayList<>(userStorage.getUserFriends(createdUser1.getId())); + + //then + assertNotNull(userFriends, "Список друзей не проинициализировался"); + assertEquals(0, userFriends.size(), "Список друзей не пустой"); + } +} \ No newline at end of file