getFilms() {
return filmService.getAllFilms();
}
+ /**
+ * Handles GET method.
+ * Retrieves film from the storage.
+ *
+ * @param id film`s id. Must be positive number
+ * @return Film.
+ */
+ @GetMapping("/{id}")
+ public Film getFilm(@PathVariable @Positive Long id) {
+ return filmService.getFilm(id);
+ }
+
/**
* Handles POST method.
*
Creates film in storage after validation.
@@ -70,6 +82,17 @@ public Film updateFilm(@Valid @RequestBody Film newFilm) {
return filmService.updateFilm(newFilm);
}
+ /**
+ * Handles DELETE method.
+ *
Deletes film from the storage.
+ *
+ * @param id film`s id. Must be positive number
+ */
+ @DeleteMapping("/{id}")
+ public void deleteFilm(@PathVariable @Positive Long id) {
+ filmService.deleteFilm(id);
+ }
+
/**
* Handles GET method.
*
Retrieves top {@code count} popular films based on likes.
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..2ee59a0
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/GenreController.java
@@ -0,0 +1,32 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import jakarta.validation.constraints.Positive;
+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.model.Genre;
+import ru.yandex.practicum.filmorate.service.film.GenreService;
+
+import java.util.Collection;
+
+@RestController
+@RequestMapping("/genres")
+public class GenreController {
+
+ private final GenreService service;
+
+ public GenreController(GenreService service) {
+ this.service = service;
+ }
+
+ @GetMapping("/{id}")
+ public Genre getGenre(@PathVariable @Positive Long id) {
+ return service.getGenre(id);
+ }
+
+ @GetMapping
+ public Collection getAll() {
+ return service.getAll();
+ }
+}
\ No newline at end of file
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..8343f8d
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/MpaController.java
@@ -0,0 +1,32 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import jakarta.validation.constraints.Positive;
+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.model.Mpa;
+import ru.yandex.practicum.filmorate.service.film.MpaService;
+
+import java.util.Collection;
+
+@RestController
+@RequestMapping("/mpa")
+public class MpaController {
+
+ private final MpaService service;
+
+ public MpaController(MpaService service) {
+ this.service = service;
+ }
+
+ @GetMapping("/{id}")
+ public Mpa getMpa(@PathVariable @Positive Long id) {
+ return service.getMpa(id);
+ }
+
+ @GetMapping
+ public Collection getAll() {
+ return service.getAll();
+ }
+}
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 975e081..5241b6c 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -37,6 +37,18 @@ public Collection getUsers() {
return userService.getAllUsers();
}
+ /**
+ * Handles GET method.
+ * Retrieves user from the storage.
+ *
+ * @param id user`s id. Must be positive number
+ * @return User.
+ */
+ @GetMapping("/{id}")
+ public User getUser(@PathVariable @Positive Long id) {
+ return userService.getUser(id);
+ }
+
/**
* Handles POST method.
*
Creates user in storage after validation.
@@ -67,6 +79,17 @@ public User updateUser(@Valid @RequestBody User newUser) {
return userService.updateUser(newUser);
}
+ /**
+ * Handles DELETE method.
+ *
Deletes user from the storage.
+ *
+ * @param id user`s id. Must be positive number
+ */
+ @DeleteMapping("/{id}")
+ public void deleteUser(@PathVariable @Positive Long id) {
+ userService.deleteUser(id);
+ }
+
/**
* Handles GET method.
*
Return collection of user`s friends.
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java b/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java
index 42ce9ce..ad5a985 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/GlobalExceptionHandler.java
@@ -5,6 +5,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
@@ -102,6 +103,33 @@ public ResponseEntity handleMismatchAndConstraintViolation(
return createBadRequest(request.getRequestURI(), Map.of("error", "Invalid request format"));
}
+ @ExceptionHandler(DataIntegrityViolationException.class)
+ public ResponseEntity handleDataIntegrityViolationException(
+ DataIntegrityViolationException ex,
+ HttpServletRequest request) {
+
+ logInfo(ex, "Handling DataIntegrityViolationException");
+ String msg = ex.getMessage().toLowerCase();
+ HttpStatus status = HttpStatus.BAD_REQUEST;
+ String errorMsg = "Invalid request format";
+
+ if (msg.contains("foreign key")) {
+
+ if (msg.contains("mpa")) {
+ errorMsg = "Mpa не найден";
+ status = HttpStatus.NOT_FOUND;
+ } else if (msg.contains("genre")) {
+ errorMsg = "Один из указанных жанров не найден";
+ status = HttpStatus.NOT_FOUND;
+ }
+ }
+
+ return createResponseEntity(
+ status,
+ request.getRequestURI(),
+ Map.of("error", errorMsg));
+ }
+
/**
* Helper for HttpMessageNotReadableException handler
* Extracts meaningful error messages from JSON parsing exceptions.
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 6fe8c60..6b59539 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -9,6 +9,8 @@
import ru.yandex.practicum.filmorate.validation.ValidReleaseDate;
import java.time.LocalDate;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
@@ -54,4 +56,8 @@ public Film(Long id, String name, String description, LocalDate releaseDate, Int
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long likes = 0L;
+
+ private Mpa mpa;
+
+ private Set genres = ConcurrentHashMap.newKeySet();
}
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..b422c45
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Genre.java
@@ -0,0 +1,16 @@
+package ru.yandex.practicum.filmorate.model;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Genre {
+
+ @NotNull
+ private Long 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..bc8733e
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Mpa.java
@@ -0,0 +1,16 @@
+package ru.yandex.practicum.filmorate.model;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Mpa {
+
+ @NotNull
+ private Long id;
+ private String name;
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java
index b53d904..1b98a88 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java
@@ -1,15 +1,17 @@
package ru.yandex.practicum.filmorate.service.film;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.storage.film.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.film.GenreStorage;
import ru.yandex.practicum.filmorate.storage.film.LikeStorage;
import ru.yandex.practicum.filmorate.storage.user.UserStorage;
import java.util.Collection;
-import java.util.Comparator;
/**
* Main service for film operations and business logic.
@@ -22,19 +24,25 @@
* @see FilmStorage
*/
@Service
+@Transactional
public class FilmService implements FilmServiceInterface {
private final FilmStorage filmStorage;
private final UserStorage userStorage;
private final LikeStorage likeStorage;
+ private final GenreStorage genreStorage;
/**
* Constructor for dependency injection
*/
- public FilmService(FilmStorage filmStorage, UserStorage userStorage, LikeStorage likeStorage) {
+ public FilmService(@Qualifier("DbFilmStorage") FilmStorage filmStorage,
+ @Qualifier("DbUserStorage") UserStorage userStorage,
+ @Qualifier("DbLikeStorage") LikeStorage likeStorage,
+ @Qualifier("DbGenreStorage") GenreStorage genreStorage) {
this.filmStorage = filmStorage;
this.userStorage = userStorage;
this.likeStorage = likeStorage;
+ this.genreStorage = genreStorage;
}
/**
@@ -68,9 +76,13 @@ public Collection getAllFilms() {
*/
@Override
public Film addFilm(Film film) {
- Film returnFilm = filmStorage.add(film);
- likeStorage.initializeLikesSet(returnFilm.getId());
- return returnFilm;
+ film = filmStorage.add(film);
+
+ if (film.getGenres() != null && !film.getGenres().isEmpty()) {
+ genreStorage.addGenresToFilm(film);
+ }
+
+ return film;
}
/**
@@ -87,7 +99,13 @@ public Film updateFilm(Film film) {
}
throwIfNotFound(film.getId());
- return filmStorage.update(film);
+ film = filmStorage.update(film);
+
+ if (film.getGenres() != null && !film.getGenres().isEmpty()) {
+ genreStorage.updateFilmGenres(film);
+ }
+
+ return film;
}
/**
@@ -100,7 +118,7 @@ public Film updateFilm(Film film) {
public void deleteFilm(Long id) {
throwIfNotFound(id);
filmStorage.remove(id);
- likeStorage.clearLikesSet(id);
+ likeStorage.clearLikes(id);
}
/**
@@ -119,9 +137,10 @@ public Film addLike(Long filmId, Long userId) {
}
Long newLikes = likeStorage.addLike(filmId, userId);
- filmStorage.get(filmId).setLikes(newLikes);
+ Film film = filmStorage.get(filmId);
+ film.setLikes(newLikes);
- return filmStorage.get(filmId);
+ return film;
}
/**
@@ -140,9 +159,10 @@ public Film removeLike(Long filmId, Long userId) {
}
Long newLikes = likeStorage.deleteLike(filmId, userId);
- filmStorage.get(filmId).setLikes(newLikes);
+ Film film = filmStorage.get(filmId);
+ film.setLikes(newLikes);
- return filmStorage.get(filmId);
+ return film;
}
/**
@@ -153,11 +173,7 @@ public Film removeLike(Long filmId, Long userId) {
*/
@Override
public Collection getTopFilms(Long count) {
-
- return filmStorage.getAll().stream()
- .sorted(Comparator.comparingLong(Film::getLikes).reversed())
- .limit(count)
- .toList();
+ return filmStorage.getTopFilms(count);
}
@Override
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/film/GenreService.java b/src/main/java/ru/yandex/practicum/filmorate/service/film/GenreService.java
new file mode 100644
index 0000000..dd774a9
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/film/GenreService.java
@@ -0,0 +1,27 @@
+package ru.yandex.practicum.filmorate.service.film;
+
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.storage.film.GenreStorage;
+
+import java.util.Collection;
+
+@Service
+public class GenreService {
+
+ private final GenreStorage storage;
+
+ public GenreService(GenreStorage storage) {
+ this.storage = storage;
+ }
+
+ public Genre getGenre(Long id) {
+ return storage.getGenre(id)
+ .orElseThrow(() -> new NotFoundException("Жанр с id = " + id + " не найден"));
+ }
+
+ public Collection getAll() {
+ return storage.getAll();
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/film/MpaService.java b/src/main/java/ru/yandex/practicum/filmorate/service/film/MpaService.java
new file mode 100644
index 0000000..38353e7
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/film/MpaService.java
@@ -0,0 +1,27 @@
+package ru.yandex.practicum.filmorate.service.film;
+
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.storage.film.MpaStorage;
+
+import java.util.Collection;
+
+@Service
+public class MpaService {
+
+ private final MpaStorage storage;
+
+ public MpaService(MpaStorage storage) {
+ this.storage = storage;
+ }
+
+ public Mpa getMpa(Long id) {
+ return storage.getMpa(id)
+ .orElseThrow(() -> new NotFoundException("Mpa с id = " + id + " не найден"));
+ }
+
+ public Collection getAll() {
+ return storage.getAll();
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java
index eff4749..a827a79 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/user/UserService.java
@@ -1,6 +1,8 @@
package ru.yandex.practicum.filmorate.service.user;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.model.User;
@@ -22,6 +24,7 @@
* @see UserStorage
*/
@Service
+@Transactional
public class UserService implements UserServiceInterface {
private final UserStorage userStorage;
@@ -30,7 +33,8 @@ public class UserService implements UserServiceInterface {
/**
* Constructor for dependency injection
*/
- public UserService(UserStorage userStorage, FriendShipStorage friendShipStorage) {
+ public UserService(@Qualifier("DbUserStorage") UserStorage userStorage,
+ @Qualifier("DbFriendShipStorage") FriendShipStorage friendShipStorage) {
this.userStorage = userStorage;
this.friendShipStorage = friendShipStorage;
}
@@ -69,10 +73,7 @@ public User addUser(User user) {
user.setName(user.getLogin());
}
- User returnUser = userStorage.add(user);
- friendShipStorage.initializeFriendsSet(returnUser.getId());
-
- return returnUser;
+ return userStorage.add(user);
}
/**
@@ -103,11 +104,7 @@ public User updateUser(User user) {
@Override
public void deleteUser(Long id) {
throwIfNotFound(id);
-
- Set friendIds = friendShipStorage.getFriends(id);
- friendIds.forEach(friendId -> friendShipStorage.getFriends(friendId).remove(id));
- friendShipStorage.clearFriendsSet(id);
-
+ friendShipStorage.deleteUserFromAllFriends(id);
userStorage.remove(id);
}
@@ -129,7 +126,6 @@ public void addFriend(Long senderId, Long receiverId) {
throwIfNotFound(receiverId);
friendShipStorage.addFriend(senderId, receiverId);
- friendShipStorage.addFriend(receiverId, senderId);
}
/**
@@ -146,7 +142,6 @@ public void deleteFriend(Long senderId, Long receiverId) {
throwIfNotFound(receiverId);
friendShipStorage.deleteFriend(senderId, receiverId);
- friendShipStorage.deleteFriend(receiverId, senderId);
}
/**
@@ -157,10 +152,7 @@ public void deleteFriend(Long senderId, Long receiverId) {
@Override
public Collection getFriends(Long id) {
throwIfNotFound(id);
-
- return friendShipStorage.getFriends(id).stream()
- .map(userStorage::get)
- .toList();
+ return userStorage.getAllFromCollection(friendShipStorage.getFriends(id));
}
/**
@@ -176,13 +168,8 @@ public Collection getCommonFriends(Long id, Long otherId) {
throwIfNotFound(id);
throwIfNotFound(otherId);
- Set friends = friendShipStorage.getFriends(id);
- Set otherFriends = friendShipStorage.getFriends(otherId);
-
- return friends.stream()
- .filter(otherFriends::contains)
- .map(userStorage::get)
- .toList();
+ Set ids = friendShipStorage.getCommonFriends(id, otherId);
+ return userStorage.getAllFromCollection(ids);
}
@Override
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/BaseDao.java b/src/main/java/ru/yandex/practicum/filmorate/storage/BaseDao.java
new file mode 100644
index 0000000..19057ce
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/BaseDao.java
@@ -0,0 +1,36 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+import java.util.Collection;
+import java.util.Map;
+
+public abstract class BaseDao {
+ protected final JdbcTemplate jdbc;
+ protected final NamedParameterJdbcTemplate namedJdbc;
+ protected final RowMapper mapper;
+
+ public BaseDao(JdbcTemplate jdbc, RowMapper mapper) {
+ this.jdbc = jdbc;
+ this.namedJdbc = new NamedParameterJdbcTemplate(jdbc);
+ this.mapper = mapper;
+ }
+
+ public void update(String sql, Map map) {
+ namedJdbc.update(sql, map);
+ }
+
+ public void remove(String sql, Object... params) {
+ jdbc.update(sql, params);
+ }
+
+ public T get(String sql, Object... params) {
+ return jdbc.queryForObject(sql, mapper, params);
+ }
+
+ public Collection getAll(String sql, Object... params) {
+ return jdbc.query(sql, mapper, params);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/BasicStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/BasicStorage.java
index 0d5f7d7..ca99808 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/BasicStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/BasicStorage.java
@@ -16,6 +16,4 @@ public interface BasicStorage {
boolean contains(Long id);
- int size();
-
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java
index 6e55005..ed5cc24 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java
@@ -3,5 +3,8 @@
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.storage.BasicStorage;
+import java.util.Collection;
+
public interface FilmStorage extends BasicStorage {
+ Collection getTopFilms(Long count);
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/GenreStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/GenreStorage.java
new file mode 100644
index 0000000..f30feba
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/GenreStorage.java
@@ -0,0 +1,18 @@
+package ru.yandex.practicum.filmorate.storage.film;
+
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+
+import java.util.Collection;
+import java.util.Optional;
+
+public interface GenreStorage {
+
+ Optional getGenre(Long id);
+
+ Collection getAll();
+
+ void addGenresToFilm(Film film);
+
+ void updateFilmGenres(Film film);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/LikeStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/LikeStorage.java
index c2cc79e..8ba7338 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/LikeStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/LikeStorage.java
@@ -2,9 +2,8 @@
public interface LikeStorage {
- void initializeLikesSet(Long id);
-
- void clearLikesSet(Long id);
+ default void clearLikes(Long id) {
+ }
Long addLike(Long filmId, Long userId);
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/MpaStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/MpaStorage.java
new file mode 100644
index 0000000..01558fc
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/MpaStorage.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate.storage.film;
+
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+import java.util.Collection;
+import java.util.Optional;
+
+public interface MpaStorage {
+
+ Optional getMpa(Long id);
+
+ Collection getAll();
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbFilmStorage.java
new file mode 100644
index 0000000..ea83281
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbFilmStorage.java
@@ -0,0 +1,120 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.storage.BaseDao;
+import ru.yandex.practicum.filmorate.storage.film.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.mappers.FilmMapper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+@Repository("DbFilmStorage")
+public class DbFilmStorage extends BaseDao implements FilmStorage {
+
+ private static final String SELECT_FIELDS = """
+ SELECT f.*,
+ COUNT(l.film_id) AS likes,
+ m.mpa_id,
+ m.name AS mpa_name,
+ ARRAY_AGG(g.genre_id) AS genre_ids,
+ ARRAY_AGG(g.name) AS genre_names
+ """;
+
+ private static final String FROM_JOIN_EVERYTHING = """
+ FROM films AS f
+ LEFT JOIN mpa AS m ON f.mpa_id = m.mpa_id
+ LEFT JOIN likes AS l ON f.film_id = l.film_id
+ LEFT JOIN films_genres AS fg ON f.film_id = fg.film_id
+ LEFT JOIN genres AS g ON fg.genre_id = g.genre_id""";
+
+ private static final String GROUP_BY = "GROUP BY f.film_id";
+ private static final String EXISTS_QUERY = "SELECT EXISTS(SELECT 1 FROM films WHERE film_id = ?)";
+ private static final String DELETE_QUERY = "DELETE FROM films WHERE film_id = ?";
+
+ private static final String SELECT_BY_ID_QUERY = String.format("%s %s WHERE f.film_id = ? %s",
+ SELECT_FIELDS, FROM_JOIN_EVERYTHING, GROUP_BY);
+
+ private static final String SELECT_ALL_QUERY = String.format("%s %s %s",
+ SELECT_FIELDS, FROM_JOIN_EVERYTHING, GROUP_BY);
+
+ private static final String UPDATE_QUERY = """
+ UPDATE films
+ SET name = :name, description = :description,
+ release_date = :release_date, duration = :duration, mpa_id = :mpa_id
+ WHERE film_id = :film_id""";
+
+
+ private final SimpleJdbcInsert simpleInsert;
+
+ public DbFilmStorage(JdbcTemplate jdbc, FilmMapper mapper) {
+ super(jdbc, mapper);
+ this.simpleInsert = new SimpleJdbcInsert(jdbc)
+ .withTableName("films")
+ .usingGeneratedKeyColumns("film_id");
+ }
+
+ @Override
+ public Film add(Film film) {
+ Long id = simpleInsert.executeAndReturnKey(filmToMap(film)).longValue();
+ film.setId(id);
+
+ return film;
+ }
+
+ @Override
+ public Film update(Film film) {
+ Map map = filmToMap(film);
+ map.put("film_id", film.getId());
+
+ update(UPDATE_QUERY, map);
+
+ return film;
+ }
+
+ @Override
+ public Film remove(Long id) {
+ Film film = get(id);
+ remove(DELETE_QUERY, id);
+ return film;
+ }
+
+ @Override
+ public Film get(Long id) {
+ return get(SELECT_BY_ID_QUERY, id);
+ }
+
+ @Override
+ public Collection getAll() {
+ return getAll(SELECT_ALL_QUERY);
+ }
+
+ @Override
+ public boolean contains(Long id) {
+ return jdbc.queryForObject(EXISTS_QUERY, Boolean.class, id);
+ }
+
+ @Override
+ public Collection getTopFilms(Long count) {
+ String sql = SELECT_ALL_QUERY + " ORDER BY likes DESC LIMIT ?";
+
+ return jdbc.query(sql, mapper, count);
+ }
+
+ private Map filmToMap(Film film) {
+ Map map = new HashMap<>();
+ map.put("name", film.getName());
+ map.put("description", film.getDescription());
+ map.put("release_date", film.getReleaseDate());
+ map.put("duration", film.getDuration());
+
+ if (film.getMpa() != null) {
+ map.put("mpa_id", film.getMpa().getId());
+ }
+
+ return map;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbGenreStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbGenreStorage.java
new file mode 100644
index 0000000..095b7d3
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbGenreStorage.java
@@ -0,0 +1,60 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.storage.BaseDao;
+import ru.yandex.practicum.filmorate.storage.film.GenreStorage;
+import ru.yandex.practicum.filmorate.storage.mappers.GenreMapper;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+@Repository("DbGenreStorage")
+public class DbGenreStorage extends BaseDao implements GenreStorage {
+
+ private static final String SELECT_BY_ID_QUERY = "SELECT * FROM genres WHERE genre_id = ?";
+ private static final String SELECT_ALL_QUERY = "SELECT * FROM genres;";
+
+ public DbGenreStorage(JdbcTemplate jdbc, GenreMapper mapper) {
+ super(jdbc, mapper);
+ }
+
+ @Override
+ public Optional getGenre(Long id) {
+ try {
+ Genre genre = get(SELECT_BY_ID_QUERY, id);
+ return Optional.ofNullable(genre);
+ } catch (EmptyResultDataAccessException e) {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Collection getAll() {
+ return getAll(SELECT_ALL_QUERY);
+ }
+
+ @Override
+ public void addGenresToFilm(Film film) {
+ String sql = "INSERT INTO films_genres(film_id, genre_id) VALUES(?,?)";
+ Long filmId = film.getId();
+
+ List batchArgs = film.getGenres()
+ .stream()
+ .map(genre -> new Object[]{filmId, genre.getId()})
+ .toList();
+
+ jdbc.batchUpdate(sql, batchArgs);
+ }
+
+ @Override
+ public void updateFilmGenres(Film film) {
+ String sql = "DELETE FROM films_genres WHERE film_id = ?";
+ jdbc.update(sql, film.getId());
+ addGenresToFilm(film);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbLikeStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbLikeStorage.java
new file mode 100644
index 0000000..9221172
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbLikeStorage.java
@@ -0,0 +1,33 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.storage.film.LikeStorage;
+
+@Repository("DbLikeStorage")
+public class DbLikeStorage implements LikeStorage {
+
+ private final JdbcTemplate jdbc;
+
+ public DbLikeStorage(JdbcTemplate jdbc) {
+ this.jdbc = jdbc;
+ }
+
+ @Override
+ public Long addLike(Long filmId, Long userId) {
+ String insertSql = "INSERT INTO likes(film_id, user_id) VALUES(?, ?)";
+ String countSql = "SELECT COUNT(*) FROM likes WHERE film_id = ?";
+ jdbc.update(insertSql, filmId, userId);
+
+ return jdbc.queryForObject(countSql, Long.class, filmId);
+ }
+
+ @Override
+ public Long deleteLike(Long filmId, Long userId) {
+ String deleteSql = "DELETE FROM likes WHERE user_id = ?";
+ String countSql = "SELECT COUNT(*) FROM likes WHERE film_id = ?";
+
+ jdbc.update(deleteSql, userId);
+ return jdbc.queryForObject(countSql, Long.class, filmId);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbMpaStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbMpaStorage.java
new file mode 100644
index 0000000..514224c
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/db/DbMpaStorage.java
@@ -0,0 +1,38 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.storage.BaseDao;
+import ru.yandex.practicum.filmorate.storage.film.MpaStorage;
+import ru.yandex.practicum.filmorate.storage.mappers.MpaMapper;
+
+import java.util.Collection;
+import java.util.Optional;
+
+@Repository("DbMpaStorage")
+public class DbMpaStorage extends BaseDao implements MpaStorage {
+
+ private static final String SELECT_BY_ID_QUERY = "SELECT * FROM mpa WHERE mpa_id = ?";
+ private static final String SELECT_ALL_QUERY = "SELECT * FROM mpa;";
+
+ public DbMpaStorage(JdbcTemplate jdbc, MpaMapper mapper) {
+ super(jdbc, mapper);
+ }
+
+ @Override
+ public Optional getMpa(Long id) {
+ try {
+ Mpa mpa = get(SELECT_BY_ID_QUERY, id);
+ return Optional.ofNullable(mpa);
+ } catch (EmptyResultDataAccessException e) {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Collection getAll() {
+ return getAll(SELECT_ALL_QUERY);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/memory/InMemoryFilmStorage.java
similarity index 76%
rename from src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java
rename to src/main/java/ru/yandex/practicum/filmorate/storage/film/memory/InMemoryFilmStorage.java
index 82b593c..b7183d7 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/memory/InMemoryFilmStorage.java
@@ -1,10 +1,13 @@
-package ru.yandex.practicum.filmorate.films.film;
+package ru.yandex.practicum.filmorate.storage.film.memory;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Repository;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.storage.film.FilmStorage;
-import java.util.*;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -18,7 +21,7 @@
* @see Film
* @see FilmStorage
*/
-@Component
+@Repository("MemFilmStorage")
public class InMemoryFilmStorage implements FilmStorage {
private final Map films;
@@ -33,7 +36,6 @@ public InMemoryFilmStorage() {
public Film add(Film film) {
film.setId(idGenerator.getAndIncrement());
films.put(film.getId(), film);
-
return films.get(film.getId());
}
@@ -65,7 +67,10 @@ public boolean contains(Long id) {
}
@Override
- public int size() {
- return films.size();
+ public Collection getTopFilms(Long count) {
+ return films.values().stream()
+ .sorted(Comparator.comparingLong(Film::getLikes).reversed())
+ .limit(count)
+ .toList();
}
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/memory/InMemoryLikeStorage.java
similarity index 53%
rename from src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorage.java
rename to src/main/java/ru/yandex/practicum/filmorate/storage/film/memory/InMemoryLikeStorage.java
index 5cf8a84..a254989 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/memory/InMemoryLikeStorage.java
@@ -1,13 +1,15 @@
-package ru.yandex.practicum.filmorate.storage.film;
+package ru.yandex.practicum.filmorate.storage.film.memory;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.storage.film.LikeStorage;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-@Component
+@Repository("MemLikeStorage")
public class InMemoryLikeStorage implements LikeStorage {
private final Map> likes;
@@ -16,27 +18,23 @@ public InMemoryLikeStorage() {
this.likes = new ConcurrentHashMap<>();
}
- @Override
- public void initializeLikesSet(Long id) {
- likes.put(id, new HashSet<>());
- }
@Override
- public void clearLikesSet(Long id) {
+ public void clearLikes(Long id) {
likes.remove(id);
}
@Override
public Long addLike(Long filmId, Long userId) {
- likes.get(filmId).add(userId);
+ likes.computeIfAbsent(filmId, id -> new HashSet<>()).add(userId);
return (long) likes.get(filmId).size();
}
@Override
public Long deleteLike(Long filmId, Long userId) {
- likes.get(filmId).remove(userId);
+ likes.getOrDefault(filmId, Collections.emptySet()).remove(userId);
- return (long) likes.get(filmId).size();
+ return (long) likes.getOrDefault(filmId, Collections.emptySet()).size();
}
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FilmMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FilmMapper.java
new file mode 100644
index 0000000..d5f8359
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/FilmMapper.java
@@ -0,0 +1,70 @@
+package ru.yandex.practicum.filmorate.storage.mappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.model.Mpa;
+
+import java.sql.Array;
+import java.sql.Date;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Set;
+
+@Component
+public class FilmMapper implements RowMapper {
+
+ @Override
+ public Film mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+ Film film = new Film();
+ film.setId(resultSet.getLong("film_id"));
+ film.setName(resultSet.getString("name"));
+ film.setDescription(resultSet.getString("description"));
+ film.setLikes(resultSet.getLong("likes"));
+ film.setDuration(resultSet.getInt("duration"));
+
+ Date date = resultSet.getDate("release_date");
+ LocalDate releaseDate = date == null ? null : date.toLocalDate();
+
+ film.setReleaseDate(releaseDate);
+
+ mapMpa(film, resultSet);
+ mapGenres(film, resultSet);
+
+ return film;
+ }
+
+ private void mapMpa(Film film, ResultSet resultSet) throws SQLException {
+ Long mpaId = resultSet.getLong("mpa_id");
+ if (!resultSet.wasNull()) {
+ String mpaName = resultSet.getString("mpa_name");
+ film.setMpa(new Mpa(mpaId, mpaName));
+ }
+ }
+
+ private void mapGenres(Film film, ResultSet resultSet) throws SQLException {
+ Array sqlArrayIds = resultSet.getArray("genre_ids");
+ if (resultSet.wasNull()) {
+ return;
+ }
+
+ Array sqlArrayNames = resultSet.getArray("genre_names");
+
+ Object[] idsArray = (Object[]) sqlArrayIds.getArray();
+ Object[] namesArray = (Object[]) sqlArrayNames.getArray();
+
+ Set genres = new HashSet<>();
+
+ for (int i = 0; i < idsArray.length; i++) {
+ if (idsArray[i] != null && namesArray[i] != null) {
+ Long id = ((Number) idsArray[i]).longValue();
+ String name = namesArray[i].toString();
+ genres.add(new Genre(id, name));
+ }
+ }
+ film.setGenres(genres);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/GenreMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/GenreMapper.java
new file mode 100644
index 0000000..2554b4d
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/GenreMapper.java
@@ -0,0 +1,20 @@
+package ru.yandex.practicum.filmorate.storage.mappers;
+
+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 GenreMapper implements RowMapper {
+ @Override
+ public Genre mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+ Genre genre = new Genre();
+ genre.setId(resultSet.getLong("genre_id"));
+ genre.setName(resultSet.getString("name"));
+
+ return genre;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/MpaMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/MpaMapper.java
new file mode 100644
index 0000000..39bc681
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/MpaMapper.java
@@ -0,0 +1,20 @@
+package ru.yandex.practicum.filmorate.storage.mappers;
+
+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 MpaMapper implements RowMapper {
+ @Override
+ public Mpa mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+ Mpa mpa = new Mpa();
+ mpa.setId(resultSet.getLong("mpa_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/mappers/UserMapper.java b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/UserMapper.java
new file mode 100644
index 0000000..4dc4a93
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/mappers/UserMapper.java
@@ -0,0 +1,28 @@
+package ru.yandex.practicum.filmorate.storage.mappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.sql.Date;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.LocalDate;
+
+@Component
+public class UserMapper implements RowMapper {
+ @Override
+ public User mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+ User user = new User();
+ user.setId(resultSet.getLong("user_id"));
+ user.setEmail(resultSet.getString("email"));
+ user.setLogin(resultSet.getString("login"));
+ user.setName(resultSet.getString("name"));
+
+ Date date = resultSet.getDate("birthday");
+ LocalDate birthday = date == null ? null : date.toLocalDate();
+ user.setBirthday(birthday);
+
+ return user;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/FriendShipStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/FriendShipStorage.java
index d54df19..af50ddc 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/user/FriendShipStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/FriendShipStorage.java
@@ -4,13 +4,14 @@
public interface FriendShipStorage {
- void initializeFriendsSet(Long id);
-
- void clearFriendsSet(Long id);
+ default void deleteUserFromAllFriends(Long id) {
+ }
void addFriend(Long senderId, Long receiverId);
void deleteFriend(Long senderId, Long receiverId);
Set getFriends(Long id);
+
+ Set getCommonFriends(Long id, Long otherId);
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorage.java
deleted file mode 100644
index 88cb81d..0000000
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorage.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package ru.yandex.practicum.filmorate.storage.user;
-
-import org.springframework.stereotype.Component;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Component
-public class InMemoryFriendShipStorage implements FriendShipStorage {
-
- private final Map> friendships;
-
- public InMemoryFriendShipStorage() {
- this.friendships = new ConcurrentHashMap<>();
- }
-
- @Override
- public void initializeFriendsSet(Long id) {
- friendships.put(id, new HashSet<>());
- }
-
- @Override
- public void clearFriendsSet(Long id) {
- friendships.remove(id);
- }
-
- @Override
- public void addFriend(Long senderId, Long receiverId) {
- friendships.get(senderId).add(receiverId);
- }
-
- @Override
- public void deleteFriend(Long senderId, Long receiverId) {
- friendships.get(senderId).remove(receiverId);
- }
-
- @Override
- public Set getFriends(Long id) {
- return friendships.get(id);
- }
-}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java
index ae4c687..0377f5d 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/UserStorage.java
@@ -3,5 +3,9 @@
import ru.yandex.practicum.filmorate.model.User;
import ru.yandex.practicum.filmorate.storage.BasicStorage;
+import java.util.Collection;
+import java.util.List;
+
public interface UserStorage extends BasicStorage {
+ List getAllFromCollection(Collection ids);
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/db/DbFriendShipStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/db/DbFriendShipStorage.java
new file mode 100644
index 0000000..728af3e
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/db/DbFriendShipStorage.java
@@ -0,0 +1,47 @@
+package ru.yandex.practicum.filmorate.storage.user.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.storage.user.FriendShipStorage;
+
+import java.util.Set;
+
+@Repository("DbFriendShipStorage")
+public class DbFriendShipStorage implements FriendShipStorage {
+
+ private static final String SELECT_BY_ID_QUERY = "SELECT friend_id FROM friendships WHERE user_id = ?";
+ private static final String INSERT_QUERY = "INSERT INTO friendships(user_id, friend_id) VALUES(?,?)";
+ private static final String DELETE_QUERY = "DELETE FROM friendships WHERE user_id = ? AND friend_id = ?";
+ private static final String SELECT_COMMON_FRIENDS = """
+ SELECT f1.friend_id
+ FROM friendships f1
+ JOIN friendships f2 ON f1.friend_id = f2.friend_id
+ WHERE f1.user_id = ? AND f2.user_id = ?""";
+
+
+ private final JdbcTemplate jdbc;
+
+ public DbFriendShipStorage(JdbcTemplate jdbc) {
+ this.jdbc = jdbc;
+ }
+
+ @Override
+ public void addFriend(Long senderId, Long receiverId) {
+ jdbc.update(INSERT_QUERY, senderId, receiverId);
+ }
+
+ @Override
+ public void deleteFriend(Long senderId, Long receiverId) {
+ jdbc.update(DELETE_QUERY, senderId, receiverId);
+ }
+
+ @Override
+ public Set getFriends(Long id) {
+ return Set.copyOf(jdbc.queryForList(SELECT_BY_ID_QUERY, Long.class, id));
+ }
+
+ @Override
+ public Set getCommonFriends(Long id, Long otherId) {
+ return Set.copyOf(jdbc.queryForList(SELECT_COMMON_FRIENDS, Long.class, id, otherId));
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/db/DbUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/db/DbUserStorage.java
new file mode 100644
index 0000000..c62e56c
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/db/DbUserStorage.java
@@ -0,0 +1,91 @@
+package ru.yandex.practicum.filmorate.storage.user.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.BaseDao;
+import ru.yandex.practicum.filmorate.storage.mappers.UserMapper;
+import ru.yandex.practicum.filmorate.storage.user.UserStorage;
+
+import java.util.*;
+
+@Repository("DbUserStorage")
+public class DbUserStorage extends BaseDao implements UserStorage {
+
+ private static final String SELECT_BY_ID_QUERY = "SELECT * FROM users WHERE user_id = ?";
+ private static final String SELECT_ALL_QUERY = "SELECT * FROM users;";
+ private static final String DELETE_QUERY = "DELETE FROM users WHERE user_id = ?";
+ private static final String EXISTS_QUERY = "SELECT EXISTS(SELECT 1 FROM users WHERE user_id = ?)";
+ private static final String SELECT_ALL_FROM_COLLECTION = "SELECT * FROM users WHERE user_id IN (:ids)";
+ private static final String UPDATE_QUERY = """
+ UPDATE users SET email = :email, login = :login, name = :name, birthday = :birthday
+ WHERE user_id = :user_id
+ """;
+
+ private final SimpleJdbcInsert simpleInsert;
+
+ public DbUserStorage(JdbcTemplate jdbc, UserMapper mapper) {
+ super(jdbc, mapper);
+ this.simpleInsert = new SimpleJdbcInsert(jdbc)
+ .withTableName("users")
+ .usingGeneratedKeyColumns("user_id");
+ }
+
+ @Override
+ public User add(User user) {
+ Long id = simpleInsert.executeAndReturnKey(userToMap(user)).longValue();
+ user.setId(id);
+
+ return user;
+ }
+
+ @Override
+ public User update(User user) {
+ Map map = userToMap(user);
+ map.put("user_id", user.getId());
+ update(UPDATE_QUERY, map);
+ return user;
+ }
+
+ @Override
+ public User remove(Long id) {
+ User user = get(id);
+ remove(DELETE_QUERY, id);
+ return user;
+ }
+
+ @Override
+ public User get(Long id) {
+ return get(SELECT_BY_ID_QUERY, id);
+ }
+
+ @Override
+ public Collection getAll() {
+ return getAll(SELECT_ALL_QUERY);
+ }
+
+ @Override
+ public boolean contains(Long id) {
+ return jdbc.queryForObject(EXISTS_QUERY, Boolean.class, id);
+ }
+
+ @Override
+ public List getAllFromCollection(Collection ids) {
+ if (ids == null || ids.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ return namedJdbc.query(SELECT_ALL_FROM_COLLECTION, Collections.singletonMap("ids", ids), mapper);
+ }
+
+ private Map userToMap(User user) {
+ Map map = new HashMap<>();
+ map.put("email", user.getEmail());
+ map.put("login", user.getLogin());
+ map.put("name", user.getName());
+ map.put("birthday", user.getBirthday());
+
+ return map;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/memory/InMemoryFriendShipStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/memory/InMemoryFriendShipStorage.java
new file mode 100644
index 0000000..2a1c7d3
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/memory/InMemoryFriendShipStorage.java
@@ -0,0 +1,50 @@
+package ru.yandex.practicum.filmorate.storage.user.memory;
+
+import org.springframework.stereotype.Repository;
+import ru.yandex.practicum.filmorate.storage.user.FriendShipStorage;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Repository("MemFriendShipStorage")
+public class InMemoryFriendShipStorage implements FriendShipStorage {
+
+ private final Map> friendships;
+
+ public InMemoryFriendShipStorage() {
+ this.friendships = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public void deleteUserFromAllFriends(Long id) {
+ friendships.get(id)
+ .forEach(friendId -> friendships.getOrDefault(friendId, Collections.emptySet()).remove(id));
+ friendships.remove(id);
+ }
+
+ @Override
+ public void addFriend(Long senderId, Long receiverId) {
+ friendships.computeIfAbsent(senderId, id -> new HashSet<>()).add(receiverId);
+ }
+
+ @Override
+ public void deleteFriend(Long senderId, Long receiverId) {
+ friendships.getOrDefault(senderId, Collections.emptySet()).remove(receiverId);
+ }
+
+ @Override
+ public Set getFriends(Long id) {
+ return friendships.getOrDefault(id, Collections.emptySet());
+ }
+
+ @Override
+ public Set getCommonFriends(Long id, Long otherId) {
+ Set friends1 = getFriends(id);
+ Set friends2 = getFriends(otherId);
+ friends1.retainAll(friends2);
+ return friends1;
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/user/memory/InMemoryUserStorage.java
similarity index 85%
rename from src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java
rename to src/main/java/ru/yandex/practicum/filmorate/storage/user/memory/InMemoryUserStorage.java
index 4b26fae..7944882 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/user/memory/InMemoryUserStorage.java
@@ -1,7 +1,8 @@
-package ru.yandex.practicum.filmorate.storage.user;
+package ru.yandex.practicum.filmorate.storage.user.memory;
import org.springframework.stereotype.Component;
import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.user.UserStorage;
import java.util.Collection;
import java.util.List;
@@ -19,7 +20,7 @@
* @see User
* @see UserStorage
*/
-@Component
+@Component("MemUserStorage")
public class InMemoryUserStorage implements UserStorage {
private final Map users;
@@ -66,7 +67,7 @@ public boolean contains(Long id) {
}
@Override
- public int size() {
- return users.size();
+ public List getAllFromCollection(Collection ids) {
+ return ids.stream().map(users::get).toList();
}
}
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index b50b2c2..3ac1048 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -1,5 +1,5 @@
spring.application.name=Filmorate
-server.port=8081
+server.port=8080
logging.file.path=./logs/
logging.logback.rollingpolicy.file-name-pattern=./logs/spring.%d{yyyy-MM-dd}.%i.log
@@ -8,5 +8,16 @@ logging.logback.rollingpolicy.total-size-cap=50MB
logging.logback.rollingpolicy.clean-history-on-start=true
spring.output.ansi.enabled=ALWAYS
-logging.level.ru.yandex.practicum.filmorate=INFO
-logging.level.org.zalando.logbook=TRACE
\ No newline at end of file
+logging.level.ru.yandex.practicum.filmorate=DEBUG
+logging.level.org.zalando.logbook=DEBUG
+logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
+logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
+logging.level.com.zaxxer.hikari=DEBUG
+
+spring.sql.init.mode=always
+#spring.datasource.url=jdbc:h2:file:./db/filmorate
+spring.datasource.url=jdbc:h2:mem:filmorate
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=admin
+spring.datasource.password=admin
+spring.h2.console.enabled=true
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 45001af..b57abfb 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -9,4 +9,10 @@ logging.logback.rollingpolicy.clean-history-on-start=true
spring.output.ansi.enabled=ALWAYS
logging.level.ru.yandex.practicum.filmorate=WARN
-logging.level.org.zalando.logbook=WARN
\ No newline at end of file
+logging.level.org.zalando.logbook=WARN
+
+spring.sql.init.mode=always
+spring.datasource.url=jdbc:h2:file:./db/filmorate
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=admin
+spring.datasource.password=admin
\ 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..2e931bd
--- /dev/null
+++ b/src/main/resources/data.sql
@@ -0,0 +1,20 @@
+DELETE FROM genres;
+DELETE FROM mpa;
+
+ALTER TABLE genres ALTER COLUMN genre_id RESTART WITH 1;
+ALTER TABLE mpa ALTER COLUMN mpa_id RESTART WITH 1;
+
+INSERT INTO mpa (name) VALUES
+('G'),
+('PG'),
+('PG-13'),
+('R'),
+('NC-17');
+
+INSERT INTO genres (name) VALUES
+('Комедия'),
+('Драма'),
+('Мультфильм'),
+('Триллер'),
+('Документальный'),
+('Боевик');
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
new file mode 100644
index 0000000..a1597a0
--- /dev/null
+++ b/src/main/resources/schema.sql
@@ -0,0 +1,44 @@
+CREATE TABLE IF NOT EXISTS users (
+ user_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ email VARCHAR NOT NULL,
+ login VARCHAR NOT NULL,
+ name VARCHAR,
+ birthday DATE);
+
+CREATE TABLE IF NOT EXISTS friendships (
+ user_id BIGINT,
+ friend_id BIGINT,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ FOREIGN KEY (friend_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ PRIMARY KEY (user_id, friend_id));
+
+CREATE TABLE IF NOT EXISTS mpa (
+ mpa_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ name VARCHAR NOT NULL);
+
+CREATE TABLE IF NOT EXISTS films (
+ film_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ name VARCHAR NOT NULL,
+ description VARCHAR(200),
+ release_date DATE,
+ duration INTEGER,
+ mpa_id BIGINT,
+ FOREIGN KEY (mpa_id) REFERENCES mpa(mpa_id) ON DELETE SET NULL);
+
+CREATE TABLE IF NOT EXISTS genres (
+ genre_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ name VARCHAR NOT NULL);
+
+CREATE TABLE IF NOT EXISTS films_genres (
+ film_id BIGINT,
+ genre_id BIGINT,
+ FOREIGN KEY (film_id) REFERENCES films(film_id) ON DELETE CASCADE,
+ FOREIGN KEY (genre_id) REFERENCES genres(genre_id) ON DELETE CASCADE,
+ PRIMARY KEY (film_id, genre_id));
+
+CREATE TABLE IF NOT EXISTS likes (
+ film_id BIGINT,
+ user_id BIGINT,
+ FOREIGN KEY (film_id) REFERENCES films(film_id) ON DELETE CASCADE,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ PRIMARY KEY (film_id, user_id));
\ 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 ce4fdbf..9939f11 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/FilmControllerTest.java
@@ -4,14 +4,18 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.MediaType;
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.service.film.FilmService;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@@ -141,6 +145,64 @@ public void shouldReturn400WhenZeroCount() throws Exception {
verify(filmService, never()).getTopFilms(any());
}
+
+
+ @Test
+ public void shouldGetFilm() throws Exception {
+ when(filmService.getFilm(1L))
+ .thenReturn(new Film(1L, "name", "description", LocalDate.MAX, 100));
+
+ mockMvc.perform(get("/films/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.name").value("name"));
+ verify(filmService).getFilm(1L);
+ }
+
+ @Test
+ public void shouldReturn404WhenFilmNotFoundAndGetFilm() throws Exception {
+ when(filmService.getFilm(1000L))
+ .thenThrow(new NotFoundException("Not Found"));
+ mockMvc.perform(get("/films/1000")).andExpect(status().isNotFound());
+ verify(filmService).getFilm(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenNegativeIdsAndGetFilm() throws Exception {
+ mockMvc.perform(get("/films/-1")).andExpect(status().isBadRequest());
+ verify(filmService, never()).getFilm(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenZeroFilmIdAndGetFilm() throws Exception {
+ mockMvc.perform(get("/films/0")).andExpect(status().isBadRequest());
+ verify(filmService, never()).getFilm(any());
+ }
+
+ @Test
+ public void shouldDeleteFilm() throws Exception {
+ mockMvc.perform(delete("/films/1")).andExpect(status().isOk());
+ verify(filmService).deleteFilm(any());
+ }
+
+ @Test
+ public void shouldReturn404WhenFilmNotFoundAndDeleteFilm() throws Exception {
+ doThrow(new NotFoundException("Not Found")).when(filmService).deleteFilm(1000L);
+ mockMvc.perform(delete("/films/1000")).andExpect(status().isNotFound());
+ verify(filmService).deleteFilm(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenNegativeIdsAndDeleteFilm() throws Exception {
+ mockMvc.perform(delete("/films/-1")).andExpect(status().isBadRequest());
+ verify(filmService, never()).deleteFilm(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenZeroFilmIdAndDeleteFilm() throws Exception {
+ mockMvc.perform(delete("/films/0")).andExpect(status().isBadRequest());
+ verify(filmService, never()).deleteFilm(any());
+ }
}
@Nested
@@ -283,6 +345,57 @@ public void shouldReturn400WhenInvalidDateFormat() throws Exception {
verify(filmService, never()).addFilm(any());
}
+
+ @Test
+ public void shouldNotCreateFilmWhenMpaIsNotFoundAndReturnNotFound() throws Exception {
+ Film filmToCreate = new Film(null, "name", "description",
+ LocalDate.of(2000, 1, 1), 10);
+ filmToCreate.setMpa(new Mpa(1000L, "name"));
+
+ when(filmService.addFilm(filmToCreate)).thenThrow(new DataIntegrityViolationException("foreign key mpa"));
+ String invalidJson = objectMapper.writeValueAsString(filmToCreate);
+
+
+ mockMvc.perform(post("/films")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(invalidJson))
+ .andExpect(status().isNotFound());
+
+ verify(filmService).addFilm(any());
+ }
+
+ @Test
+ public void shouldNotCreateFilmWhenGenreIsNotFoundAndReturnNotFound() throws Exception {
+ Film filmToCreate = new Film(null, "name", "description",
+ LocalDate.of(2000, 1, 1), 10);
+ filmToCreate.setGenres(Set.of(new Genre(1000L, "bla")));
+
+ when(filmService.addFilm(filmToCreate)).thenThrow(new DataIntegrityViolationException("foreign key genre"));
+ String invalidJson = objectMapper.writeValueAsString(filmToCreate);
+
+ mockMvc.perform(post("/films")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(invalidJson))
+ .andExpect(status().isNotFound());
+
+ verify(filmService).addFilm(any());
+ }
+
+ @Test
+ public void shouldNotCreateFilmWhenDataBrokenAndReturnBadRequest() throws Exception {
+ Film filmToCreate = new Film(null, "name", "description",
+ LocalDate.of(2000, 1, 1), 10);
+
+ when(filmService.addFilm(filmToCreate)).thenThrow(new DataIntegrityViolationException("something"));
+ String invalidJson = objectMapper.writeValueAsString(filmToCreate);
+
+ mockMvc.perform(post("/films")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(invalidJson))
+ .andExpect(status().isBadRequest());
+
+ verify(filmService).addFilm(any());
+ }
}
@Nested
@@ -379,7 +492,7 @@ public void shouldAddLike() throws Exception {
}
@Test
- public void shouldNotAddLikeWhenFilmNotFound() throws Exception {
+ public void shouldNotAddLikeWhenUserNotFound() throws Exception {
Film filmToUpdate = new Film(1000L, "ONLY TODAY", "NEW TEXT",
LocalDate.of(2000, 1, 1), 120);
@@ -397,7 +510,7 @@ public void shouldNotAddLikeWhenFilmNotFound() throws Exception {
}
@Test
- public void shouldNotAddLikeWhenUserNotFound() throws Exception {
+ public void shouldNotAddLikeWhenFilmNotFound() throws Exception {
Film filmToUpdate = new Film(1L, "ONLY TODAY", "NEW TEXT",
LocalDate.of(2000, 1, 1), 120);
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..8497549
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/GenreControllerTest.java
@@ -0,0 +1,77 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.service.film.GenreService;
+
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@WebMvcTest(GenreController.class)
+public class GenreControllerTest extends ControllerTest {
+
+ @MockBean
+ GenreService genreService;
+
+ @Test
+ public void shouldGetGenre() throws Exception {
+ when(genreService.getGenre(1L))
+ .thenReturn(new Genre(1L, "name"));
+
+ mockMvc.perform(get("/genres/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.name").value("name"));
+ verify(genreService).getGenre(1L);
+ }
+
+ @Test
+ public void shouldReturn404WhenUserNotFoundAndGetGenre() throws Exception {
+ when(genreService.getGenre(1000L))
+ .thenThrow(new NotFoundException("Not Found"));
+ mockMvc.perform(get("/genres/1000")).andExpect(status().isNotFound());
+ verify(genreService).getGenre(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenNegativeIdsAndGetGenre() throws Exception {
+ mockMvc.perform(get("/genres/-1")).andExpect(status().isBadRequest());
+ verify(genreService, never()).getGenre(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenZeroUserIdAndGetGenre() throws Exception {
+ mockMvc.perform(get("/genres/0")).andExpect(status().isBadRequest());
+ verify(genreService, never()).getGenre(any());
+ }
+
+ @Test
+ public void shouldGetGenres() throws Exception {
+ Genre mpa1 = new Genre(1L, "name1");
+ Genre mpa2 = new Genre(2L, "name2");
+
+ List genres = List.of(mpa1, mpa2);
+
+ // Настраиваем мок сервиса
+ when(genreService.getAll()).thenReturn(genres);
+
+ mockMvc.perform(get("/genres"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$").isArray())
+ .andExpect(jsonPath("$.length()").value(2))
+ .andExpect(jsonPath("$[0].id").value(1))
+ .andExpect(jsonPath("$[0].name").value("name1"))
+ .andExpect(jsonPath("$[1].id").value(2))
+ .andExpect(jsonPath("$[1].name").value("name2"));
+
+ verify(genreService, times(1)).getAll();
+ }
+}
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..f728905
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/MpaControllerTest.java
@@ -0,0 +1,77 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.service.film.MpaService;
+
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@WebMvcTest(MpaController.class)
+public class MpaControllerTest extends ControllerTest {
+
+ @MockBean
+ MpaService mpaService;
+
+ @Test
+ public void shouldGetMpa() throws Exception {
+ when(mpaService.getMpa(1L))
+ .thenReturn(new Mpa(1L, "name"));
+
+ mockMvc.perform(get("/mpa/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.name").value("name"));
+ verify(mpaService).getMpa(1L);
+ }
+
+ @Test
+ public void shouldReturn404WhenUserNotFoundAndGetMpa() throws Exception {
+ when(mpaService.getMpa(1000L))
+ .thenThrow(new NotFoundException("Not Found"));
+ mockMvc.perform(get("/mpa/1000")).andExpect(status().isNotFound());
+ verify(mpaService).getMpa(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenNegativeIdsAndGetMpa() throws Exception {
+ mockMvc.perform(get("/mpa/-1")).andExpect(status().isBadRequest());
+ verify(mpaService, never()).getMpa(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenZeroUserIdAndGetMpa() throws Exception {
+ mockMvc.perform(get("/mpa/0")).andExpect(status().isBadRequest());
+ verify(mpaService, never()).getMpa(any());
+ }
+
+ @Test
+ public void shouldGetMpas() throws Exception {
+ Mpa mpa1 = new Mpa(1L, "name1");
+ Mpa mpa2 = new Mpa(2L, "name2");
+
+ List mpas = List.of(mpa1, mpa2);
+
+ // Настраиваем мок сервиса
+ when(mpaService.getAll()).thenReturn(mpas);
+
+ mockMvc.perform(get("/mpa"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$").isArray())
+ .andExpect(jsonPath("$.length()").value(2))
+ .andExpect(jsonPath("$[0].id").value(1))
+ .andExpect(jsonPath("$[0].name").value("name1"))
+ .andExpect(jsonPath("$[1].id").value(2))
+ .andExpect(jsonPath("$[1].name").value("name2"));
+
+ verify(mpaService, times(1)).getAll();
+ }
+}
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 7884b1a..540a5f9 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/controller/UserControllerTest.java
@@ -172,6 +172,63 @@ public void shouldReturn400WhenZeroUserId() throws Exception {
verify(userService, never()).getFriends(any());
}
+
+ @Test
+ public void shouldGetUser() throws Exception {
+ when(userService.getUser(1L))
+ .thenReturn(new User(1L, "email", "login", "name", LocalDate.MIN));
+
+ mockMvc.perform(get("/users/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.email").value("email"));
+ verify(userService).getUser(1L);
+ }
+
+ @Test
+ public void shouldReturn404WhenUserNotFoundAndGetUser() throws Exception {
+ when(userService.getUser(1000L))
+ .thenThrow(new NotFoundException("Not Found"));
+ mockMvc.perform(get("/users/1000")).andExpect(status().isNotFound());
+ verify(userService).getUser(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenNegativeIdsAndGetUser() throws Exception {
+ mockMvc.perform(get("/users/-1")).andExpect(status().isBadRequest());
+ verify(userService, never()).getUser(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenZeroUserIdAndGetUser() throws Exception {
+ mockMvc.perform(get("/users/0")).andExpect(status().isBadRequest());
+ verify(userService, never()).getUser(any());
+ }
+
+ @Test
+ public void shouldDeleteUser() throws Exception {
+ mockMvc.perform(delete("/users/1")).andExpect(status().isOk());
+ verify(userService).deleteUser(any());
+ }
+
+ @Test
+ public void shouldReturn404WhenUserNotFoundAndDeleteUser() throws Exception {
+ doThrow(new NotFoundException("Not Found")).when(userService).deleteUser(1000L);
+ mockMvc.perform(delete("/users/1000")).andExpect(status().isNotFound());
+ verify(userService).deleteUser(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenNegativeIdsAndDeleteUser() throws Exception {
+ mockMvc.perform(delete("/users/-1")).andExpect(status().isBadRequest());
+ verify(userService, never()).deleteUser(any());
+ }
+
+ @Test
+ public void shouldReturn400WhenZeroUserIdAndDeleteUser() throws Exception {
+ mockMvc.perform(delete("/users/0")).andExpect(status().isBadRequest());
+ verify(userService, never()).deleteUser(any());
+ }
}
@Nested
diff --git a/src/test/java/ru/yandex/practicum/filmorate/service/film/FilmServiceTest.java b/src/test/java/ru/yandex/practicum/filmorate/service/film/FilmServiceTest.java
index d883d1a..9c81fa8 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/service/film/FilmServiceTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/service/film/FilmServiceTest.java
@@ -8,14 +8,14 @@
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.Genre;
import ru.yandex.practicum.filmorate.storage.film.FilmStorage;
+import ru.yandex.practicum.filmorate.storage.film.GenreStorage;
import ru.yandex.practicum.filmorate.storage.film.LikeStorage;
import ru.yandex.practicum.filmorate.storage.user.UserStorage;
import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@@ -32,11 +32,14 @@ public class FilmServiceTest {
@Mock
private UserStorage userStorage;
+ @Mock
+ private GenreStorage genreStorage;
+
private FilmService filmService;
@BeforeEach
public void setUp() {
- filmService = new FilmService(filmStorage, userStorage, likeStorage);
+ filmService = new FilmService(filmStorage, userStorage, likeStorage, genreStorage);
}
@Test
@@ -87,14 +90,64 @@ public void shouldAddFilmSuccessfully() {
Film savedFilm = new Film(1L, "New Film", "Description", LocalDate.now(), 120);
when(filmStorage.add(inputFilm)).thenReturn(savedFilm);
- doNothing().when(likeStorage).initializeLikesSet(1L);
Film result = filmService.addFilm(inputFilm);
assertNotNull(result);
assertEquals(1L, result.getId());
verify(filmStorage).add(inputFilm);
- verify(likeStorage).initializeLikesSet(1L);
+ }
+
+ @Test
+ public void shouldAddFilmWithGenres() {
+ Film inputFilm = new Film(null, "New Film", "Description", LocalDate.now(), 120);
+ inputFilm.setGenres(Set.of(new Genre(1L, "Комедия"), new Genre(2L, "Драма")));
+
+ Film savedFilm = new Film(1L, "New Film", "Description", LocalDate.now(), 120);
+ savedFilm.setGenres(inputFilm.getGenres());
+
+ when(filmStorage.add(inputFilm)).thenReturn(savedFilm);
+
+ Film result = filmService.addFilm(inputFilm);
+
+ assertNotNull(result);
+ assertEquals(1L, result.getId());
+ verify(filmStorage).add(inputFilm);
+ verify(genreStorage).addGenresToFilm(savedFilm);
+ }
+
+ @Test
+ public void shouldAddFilmWithoutGenres() {
+ Film inputFilm = new Film(null, "New Film", "Description", LocalDate.now(), 120);
+ inputFilm.setGenres(null);
+
+ Film savedFilm = new Film(1L, "New Film", "Description", LocalDate.now(), 120);
+
+ when(filmStorage.add(inputFilm)).thenReturn(savedFilm);
+
+ Film result = filmService.addFilm(inputFilm);
+
+ assertNotNull(result);
+ assertEquals(1L, result.getId());
+ verify(filmStorage).add(inputFilm);
+ verify(genreStorage, never()).addGenresToFilm(any());
+ }
+
+ @Test
+ public void shouldAddFilmWithEmptyGenres() {
+ Film inputFilm = new Film(null, "New Film", "Description", LocalDate.now(), 120);
+ inputFilm.setGenres(Collections.emptySet());
+
+ Film savedFilm = new Film(1L, "New Film", "Description", LocalDate.now(), 120);
+
+ when(filmStorage.add(inputFilm)).thenReturn(savedFilm);
+
+ Film result = filmService.addFilm(inputFilm);
+
+ assertNotNull(result);
+ assertEquals(1L, result.getId());
+ verify(filmStorage).add(inputFilm);
+ verify(genreStorage, never()).addGenresToFilm(any());
}
@Test
@@ -132,6 +185,43 @@ public void shouldThrowNotFoundExceptionWhenUpdateNonExistentFilm() {
verify(filmStorage, never()).update(any());
}
+ @Test
+ public void shouldUpdateFilmWithGenres() {
+ Film inputFilm = new Film(1L, "Updated Film", "Updated Description", LocalDate.now(), 150);
+ inputFilm.setGenres(Set.of(new Genre(1L, "Комедия"), new Genre(3L, "Мультфильм")));
+
+ Film updatedFilm = new Film(1L, "Updated Film", "Updated Description", LocalDate.now(), 150);
+ updatedFilm.setGenres(inputFilm.getGenres());
+
+ when(filmStorage.contains(1L)).thenReturn(true);
+ when(filmStorage.update(inputFilm)).thenReturn(updatedFilm);
+
+ Film result = filmService.updateFilm(inputFilm);
+
+ assertNotNull(result);
+ assertEquals(1L, result.getId());
+ verify(filmStorage).update(inputFilm);
+ verify(genreStorage).updateFilmGenres(updatedFilm);
+ }
+
+ @Test
+ public void shouldUpdateFilmWithoutGenres() {
+ Film inputFilm = new Film(1L, "Updated Film", "Updated Description", LocalDate.now(), 150);
+ inputFilm.setGenres(null);
+
+ Film updatedFilm = new Film(1L, "Updated Film", "Updated Description", LocalDate.now(), 150);
+
+ when(filmStorage.contains(1L)).thenReturn(true);
+ when(filmStorage.update(inputFilm)).thenReturn(updatedFilm);
+
+ Film result = filmService.updateFilm(inputFilm);
+
+ assertNotNull(result);
+ assertEquals(1L, result.getId());
+ verify(filmStorage).update(inputFilm);
+ verify(genreStorage, never()).updateFilmGenres(any());
+ }
+
@Test
public void shouldDeleteFilmSuccessfully() {
Long filmId = 1L;
@@ -141,7 +231,7 @@ public void shouldDeleteFilmSuccessfully() {
filmService.deleteFilm(filmId);
verify(filmStorage).remove(filmId);
- verify(likeStorage).clearLikesSet(filmId);
+ verify(likeStorage).clearLikes(filmId);
}
@Test
@@ -152,7 +242,7 @@ public void shouldThrowNotFoundExceptionWhenDeleteNonExistentFilm() {
assertThrows(NotFoundException.class, () -> filmService.deleteFilm(filmId));
verify(filmStorage, never()).remove(filmId);
- verify(likeStorage, never()).clearLikesSet(filmId);
+ verify(likeStorage, never()).clearLikes(filmId);
}
@Test
@@ -171,7 +261,7 @@ public void shouldAddLikeSuccessfully() {
assertNotNull(result);
assertEquals(5L, result.getLikes());
verify(likeStorage).addLike(filmId, userId);
- verify(filmStorage, times(2)).get(filmId);
+ verify(filmStorage).get(filmId);
}
@Test
@@ -247,7 +337,7 @@ public void shouldGetTopFilmsOrderedByLikes() {
Film film3 = new Film(3L, "Film 3", "Desc 3", LocalDate.now(), 130);
film3.setLikes(5L);
- when(filmStorage.getAll()).thenReturn(List.of(film1, film2, film3));
+ when(filmStorage.getTopFilms(2L)).thenReturn(List.of(film2, film1));
Collection topFilms = filmService.getTopFilms(2L);
@@ -257,13 +347,11 @@ public void shouldGetTopFilmsOrderedByLikes() {
List resultList = new ArrayList<>(topFilms);
assertEquals(25L, resultList.get(0).getLikes()); // film2
assertEquals(10L, resultList.get(1).getLikes()); // film1
+ verify(filmStorage).getTopFilms(2L);
}
@Test
public void shouldReturnEmptyListWhenNoFilms() {
- when(filmStorage.getAll()).thenReturn(List.of());
-
- // when
Collection topFilms = filmService.getTopFilms(10L);
assertNotNull(topFilms);
@@ -272,17 +360,18 @@ public void shouldReturnEmptyListWhenNoFilms() {
@Test
public void shouldReturnAllFilmsWhenCountIsGreaterThanTotal() {
- Film film1 = new Film(1L, "Film 1", "Desc 1", LocalDate.now(), 120);
- film1.setLikes(5L);
- Film film2 = new Film(2L, "Film 2", "Desc 2", LocalDate.now(), 150);
- film2.setLikes(3L);
+ List allFilms = List.of(
+ new Film(1L, "Film 1", "Desc 1", LocalDate.now(), 120),
+ new Film(2L, "Film 2", "Desc 2", LocalDate.now(), 150),
+ new Film(3L, "Film 3", "Desc 3", LocalDate.now(), 130)
+ );
- when(filmStorage.getAll()).thenReturn(List.of(film1, film2));
+ when(filmStorage.getTopFilms(10L)).thenReturn(allFilms);
- // when
- Collection topFilms = filmService.getTopFilms(5L);
+ Collection topFilms = filmService.getTopFilms(10L);
assertNotNull(topFilms);
- assertEquals(2, topFilms.size());
+ assertEquals(3, topFilms.size());
+ verify(filmStorage).getTopFilms(10L);
}
}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/service/film/GenreServiceTest.java b/src/test/java/ru/yandex/practicum/filmorate/service/film/GenreServiceTest.java
new file mode 100644
index 0000000..601fbd0
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/service/film/GenreServiceTest.java
@@ -0,0 +1,71 @@
+package ru.yandex.practicum.filmorate.service.film;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.storage.film.GenreStorage;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class GenreServiceTest {
+
+ @Mock
+ private GenreStorage genreStorage;
+
+ private GenreService mpaService;
+
+ @BeforeEach
+ public void setUp() {
+ mpaService = new GenreService(genreStorage);
+ }
+
+ @Test
+ public void shouldGetGenre() {
+ Genre mpa = new Genre(1L, "name1");
+ when(genreStorage.getGenre(1L)).thenReturn(Optional.of(mpa));
+
+ Genre actualGenre = mpaService.getGenre(1L);
+
+ assertNotNull(actualGenre);
+ assertEquals(actualGenre, mpa);
+ verify(genreStorage).getGenre(1L);
+ }
+
+ @Test
+ public void shouldThrowNotFoundWhenNull() {
+ when(genreStorage.getGenre(1L)).thenReturn(Optional.ofNullable(null));
+
+ assertThrows(NotFoundException.class, () -> mpaService.getGenre(1L));
+
+ verify(genreStorage).getGenre(1L);
+ }
+
+ @Test
+ public void shouldThrowNotFound() {
+ when(genreStorage.getGenre(1L)).thenReturn(Optional.empty());
+
+ assertThrows(NotFoundException.class, () -> mpaService.getGenre(1L));
+
+ verify(genreStorage).getGenre(1L);
+ }
+
+ @Test
+ public void shouldGetAll() {
+ when(genreStorage.getAll()).thenReturn(List.of(new Genre(1L, "name1"), new Genre(2L, "name2")));
+
+ List list = (List) mpaService.getAll();
+
+ assertEquals(2, list.size());
+ verify(genreStorage).getAll();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/service/film/MpaServiceTest.java b/src/test/java/ru/yandex/practicum/filmorate/service/film/MpaServiceTest.java
new file mode 100644
index 0000000..5e17a2a
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/service/film/MpaServiceTest.java
@@ -0,0 +1,71 @@
+package ru.yandex.practicum.filmorate.service.film;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.storage.film.MpaStorage;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class MpaServiceTest {
+
+ @Mock
+ private MpaStorage mpaStorage;
+
+ private MpaService mpaService;
+
+ @BeforeEach
+ public void setUp() {
+ mpaService = new MpaService(mpaStorage);
+ }
+
+ @Test
+ public void shouldGetMpa() {
+ Mpa mpa = new Mpa(1L, "name1");
+ when(mpaStorage.getMpa(1L)).thenReturn(Optional.of(mpa));
+
+ Mpa actualMpa = mpaService.getMpa(1L);
+
+ assertNotNull(actualMpa);
+ assertEquals(actualMpa, mpa);
+ verify(mpaStorage).getMpa(1L);
+ }
+
+ @Test
+ public void shouldThrowNotFoundWhenNull() {
+ when(mpaStorage.getMpa(1L)).thenReturn(Optional.ofNullable(null));
+
+ assertThrows(NotFoundException.class, () -> mpaService.getMpa(1L));
+
+ verify(mpaStorage).getMpa(1L);
+ }
+
+ @Test
+ public void shouldThrowNotFound() {
+ when(mpaStorage.getMpa(1L)).thenReturn(Optional.empty());
+
+ assertThrows(NotFoundException.class, () -> mpaService.getMpa(1L));
+
+ verify(mpaStorage).getMpa(1L);
+ }
+
+ @Test
+ public void shouldGetAll() {
+ when(mpaStorage.getAll()).thenReturn(List.of(new Mpa(1L, "name1"), new Mpa(2L, "name2")));
+
+ List list = (List) mpaService.getAll();
+
+ assertEquals(2, list.size());
+ verify(mpaStorage).getAll();
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/service/user/UserServiceTest.java b/src/test/java/ru/yandex/practicum/filmorate/service/user/UserServiceTest.java
index a314020..9b22297 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/service/user/UserServiceTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/service/user/UserServiceTest.java
@@ -13,7 +13,6 @@
import java.time.LocalDate;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -78,13 +77,11 @@ public void shouldAddUser() {
User returnUser = new User(1L, "email@mail.com", "login", "name", LocalDate.now());
when(userStorage.add(any(User.class))).thenReturn(returnUser);
- doNothing().when(friendShipStorage).initializeFriendsSet(1L);
User result = userService.addUser(user1);
assertEquals(returnUser, result);
verify(userStorage).add(any(User.class));
- verify(friendShipStorage).initializeFriendsSet(1L);
}
@Test
@@ -189,19 +186,12 @@ public void shouldThrowNotFoundWhenUpdateUserWhenIdNotFound() {
@Test
public void shouldDeleteUser() {
Long userId = 1L;
- User user = new User(userId, "email@mail.com", "login", "name", LocalDate.now());
when(userStorage.contains(userId)).thenReturn(true);
- when(friendShipStorage.getFriends(userId)).thenReturn(new HashSet<>(Set.of(2L, 3L)));
- when(friendShipStorage.getFriends(2L)).thenReturn(new HashSet<>(Set.of(1L, 4L)));
- when(friendShipStorage.getFriends(3L)).thenReturn(new HashSet<>(Set.of(1L, 5L)));
-
userService.deleteUser(userId);
verify(userStorage).remove(userId);
- verify(friendShipStorage).clearFriendsSet(userId);
- verify(friendShipStorage).getFriends(2L);
- verify(friendShipStorage).getFriends(3L);
+ verify(friendShipStorage).deleteUserFromAllFriends(1L);
}
@Test
@@ -210,27 +200,6 @@ public void shouldThrowNotFoundWhenDeleteUserWhenIdNotFound() {
assertThrows(NotFoundException.class, () -> userService.deleteUser(1000L));
verify(userStorage, never()).remove(1000L);
- verify(friendShipStorage, never()).clearFriendsSet(1000L);
- }
-
- @Test
- public void shouldRemoveUserFromFriendsFriendsLists() {
- Long userId = 1L;
- Set friends = Set.of(2L, 3L);
-
- Set friend2Friends = new HashSet<>(Set.of(1L, 4L, 5L));
- Set friend3Friends = new HashSet<>(Set.of(1L, 6L));
-
- when(userStorage.contains(userId)).thenReturn(true);
- when(friendShipStorage.getFriends(userId)).thenReturn(friends);
- when(friendShipStorage.getFriends(2L)).thenReturn(friend2Friends);
- when(friendShipStorage.getFriends(3L)).thenReturn(friend3Friends);
-
- userService.deleteUser(userId);
-
- assertFalse(friend2Friends.contains(userId));
- assertFalse(friend3Friends.contains(userId));
- verify(userStorage).remove(userId);
}
@Test
@@ -244,7 +213,7 @@ public void shouldAddFriendSuccessfully() {
userService.addFriend(senderId, receiverId);
verify(friendShipStorage).addFriend(senderId, receiverId);
- verify(friendShipStorage).addFriend(receiverId, senderId);
+ verify(friendShipStorage, never()).addFriend(receiverId, senderId);
}
@Test
@@ -292,7 +261,7 @@ public void shouldDeleteFriendSuccessfully() {
userService.deleteFriend(senderId, receiverId);
verify(friendShipStorage).deleteFriend(senderId, receiverId);
- verify(friendShipStorage).deleteFriend(receiverId, senderId);
+ verify(friendShipStorage, never()).deleteFriend(receiverId, senderId);
}
@Test
@@ -327,9 +296,7 @@ public void shouldGetFriendsSuccessfully() {
when(userStorage.contains(userId)).thenReturn(true);
when(friendShipStorage.getFriends(userId)).thenReturn(friendIds);
- when(userStorage.get(2L)).thenReturn(friend1);
- when(userStorage.get(3L)).thenReturn(friend2);
-
+ when(userStorage.getAllFromCollection(friendIds)).thenReturn(List.of(friend1, friend2));
Collection friends = userService.getFriends(userId);
assertNotNull(friends);
@@ -337,8 +304,7 @@ public void shouldGetFriendsSuccessfully() {
assertTrue(friends.contains(friend1));
assertTrue(friends.contains(friend2));
verify(friendShipStorage).getFriends(userId);
- verify(userStorage).get(2L);
- verify(userStorage).get(3L);
+ verify(userStorage).getAllFromCollection(friendIds);
}
@Test
@@ -368,19 +334,16 @@ public void shouldThrowNotFoundExceptionWhenGettingFriendsOfNonExistentUser() {
public void shouldGetCommonFriendsSuccessfully() {
Long user1Id = 1L;
Long user2Id = 2L;
- Set user1Friends = Set.of(3L, 4L, 5L);
- Set user2Friends = Set.of(4L, 5L, 6L);
+ Set common = Set.of(4L, 5L);
User commonFriend1 = new User(4L, "common1@mail.com", "common1", "Common One", LocalDate.now());
User commonFriend2 = new User(5L, "common2@mail.com", "common2", "Common Two", LocalDate.now());
+ List commonUsers = List.of(commonFriend1, commonFriend2);
when(userStorage.contains(user1Id)).thenReturn(true);
when(userStorage.contains(user2Id)).thenReturn(true);
- when(friendShipStorage.getFriends(user1Id)).thenReturn(user1Friends);
- when(friendShipStorage.getFriends(user2Id)).thenReturn(user2Friends);
- when(userStorage.get(4L)).thenReturn(commonFriend1);
- when(userStorage.get(5L)).thenReturn(commonFriend2);
-
+ when(friendShipStorage.getCommonFriends(user1Id, user2Id)).thenReturn(common);
+ when(userStorage.getAllFromCollection(common)).thenReturn(commonUsers);
Collection commonFriends = userService.getCommonFriends(user1Id, user2Id);
assertNotNull(commonFriends);
@@ -393,13 +356,10 @@ public void shouldGetCommonFriendsSuccessfully() {
public void shouldReturnEmptyCommonFriendsWhenNoCommonFriends() {
Long user1Id = 1L;
Long user2Id = 2L;
- Set user1Friends = Set.of(3L, 4L);
- Set user2Friends = Set.of(5L, 6L);
when(userStorage.contains(user1Id)).thenReturn(true);
when(userStorage.contains(user2Id)).thenReturn(true);
- when(friendShipStorage.getFriends(user1Id)).thenReturn(user1Friends);
- when(friendShipStorage.getFriends(user2Id)).thenReturn(user2Friends);
+ when(friendShipStorage.getCommonFriends(user1Id, user2Id)).thenReturn(Set.of());
Collection commonFriends = userService.getCommonFriends(user1Id, user2Id);
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorageTest.java
index b7fb5b2..e17c170 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorageTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryFilmStorageTest.java
@@ -2,9 +2,12 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import ru.yandex.practicum.filmorate.films.film.InMemoryFilmStorage;
+import ru.yandex.practicum.filmorate.storage.film.memory.InMemoryFilmStorage;
import ru.yandex.practicum.filmorate.model.Film;
+import java.time.LocalDate;
+import java.util.List;
+
import static org.junit.jupiter.api.Assertions.*;
public class InMemoryFilmStorageTest {
@@ -74,8 +77,47 @@ public void shouldReturnFalseIfNotContains() {
}
@Test
- public void shouldReturnSize() {
- storage.add(film);
- assertEquals(storage.getAll().size(), storage.size());
+ public void shouldReturnTopFilms() {
+ Film film1 = new Film(1L, "Film 1", "Desc 1", LocalDate.now(), 120);
+ film1.setLikes(10L);
+
+ Film film2 = new Film(2L, "Film 2", "Desc 2", LocalDate.now(), 130);
+ film2.setLikes(5L);
+
+ Film film3 = new Film(3L, "Film 3", "Desc 3", LocalDate.now(), 140);
+ film3.setLikes(15L);
+
+ storage.add(film1);
+ storage.add(film2);
+ storage.add(film3);
+
+ List top = (List) storage.getTopFilms(5L);
+
+ assertEquals(3,top.size());
+ assertEquals(film3, top.get(0));
+ assertEquals(film1, top.get(1));
+ assertEquals(film2, top.get(2));
+ }
+
+ @Test
+ public void shouldReturnOnlyTwoTopFilms() {
+ Film film1 = new Film(1L, "Film 1", "Desc 1", LocalDate.now(), 120);
+ film1.setLikes(10L);
+
+ Film film2 = new Film(2L, "Film 2", "Desc 2", LocalDate.now(), 130);
+ film2.setLikes(5L);
+
+ Film film3 = new Film(3L, "Film 3", "Desc 3", LocalDate.now(), 140);
+ film3.setLikes(15L);
+
+ storage.add(film1);
+ storage.add(film2);
+ storage.add(film3);
+
+ List top = (List) storage.getTopFilms(2L);
+
+ assertEquals(2,top.size());
+ assertEquals(film3, top.get(0));
+ assertEquals(film1, top.get(1));
}
}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorageTest.java
index b51076d..0c9f24c 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorageTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/InMemoryLikeStorageTest.java
@@ -2,6 +2,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.storage.film.memory.InMemoryLikeStorage;
import java.lang.reflect.Field;
import java.util.Map;
@@ -29,23 +30,9 @@ private Map> getLikesField() {
}
}
- @Test
- public void shouldCreateLikesSet() {
- storage.initializeLikesSet(1L);
- assertNotNull(getLikesField().get(1L));
- }
-
- @Test
- public void shouldRemoveSet() {
- storage.initializeLikesSet(1L);
- storage.clearLikesSet(1L);
- assertNull(getLikesField().get(1L));
- }
@Test
public void shouldAddLike() {
- storage.initializeLikesSet(1L);
-
storage.addLike(1L, 1L);
storage.addLike(1L, 2L);
@@ -54,7 +41,6 @@ public void shouldAddLike() {
@Test
public void shouldAddLikeOnlyOnce() {
- storage.initializeLikesSet(1L);
storage.addLike(1L, 1L);
storage.addLike(1L, 1L);
@@ -62,14 +48,8 @@ public void shouldAddLikeOnlyOnce() {
assertEquals(1, getLikesField().get(1L).size());
}
- @Test
- public void shouldThrowNullPointerExceptionWhenAddLikeToFilmThatDoesNotExist() {
- assertThrows(NullPointerException.class, () -> storage.addLike(1L, 1L));
- }
-
@Test
public void shouldRemoveLike() {
- storage.initializeLikesSet(1L);
storage.addLike(1L, 1L);
storage.addLike(1L, 2L);
@@ -81,7 +61,6 @@ public void shouldRemoveLike() {
@Test
public void shouldRemoveLikeOnlyOnce() {
- storage.initializeLikesSet(1L);
storage.addLike(1L, 1L);
storage.addLike(1L, 2L);
@@ -93,7 +72,9 @@ public void shouldRemoveLikeOnlyOnce() {
}
@Test
- public void shouldThrowNullPointerExceptionWhenRemoveLikeToFilmThatDoesNotExist() {
- assertThrows(NullPointerException.class, () -> storage.deleteLike(1L, 1L));
+ public void shouldClearLikes() {
+ storage.addLike(1L, 2L);
+ storage.clearLikes(1L);
+ assertFalse(getLikesField().containsKey(1L));
}
}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbFilmStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbFilmStorageTest.java
new file mode 100644
index 0000000..1b79354
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbFilmStorageTest.java
@@ -0,0 +1,117 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import lombok.RequiredArgsConstructor;
+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.Import;
+import org.springframework.dao.EmptyResultDataAccessException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.storage.mappers.FilmMapper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@JdbcTest
+@Import({DbFilmStorage.class, FilmMapper.class})
+@AutoConfigureTestDatabase
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class DbFilmStorageTest {
+
+ private final DbFilmStorage filmStorage;
+
+ @Test
+ public void shouldGetFilm() {
+ Film film = filmStorage.get(1L);
+ assertNotNull(film);
+ assertEquals("name1", film.getName());
+ }
+
+ @Test
+ public void shouldThrowWhenNotFound() {
+ assertThrows(EmptyResultDataAccessException.class, () -> filmStorage.get(1000L));
+ }
+
+ @Test
+ public void shouldGetAll() {
+ List list = (List) filmStorage.getAll();
+
+ assertEquals(5, list.size());
+
+ for (int i = 1; i <= list.size(); i++) {
+ assertEquals("name" + i, list.get(i - 1).getName());
+ }
+ }
+
+ @Test
+ public void shouldReturnTrue() {
+ boolean actual = filmStorage.contains(1L);
+ assertTrue(actual);
+ }
+
+ @Test
+ public void shouldReturnFalse() {
+ boolean actual = filmStorage.contains(1000L);
+ assertFalse(actual);
+ }
+
+ @Test
+ public void shouldAddFilm() {
+ Film film = new Film();
+ film.setName("name6");
+ film.setDescription("desc6");
+
+ Film actual = filmStorage.add(film);
+
+ assertEquals(6, actual.getId());
+
+ assertTrue(filmStorage.contains(6L));
+ }
+
+ @Test
+ public void shouldUpdateFilm() {
+ Film film = filmStorage.get(1L);
+ film.setName("newName1");
+
+ Film actual = filmStorage.update(film);
+
+ assertEquals("newName1", actual.getName());
+ }
+
+ @Test
+ public void shouldRemoveFilm() {
+ filmStorage.remove(1L);
+ assertFalse(filmStorage.contains(1L));
+ }
+
+ @Test
+ public void shouldGetTopFilmsLimit() {
+
+ Collection topFilms = filmStorage.getTopFilms(2L);
+
+ assertNotNull(topFilms);
+ assertEquals(2, topFilms.size());
+
+ List resultList = new ArrayList<>(topFilms);
+ assertEquals(3L, resultList.get(0).getId());
+ assertEquals(2L, resultList.get(1).getId());
+ }
+
+ @Test
+ public void shouldGetTopFilmsNoLimit() {
+
+ Collection topFilms = filmStorage.getTopFilms(1000L);
+
+ assertNotNull(topFilms);
+ assertEquals(5, topFilms.size());
+
+ List resultList = new ArrayList<>(topFilms);
+ assertEquals(3L, resultList.get(0).getId());
+ assertEquals(2L, resultList.get(1).getId());
+ assertEquals(1L, resultList.get(2).getId());
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbGenreStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbGenreStorageTest.java
new file mode 100644
index 0000000..9698668
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbGenreStorageTest.java
@@ -0,0 +1,115 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import lombok.RequiredArgsConstructor;
+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.Import;
+import org.springframework.dao.DataIntegrityViolationException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Genre;
+import ru.yandex.practicum.filmorate.storage.mappers.FilmMapper;
+import ru.yandex.practicum.filmorate.storage.mappers.GenreMapper;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@JdbcTest
+@Import({DbGenreStorage.class, GenreMapper.class, DbFilmStorage.class, FilmMapper.class})
+@AutoConfigureTestDatabase
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class DbGenreStorageTest {
+
+ private final DbGenreStorage storage;
+ private final DbFilmStorage filmStorage;
+
+ @Test
+ public void shouldGetGenre() {
+
+ Optional genreOptional = storage.getGenre(1L);
+
+ assertThat(genreOptional)
+ .isPresent()
+ .hasValueSatisfying(genre ->
+ assertThat(genre)
+ .hasFieldOrPropertyWithValue("id", 1L)
+ .hasFieldOrPropertyWithValue("name", "Комедия")
+ );
+ }
+
+ @Test
+ public void shouldGetEmptyGenre() {
+ Optional genreOptional = storage.getGenre(1000L);
+ assertThat(genreOptional).isEmpty();
+ }
+
+ @Test
+ public void shouldGetAll() {
+ List list = (List) storage.getAll();
+ assertEquals(6, list.size());
+ assertEquals("Боевик", list.getLast().getName());
+ }
+
+ @Test
+ public void shouldAddGenresToFilm() {
+ Film film = new Film();
+ film.setId(1L);
+ List allGenres = (List) storage.getAll();
+ Set genres = new HashSet<>();
+ for (int i = 1; i <= 3; i++) {
+ genres.add(allGenres.get(i - 1));
+ }
+ film.setGenres(genres);
+
+ storage.addGenresToFilm(film);
+
+ Set actual = filmStorage.get(1L).getGenres();
+ assertEquals(3, actual.size());
+ assertEquals(genres, actual);
+ }
+
+ @Test
+ public void shouldThrowIfGenreInvalidAddGenre() {
+ Film film = new Film();
+ film.setId(1L);
+ Set genres = Set.of(new Genre(1000L, "bla"));
+ film.setGenres(genres);
+
+ assertThrows(DataIntegrityViolationException.class, () -> storage.addGenresToFilm(film));
+ }
+
+ @Test
+ public void shouldUpdateFilmGenres() {
+ Film film = new Film();
+ film.setId(1L);
+ List allGenres = (List) storage.getAll();
+ Set genres = new HashSet<>();
+ for (int i = 1; i <= 3; i++) {
+ genres.add(allGenres.get(i - 1));
+ }
+ film.setGenres(genres);
+
+ storage.updateFilmGenres(film);
+
+ Set actual = filmStorage.get(1L).getGenres();
+ assertEquals(3, actual.size());
+ assertEquals(genres, actual);
+ }
+
+ @Test
+ public void shouldThrowIfGenreInvalidUpdateGenre() {
+ Film film = new Film();
+ film.setId(1L);
+ Set genres = Set.of(new Genre(1000L, "bla"));
+ film.setGenres(genres);
+
+ assertThrows(DataIntegrityViolationException.class, () -> storage.updateFilmGenres(film));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbLikeStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbLikeStorageTest.java
new file mode 100644
index 0000000..5750b1b
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbLikeStorageTest.java
@@ -0,0 +1,37 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import lombok.RequiredArgsConstructor;
+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.Import;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@JdbcTest
+@Import(DbLikeStorage.class)
+@AutoConfigureTestDatabase
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class DbLikeStorageTest {
+
+ private final DbLikeStorage storage;
+
+ @Test
+ public void shouldAddLikes() {
+ Long likes = storage.addLike(1L, 5L);
+ assertEquals(3L, likes);
+ }
+
+ @Test
+ public void shouldDeleteLikes() {
+ Long likes = storage.deleteLike(1L, 1L);
+ assertEquals(1L, likes);
+ }
+
+ @Test
+ public void shouldDoNothing() {
+ assertDoesNotThrow(() -> storage.clearLikes(1L));
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbMpaStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbMpaStorageTest.java
new file mode 100644
index 0000000..c02cd38
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/db/DbMpaStorageTest.java
@@ -0,0 +1,52 @@
+package ru.yandex.practicum.filmorate.storage.film.db;
+
+import lombok.RequiredArgsConstructor;
+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.Import;
+import ru.yandex.practicum.filmorate.model.Mpa;
+import ru.yandex.practicum.filmorate.storage.mappers.MpaMapper;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@JdbcTest
+@Import({DbMpaStorage.class, MpaMapper.class})
+@AutoConfigureTestDatabase
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class DbMpaStorageTest {
+
+ private final DbMpaStorage storage;
+
+ @Test
+ public void shouldGetMpa() {
+
+ Optional mpaOptional = storage.getMpa(1L);
+
+ assertThat(mpaOptional)
+ .isPresent()
+ .hasValueSatisfying(mpa ->
+ assertThat(mpa)
+ .hasFieldOrPropertyWithValue("id", 1L)
+ .hasFieldOrPropertyWithValue("name", "G")
+ );
+ }
+
+ @Test
+ public void shouldGetEmptyMpa() {
+ Optional mpaOptional = storage.getMpa(1000L);
+ assertThat(mpaOptional).isEmpty();
+ }
+
+ @Test
+ public void shouldGetAll() {
+ List list = (List) storage.getAll();
+ assertEquals(5, list.size());
+ assertEquals("NC-17", list.getLast().getName());
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorageTest.java
index 058ac5b..802db3a 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorageTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryFriendShipStorageTest.java
@@ -2,6 +2,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.storage.user.memory.InMemoryFriendShipStorage;
import java.lang.reflect.Field;
import java.util.Map;
@@ -32,8 +33,6 @@ private Map> getFriendShips() {
@Test
public void shouldCreateFriendShip() {
- friendShipStorage.initializeFriendsSet(1L);
- friendShipStorage.initializeFriendsSet(2L);
friendShipStorage.addFriend(1L, 2L);
friendShipStorage.addFriend(2L, 1L);
@@ -42,27 +41,15 @@ public void shouldCreateFriendShip() {
assertTrue(getFriendShips().get(2L).contains(1L));
}
- @Test
- public void shouldThrowNullPointerExceptionWhenSenderIsNotFoundCreateFriendShip() {
- assertThrows(NullPointerException.class, () -> friendShipStorage.addFriend(1000L, 1L));
- }
-
- @Test
- public void shouldThrowNullPointerExceptionWhenReceiverIsNotFoundCreateFriendShip() {
- assertThrows(NullPointerException.class, () -> friendShipStorage.addFriend(1L, 1000L));
- }
-
@Test
public void shouldClearUserFriendsAndDeleteUser() {
- friendShipStorage.initializeFriendsSet(1L);
- friendShipStorage.clearFriendsSet(1L);
+ friendShipStorage.addFriend(1L, 2L);
+ friendShipStorage.deleteUserFromAllFriends(1L);
assertNull(getFriendShips().get(1L));
}
@Test
public void shouldBreakFriendShip() {
- friendShipStorage.initializeFriendsSet(1L);
- friendShipStorage.initializeFriendsSet(2L);
friendShipStorage.addFriend(1L, 2L);
friendShipStorage.addFriend(2L, 1L);
@@ -74,20 +61,8 @@ public void shouldBreakFriendShip() {
assertFalse(getFriendShips().get(2L).contains(1L));
}
- @Test
- public void shouldThrowNullPointerExceptionWhenSenderIsNotFoundBreakFriendShip() {
- assertThrows(NullPointerException.class, () -> friendShipStorage.deleteFriend(1000L, 1L));
- }
-
- @Test
- public void shouldThrowNullPointerExceptionWhenReceiverIsNotFoundBreakFriendShip() {
- assertThrows(NullPointerException.class, () -> friendShipStorage.deleteFriend(1L, 1000L));
- }
-
@Test
public void shouldReturnFriends() {
- friendShipStorage.initializeFriendsSet(1L);
- friendShipStorage.initializeFriendsSet(2L);
friendShipStorage.addFriend(1L, 2L);
friendShipStorage.addFriend(2L, 1L);
@@ -96,7 +71,28 @@ public void shouldReturnFriends() {
}
@Test
- public void shouldThrowNullPointerExceptionWhenUserIsNotFoundReturnFriends() {
- assertNull(friendShipStorage.getFriends(1000L));
+ public void shouldGetCommonFriends() {
+ friendShipStorage.addFriend(1L, 2L);
+ friendShipStorage.addFriend(1L, 3L);
+ friendShipStorage.addFriend(2L, 3L);
+
+ Set common = friendShipStorage.getCommonFriends(1L, 2L);
+
+ assertTrue(common.contains(3L));
+ assertFalse(common.contains(1L));
+ assertFalse(common.contains(2L));
+ }
+
+ @Test
+ public void shouldGetCommonFriendsReverseOrder() {
+ friendShipStorage.addFriend(1L, 2L);
+ friendShipStorage.addFriend(1L, 3L);
+ friendShipStorage.addFriend(2L, 3L);
+
+ Set common = friendShipStorage.getCommonFriends(2L, 1L);
+
+ assertTrue(common.contains(3L));
+ assertFalse(common.contains(1L));
+ assertFalse(common.contains(2L));
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorageTest.java
index cd48557..e8ef364 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorageTest.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/user/InMemoryUserStorageTest.java
@@ -3,8 +3,10 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.user.memory.InMemoryUserStorage;
import java.lang.reflect.Field;
+import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@@ -89,8 +91,18 @@ public void shouldReturnFalseIfNotContains() {
}
@Test
- public void shouldReturnSize() {
- storage.add(user);
- assertEquals(storage.getAll().size(), storage.size());
+ public void shouldGetAllFromCollection() {
+ List ids = List.of(1L, 2L, 3L);
+ for (int i = 0; i < 3; i++) {
+ User user = new User();
+ user.setName(i + "");
+ storage.add(user);
+ }
+
+ List users = storage.getAllFromCollection(ids);
+
+ assertEquals("0", users.get(0).getName());
+ assertEquals("1", users.get(1).getName());
+ assertEquals("2", users.get(2).getName());
}
}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/user/db/DbFriendShipStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/user/db/DbFriendShipStorageTest.java
new file mode 100644
index 0000000..2fca837
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/user/db/DbFriendShipStorageTest.java
@@ -0,0 +1,59 @@
+package ru.yandex.practicum.filmorate.storage.user.db;
+
+import lombok.RequiredArgsConstructor;
+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.Import;
+
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@JdbcTest
+@Import({DbFriendShipStorage.class})
+@AutoConfigureTestDatabase
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class DbFriendShipStorageTest {
+
+ private final DbFriendShipStorage storage;
+
+ @Test
+ public void shouldGetFriend() {
+ Set friends = storage.getFriends(3L);
+ assertTrue(friends.contains(1L));
+ assertTrue(friends.contains(4L));
+ }
+
+ @Test
+ public void shouldAddFriend() {
+ storage.addFriend(1L, 4L);
+ Set list = storage.getFriends(1L);
+ assertTrue(list.contains(4L));
+ }
+
+ @Test
+ public void shouldDeleteFriend() {
+ storage.deleteFriend(3L, 1L);
+ Set list = storage.getFriends(3L);
+ assertFalse(list.contains(1L));
+ }
+
+ @Test
+ public void shouldGetCommonFriends() {
+ Set common = storage.getCommonFriends(2L, 3L);
+ assertTrue(common.contains(1L));
+ }
+
+ @Test
+ public void shouldGetCommonFriendsReverse() {
+ Set common = storage.getCommonFriends(3L, 2L);
+ assertTrue(common.contains(1L));
+ }
+
+ @Test
+ public void shouldDoNothing() {
+ assertDoesNotThrow(() -> storage.deleteUserFromAllFriends(1L));
+ }
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/user/db/DbUserStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/user/db/DbUserStorageTest.java
new file mode 100644
index 0000000..cf97adf
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/storage/user/db/DbUserStorageTest.java
@@ -0,0 +1,119 @@
+package ru.yandex.practicum.filmorate.storage.user.db;
+
+import lombok.RequiredArgsConstructor;
+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.Import;
+import org.springframework.dao.EmptyResultDataAccessException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.mappers.UserMapper;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@JdbcTest
+@Import({DbUserStorage.class, UserMapper.class})
+@AutoConfigureTestDatabase
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class DbUserStorageTest {
+
+ private final DbUserStorage userStorage;
+
+ @Test
+ public void shouldGetUser() {
+ User user = userStorage.get(1L);
+ assertNotNull(user);
+ assertEquals("name1", user.getName());
+ }
+
+ @Test
+ public void shouldThrowWhenNotFound() {
+ assertThrows(EmptyResultDataAccessException.class, () -> userStorage.get(1000L));
+ }
+
+ @Test
+ public void shouldGetAll() {
+ List list = (List) userStorage.getAll();
+
+ assertEquals(5, list.size());
+
+ for (int i = 1; i <= list.size(); i++) {
+ assertEquals("name" + i, list.get(i - 1).getName());
+ }
+ }
+
+ @Test
+ public void shouldReturnTrue() {
+ boolean actual = userStorage.contains(1L);
+ assertTrue(actual);
+ }
+
+ @Test
+ public void shouldReturnFalse() {
+ boolean actual = userStorage.contains(1000L);
+ assertFalse(actual);
+ }
+
+ @Test
+ public void shouldAddUser() {
+ User user = new User();
+ user.setEmail("login6");
+ user.setLogin("login6");
+
+ User actual = userStorage.add(user);
+
+ assertEquals(6, actual.getId());
+
+ assertTrue(userStorage.contains(6L));
+ }
+
+ @Test
+ public void shouldUpdateUser() {
+ User user = userStorage.get(1L);
+ user.setName("newName1");
+
+ User actual = userStorage.update(user);
+
+ assertEquals("newName1", actual.getName());
+ }
+
+ @Test
+ public void shouldRemoveUser() {
+ userStorage.remove(1L);
+ assertFalse(userStorage.contains(1L));
+ }
+
+ @Test
+ public void shouldGetAllFromCollection() {
+ List ids = List.of(3L, 4L, 5L);
+ List users = userStorage.getAllFromCollection(ids);
+
+ assertEquals(3, users.size());
+ assertEquals("name3", users.get(0).getName());
+ assertEquals("name4", users.get(1).getName());
+ assertEquals("name5", users.get(2).getName());
+ }
+
+ @Test
+ public void shouldIgnoreIfNotFound() {
+ List ids = List.of(4000L, 5L, 6L);
+ assertDoesNotThrow(() -> userStorage.getAllFromCollection(ids));
+ }
+
+ @Test
+ public void shouldIgnoreIfEmpty() {
+ List ids = Collections.emptyList();
+ List list = userStorage.getAllFromCollection(ids);
+ assertTrue(list.isEmpty());
+ }
+
+ @Test
+ public void shouldReturnEmptyListIfNull() {
+ List list = userStorage.getAllFromCollection(null);
+ assertTrue(list.isEmpty());
+ }
+}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index 947989f..6f392d3 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -1,3 +1,12 @@
logging.level.ru.yandex.practicum.filmorate=OFF
logging.level.org.springframework=WARN
-spring.main.banner-mode=off
\ No newline at end of file
+spring.main.banner-mode=off
+
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+
+spring.sql.init.mode=always
+spring.sql.init.schema-locations=classpath:schema.sql
+spring.sql.init.data-locations=classpath:data.sql
\ No newline at end of file
diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql
new file mode 100644
index 0000000..4019ea2
--- /dev/null
+++ b/src/test/resources/data.sql
@@ -0,0 +1,51 @@
+INSERT INTO mpa (name) VALUES
+('G'),
+('PG'),
+('PG-13'),
+('R'),
+('NC-17');
+
+INSERT INTO genres (name) VALUES
+('Комедия'),
+('Драма'),
+('Мультфильм'),
+('Триллер'),
+('Документальный'),
+('Боевик');
+
+INSERT INTO users (email, login, name, birthday) VALUES
+('email1', 'login1', 'name1', '2000-01-01'),
+('email2', 'login2', 'name2', '2000-01-01'),
+('email3', 'login3', 'name3', '2000-01-01'),
+('email4', 'login4', 'name4', '2000-01-01'),
+('email5', 'login5', 'name5', NULL);
+
+INSERT INTO friendships(user_id, friend_id) VALUES
+(1L, 2L),
+(2L, 1L),
+(3L, 1L),
+(3L, 4L);
+
+INSERT INTO films (name, description, release_date, duration, mpa_id) VALUES
+('name1', 'desc1', '2000-01-01', 100, 1),
+('name2', 'desc2', '2000-01-01', 100, 2),
+('name3', 'desc3', '2000-01-01', 100, 3),
+('name4', 'desc4', '2000-01-01', 100, 4),
+('name5', 'desc5', '2000-01-01', 100, 5);
+
+INSERT INTO films_genres (film_id, genre_id) VALUES
+(4,1),
+(5,1),
+(5,2),
+(5,3);
+
+INSERT INTO likes (film_id, user_id) VALUES
+(1,1),
+(1,2),
+(2,1),
+(2,3),
+(2,4),
+(3,1),
+(3,2),
+(3,3),
+(3,4);
\ No newline at end of file
diff --git a/src/test/resources/schema.sql b/src/test/resources/schema.sql
new file mode 100644
index 0000000..a1597a0
--- /dev/null
+++ b/src/test/resources/schema.sql
@@ -0,0 +1,44 @@
+CREATE TABLE IF NOT EXISTS users (
+ user_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ email VARCHAR NOT NULL,
+ login VARCHAR NOT NULL,
+ name VARCHAR,
+ birthday DATE);
+
+CREATE TABLE IF NOT EXISTS friendships (
+ user_id BIGINT,
+ friend_id BIGINT,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ FOREIGN KEY (friend_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ PRIMARY KEY (user_id, friend_id));
+
+CREATE TABLE IF NOT EXISTS mpa (
+ mpa_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ name VARCHAR NOT NULL);
+
+CREATE TABLE IF NOT EXISTS films (
+ film_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ name VARCHAR NOT NULL,
+ description VARCHAR(200),
+ release_date DATE,
+ duration INTEGER,
+ mpa_id BIGINT,
+ FOREIGN KEY (mpa_id) REFERENCES mpa(mpa_id) ON DELETE SET NULL);
+
+CREATE TABLE IF NOT EXISTS genres (
+ genre_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ name VARCHAR NOT NULL);
+
+CREATE TABLE IF NOT EXISTS films_genres (
+ film_id BIGINT,
+ genre_id BIGINT,
+ FOREIGN KEY (film_id) REFERENCES films(film_id) ON DELETE CASCADE,
+ FOREIGN KEY (genre_id) REFERENCES genres(genre_id) ON DELETE CASCADE,
+ PRIMARY KEY (film_id, genre_id));
+
+CREATE TABLE IF NOT EXISTS likes (
+ film_id BIGINT,
+ user_id BIGINT,
+ FOREIGN KEY (film_id) REFERENCES films(film_id) ON DELETE CASCADE,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ PRIMARY KEY (film_id, user_id));
\ No newline at end of file