diff --git a/pom.xml b/pom.xml
index 0cad031..9c95881 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,11 +27,24 @@
lombok
provided
+
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+ org.zalando
+ logbook-spring-boot-starter
+ 3.7.2
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
index dca451b..3f2baca 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
@@ -5,8 +5,7 @@
@SpringBootApplication
public class FilmorateApplication {
- public static void main(String[] args) {
- SpringApplication.run(FilmorateApplication.class, args);
- }
-
+ public static void main(String[] args) {
+ SpringApplication.run(FilmorateApplication.class, args);
+ }
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
index 08cf0a1..d7dd0ee 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -1,7 +1,91 @@
package ru.yandex.practicum.filmorate.controller;
-import org.springframework.web.bind.annotation.RestController;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import jakarta.validation.Valid;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.service.FilmService;
+
+import java.time.LocalDate;
+import java.util.List;
@RestController
+@RequestMapping("/films")
+@Slf4j
public class FilmController {
+ private final FilmService filmService;
+
+ public FilmController(FilmService filmService) {
+ this.filmService = filmService;
+ }
+
+ @GetMapping
+ public List getAllFilms() {
+ log.info("Fetching all films");
+ return filmService.getAllFilms();
+ }
+
+ @GetMapping("/{id}")
+ public Film getFilm(@PathVariable Long id) {
+ log.info("Fetching film with id {}", id);
+ return filmService.getFilmById(id);
+ }
+
+ @PostMapping
+ public Film addFilm(@Valid @RequestBody Film film) {
+ log.info("Adding film: {}", film);
+ validateFilm(film);
+ return filmService.addFilm(film);
+ }
+
+ @PutMapping
+ public Film updateFilm(@Valid @RequestBody Film film) {
+ log.info("Updating film: {}", film);
+ validateFilm(film);
+ return filmService.updateFilm(film);
+ }
+
+ @DeleteMapping("/{id}")
+ public void deleteFilm(@PathVariable Long id) {
+ log.info("Deleting film with id {}", id);
+ filmService.removeFilm(id);
+ }
+
+ @PutMapping("/{id}/like/{userId}")
+ public void addLike(@PathVariable Long id, @PathVariable Long userId) {
+ log.info("User {} likes film {}", userId, id);
+ filmService.addLike(id, userId);
+ }
+
+ @DeleteMapping("/{id}/like/{userId}")
+ public void removeLike(@PathVariable Long id, @PathVariable Long userId) {
+ log.info("User {} removes like from film {}", userId, id);
+ filmService.removeLike(id, userId);
+ }
+
+ @GetMapping("/popular")
+ public List getPopularFilms(@RequestParam(defaultValue = "10") int count) {
+ log.info("Fetching top {} popular films", count);
+ return filmService.getPopularFilms(count);
+ }
+
+ private void validateFilm(Film film) {
+ if (film.getName() == null || film.getName().isBlank()) {
+ log.warn("Film name is invalid");
+ throw new ValidationException("Film name is invalid");
+ }
+ if (film.getDescription() != null && film.getDescription().length() > 200) {
+ log.warn("Film description is too long");
+ throw new ValidationException("Description length exceeds 200 characters");
+ }
+ if (film.getReleaseDate() == null || film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) {
+ log.warn("Film release date is invalid");
+ throw new ValidationException("Release date is invalid");
+ }
+ if (film.getDuration() <= 0) {
+ log.warn("Film duration is invalid");
+ throw new ValidationException("Duration must be positive");
+ }
+ }
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
new file mode 100644
index 0000000..30209bd
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -0,0 +1,91 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import jakarta.validation.Valid;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.service.UserService;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@RestController
+@RequestMapping("/users")
+@Slf4j
+public class UserController {
+ private final UserService userService;
+
+ public UserController(UserService userService) {
+ this.userService = userService;
+ }
+
+ @GetMapping
+ public List getAllUsers() {
+ log.info("Fetching all users");
+ return userService.getAllUsers();
+ }
+
+ @GetMapping("/{id}")
+ public User getUser(@PathVariable Long id) {
+ log.info("Fetching user with id {}", id);
+ return userService.getUserById(id);
+ }
+
+ @PostMapping
+ public User createUser(@Valid @RequestBody User user) {
+ log.info("Creating user: {}", user);
+ validateUser(user);
+ return userService.createUser(user);
+ }
+
+ @PutMapping
+ public User updateUser(@Valid @RequestBody User user) {
+ log.info("Updating user: {}", user);
+ validateUser(user);
+ return userService.updateUser(user);
+ }
+
+ @PutMapping("/{id}/friends/{friendId}")
+ public void addFriend(@PathVariable Long id, @PathVariable Long friendId) {
+ log.info("User {} adds friend {}", id, friendId);
+ userService.addFriend(id, friendId);
+ }
+
+ @DeleteMapping("/{id}/friends/{friendId}")
+ public void removeFriend(@PathVariable Long id, @PathVariable Long friendId) {
+ log.info("User {} removes friend {}", id, friendId);
+ userService.removeFriend(id, friendId);
+ }
+
+ @GetMapping("/{id}/friends")
+ public List getFriends(@PathVariable Long id) {
+ log.info("Fetching friends of user {}", id);
+ return userService.getFriends(id);
+ }
+
+ @GetMapping("/{id}/friends/common/{otherId}")
+ public List getCommonFriends(@PathVariable Long id, @PathVariable Long otherId) {
+ log.info("Fetching common friends of users {} and {}", id, otherId);
+ return userService.getCommonFriends(id, otherId);
+ }
+
+ private void validateUser(User user) {
+ if (user.getEmail() == null || user.getEmail().isBlank() || !user.getEmail().contains("@")) {
+ log.warn("Email is invalid");
+ throw new ValidationException("Email is invalid");
+ }
+ if (user.getLogin() == null || user.getLogin().isBlank() || user.getLogin().contains(" ")) {
+ log.warn("Login is invalid");
+ throw new ValidationException("Login is invalid");
+ }
+ if (user.getName() == null || user.getName().isBlank()) {
+ user.setName(user.getLogin());
+ log.info("Name set to login for user {}", user.getLogin());
+ }
+ if (user.getBirthday() == null || user.getBirthday().isAfter(LocalDate.now())) {
+ log.warn("Birthday is in the future");
+ throw new ValidationException("Birthday is invalid");
+ }
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java
new file mode 100644
index 0000000..ad7129c
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/NotFoundException.java
@@ -0,0 +1,20 @@
+package ru.yandex.practicum.filmorate.exception;
+
+
+public class NotFoundException extends RuntimeException {
+ public NotFoundException() {
+ super();
+ }
+
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+ public NotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public NotFoundException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java
new file mode 100644
index 0000000..532f9bd
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/exception/ValidationException.java
@@ -0,0 +1,7 @@
+package ru.yandex.practicum.filmorate.exception;
+
+public class ValidationException extends RuntimeException {
+ public ValidationException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
index 3614a44..ae6de4f 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -1,12 +1,28 @@
package ru.yandex.practicum.filmorate.model;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * Film.
- */
-@Getter
-@Setter
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+
+@Data
public class Film {
+ private Long id;
+
+ @NotBlank
+ private String name;
+
+ @NotBlank
+ @Size(min = 1, max = 200)
+ private String description;
+
+ @NotNull
+ private LocalDate releaseDate;
+
+ @Min(1)
+ private int duration;
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/User.java b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
new file mode 100644
index 0000000..15af210
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -0,0 +1,21 @@
+package ru.yandex.practicum.filmorate.model;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+public class User {
+ private Long id;
+
+ @NotBlank
+ @Email
+ private String email;
+
+ @NotBlank
+ private String login;
+ private String name;
+ private LocalDate birthday;
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
new file mode 100644
index 0000000..ecea46f
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -0,0 +1,48 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.storage.FilmStorage;
+
+import java.util.List;
+
+@Service
+public class FilmService {
+ private final FilmStorage filmStorage;
+
+ public FilmService(FilmStorage filmStorage) {
+ this.filmStorage = filmStorage;
+ }
+
+ public Film addFilm(Film film) {
+ return filmStorage.addFilm(film);
+ }
+
+ public Film updateFilm(Film film) {
+ return filmStorage.updateFilm(film);
+ }
+
+ public Film getFilmById(Long id) {
+ return filmStorage.getFilmById(id);
+ }
+
+ public List getAllFilms() {
+ return filmStorage.getAllFilms();
+ }
+
+ public void removeFilm(Long id) {
+ filmStorage.removeFilm(id);
+ }
+
+ public void addLike(Long filmId, Long userId) {
+ filmStorage.addLike(filmId, userId);
+ }
+
+ public void removeLike(Long filmId, Long userId) {
+ filmStorage.removeLike(filmId, userId);
+ }
+
+ public List getPopularFilms(int count) {
+ return filmStorage.getPopularFilms(count);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
new file mode 100644
index 0000000..64b1bfb
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -0,0 +1,49 @@
+package ru.yandex.practicum.filmorate.service;
+
+import org.springframework.stereotype.Service;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.storage.UserStorage;
+
+import java.util.List;
+
+@Service
+public class UserService {
+ private final UserStorage userStorage;
+
+ public UserService(UserStorage userStorage) {
+ this.userStorage = userStorage;
+ }
+
+ public User createUser(User user) {
+ return userStorage.addUser(user);
+ }
+
+ public User updateUser(User user) {
+ return userStorage.updateUser(user);
+ }
+
+ public User getUserById(Long id) {
+ return userStorage.getUserById(id);
+ }
+
+ public List getAllUsers() {
+ return userStorage.getAllUsers();
+ }
+
+ public void addFriend(Long userId, Long friendId) {
+ userStorage.addFriend(userId, friendId);
+ }
+
+ public void removeFriend(Long userId, Long friendId) {
+ userStorage.removeFriend(userId, friendId);
+ }
+
+ public List getFriends(Long userId) {
+ return userStorage.getFriends(userId);
+ }
+
+ public List getCommonFriends(Long userId, Long otherId) {
+ return userStorage.getCommonFriends(userId, otherId);
+ }
+}
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
new file mode 100644
index 0000000..1c0d68e
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/FilmStorage.java
@@ -0,0 +1,15 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.Film;
+import java.util.List;
+
+public interface FilmStorage {
+ Film addFilm(Film film);
+ Film updateFilm(Film film);
+ Film getFilmById(Long id);
+ List getAllFilms();
+ void removeFilm(Long id);
+ void addLike(Long filmId, Long userId);
+ void removeLike(Long filmId, Long userId);
+ List getPopularFilms(int count);
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
new file mode 100644
index 0000000..57f74b4
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryFilmStorage.java
@@ -0,0 +1,79 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.util.*;
+
+@Component
+public class InMemoryFilmStorage implements FilmStorage {
+ private final Map films = new HashMap<>();
+ private final Map> likes = new HashMap<>();
+ private long idCounter = 1L;
+
+ @Override
+ public Film addFilm(Film film) {
+ film.setId(idCounter++);
+ films.put(film.getId(), film);
+ likes.put(film.getId(), new HashSet<>());
+ return film;
+ }
+
+ @Override
+ public Film updateFilm(Film film) {
+ if (film.getId() == null || !films.containsKey(film.getId())) {
+ throw new NotFoundException("Film not found");
+ }
+ films.put(film.getId(), film);
+ return film;
+ }
+
+ @Override
+ public Film getFilmById(Long id) {
+ Film film = films.get(id);
+ if (film == null) {
+ throw new NotFoundException("Film not found");
+ }
+ return film;
+ }
+
+ @Override
+ public List getAllFilms() {
+ return new ArrayList<>(films.values());
+ }
+
+ @Override
+ public void removeFilm(Long id) {
+ if (!films.containsKey(id)) {
+ throw new NotFoundException("Film not found");
+ }
+ films.remove(id);
+ likes.remove(id);
+ }
+
+ @Override
+ public void addLike(Long filmId, Long userId) {
+ if (!films.containsKey(filmId)) {
+ throw new NotFoundException("Film not found");
+ }
+ likes.get(filmId).add(userId);
+ }
+
+ @Override
+ public void removeLike(Long filmId, Long userId) {
+ if (!films.containsKey(filmId)) {
+ throw new NotFoundException("Film not found");
+ }
+ likes.get(filmId).remove(userId);
+ }
+
+ @Override
+ public List getPopularFilms(int count) {
+ return films.values().stream()
+ .sorted((f1, f2) -> Integer.compare(
+ likes.get(f2.getId()).size(), likes.get(f1.getId()).size()))
+ .limit(count)
+ .toList();
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
new file mode 100644
index 0000000..620aa36
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/InMemoryUserStorage.java
@@ -0,0 +1,89 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import org.springframework.stereotype.Component;
+import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.exception.NotFoundException;
+
+import java.util.*;
+
+@Component
+public class InMemoryUserStorage implements UserStorage {
+ private final Map users = new HashMap<>();
+ private final Map> friends = new HashMap<>();
+ private long idCounter = 1L;
+
+ @Override
+ public User addUser(User user) {
+ user.setId(idCounter++);
+ users.put(user.getId(), user);
+ friends.put(user.getId(), new HashSet<>());
+ return user;
+ }
+
+ @Override
+ public User updateUser(User user) {
+ if (user.getId() == null || !users.containsKey(user.getId())) {
+ throw new NotFoundException("User not found");
+ }
+ users.put(user.getId(), user);
+ return user;
+ }
+
+ @Override
+ public User getUserById(Long id) {
+ User user = users.get(id);
+ if (user == null) {
+ throw new NotFoundException("User not found");
+ }
+ return user;
+ }
+
+ @Override
+ public List getAllUsers() {
+ return new ArrayList<>(users.values());
+ }
+
+ @Override
+ public void removeUser(Long id) {
+ if (!users.containsKey(id)) {
+ throw new NotFoundException("User not found");
+ }
+ users.remove(id);
+ friends.remove(id);
+ }
+
+ @Override
+ public void addFriend(Long userId, Long friendId) {
+ getUserById(userId);
+ getUserById(friendId);
+ friends.get(userId).add(friendId);
+ friends.get(friendId).add(userId);
+ }
+
+ @Override
+ public void removeFriend(Long userId, Long friendId) {
+ getUserById(userId);
+ getUserById(friendId);
+ friends.get(userId).remove(friendId);
+ friends.get(friendId).remove(userId);
+ }
+
+ @Override
+ public List getFriends(Long userId) {
+ getUserById(userId);
+ return friends.get(userId).stream()
+ .map(users::get)
+ .toList();
+ }
+
+ @Override
+ public List getCommonFriends(Long userId, Long otherId) {
+ getUserById(userId);
+ getUserById(otherId);
+ Set common = new HashSet<>(friends.get(userId));
+ common.retainAll(friends.get(otherId));
+ return common.stream()
+ .map(users::get)
+ .toList();
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
new file mode 100644
index 0000000..97a3dad
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/storage/UserStorage.java
@@ -0,0 +1,16 @@
+package ru.yandex.practicum.filmorate.storage;
+
+import ru.yandex.practicum.filmorate.model.User;
+import java.util.List;
+
+public interface UserStorage {
+ User addUser(User user);
+ User updateUser(User user);
+ User getUserById(Long id);
+ List getAllUsers();
+ void removeUser(Long id);
+ void addFriend(Long userId, Long friendId);
+ void removeFriend(Long userId, Long friendId);
+ List getFriends(Long userId);
+ List getCommonFriends(Long userId, Long otherId);
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8b13789..1d1b4bf 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1 @@
-
+logging.level.org.zalando.logbook: TRACE
diff --git a/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java b/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java
new file mode 100644
index 0000000..48e3c78
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/BoundaryTests.java
@@ -0,0 +1,56 @@
+package ru.yandex.practicum.filmorate;
+
+import ru.yandex.practicum.filmorate.controller.FilmController;
+import ru.yandex.practicum.filmorate.controller.UserController;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.User;
+import org.junit.jupiter.api.Test;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.*;
+
+class BoundaryTests {
+ private FilmController filmController = new FilmController();
+ private UserController userController = new UserController();
+
+ @Test
+ void filmValidation_BoundaryValues() {
+ // Проверка минимально допустимой даты релиза
+ Film film1 = new Film();
+ film1.setName("Boundary Film");
+ film1.setDescription("Desc");
+ film1.setReleaseDate(LocalDate.of(1895, 12, 28));
+ film1.setDuration(1);
+ assertDoesNotThrow(() -> filmController.addFilm(film1));
+
+ // Проверка максимальной длины описания
+ Film film2 = new Film();
+ film2.setName("Boundary Film");
+ film2.setDescription("a".repeat(200));
+ film2.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film2.setDuration(1);
+ assertDoesNotThrow(() -> filmController.addFilm(film2));
+ }
+
+ @Test
+ void userValidation_BoundaryValues() {
+ // Проверка сегодняшней даты рождения
+ User user1 = new User();
+ user1.setEmail("test@example.com");
+ user1.setLogin("testlogin");
+ user1.setBirthday(LocalDate.now());
+ assertDoesNotThrow(() -> userController.createUser(user1));
+
+ // Проверка минимально допустимого логина
+ User user2 = new User();
+ user2.setEmail("test@example.com");
+ user2.setLogin("a"); // Минимальный логин
+ user2.setBirthday(LocalDate.of(1990, 1, 1));
+ assertDoesNotThrow(() -> userController.createUser(user2));
+ }
+
+ @Test
+ void nullRequest_ThrowsException() {
+ assertThrows(Exception.class, () -> filmController.addFilm(null));
+ assertThrows(Exception.class, () -> userController.createUser(null));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java
new file mode 100644
index 0000000..0c95ae8
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/FilmControllerTest.java
@@ -0,0 +1,109 @@
+package ru.yandex.practicum.filmorate;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.controller.FilmController;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class FilmControllerTest {
+ private FilmController filmController;
+
+ @BeforeEach
+ void setUp() {
+ filmController = new FilmController();
+ }
+
+ @Test
+ void addValidFilm() {
+ Film film = new Film();
+ film.setName("Valid Film");
+ film.setDescription("Valid Description");
+ film.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film.setDuration(120);
+
+ Film addedFilm = filmController.addFilm(film);
+ assertNotNull(addedFilm.getId());
+ assertEquals(1, filmController.getAllFilms().size());
+ }
+
+ @Test
+ void addFilmWithEmptyName() {
+ Film film = new Film();
+ film.setName("");
+ film.setDescription("Description");
+ film.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film.setDuration(120);
+
+ assertThrows(ValidationException.class, () -> filmController.addFilm(film));
+ }
+
+ @Test
+ void addFilmWithLongDescription() {
+ Film film = new Film();
+ film.setName("Name");
+ film.setDescription("A".repeat(201));
+ film.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film.setDuration(120);
+
+ assertThrows(ValidationException.class, () -> filmController.addFilm(film));
+ }
+
+ @Test
+ void addFilmWithEarlyReleaseDate() {
+ Film film = new Film();
+ film.setName("Name");
+ film.setDescription("Description");
+ film.setReleaseDate(LocalDate.of(1895, 12, 27));
+ film.setDuration(120);
+
+ assertThrows(ValidationException.class, () -> filmController.addFilm(film));
+ }
+
+ @Test
+ void addFilmWithNegativeDuration() {
+ Film film = new Film();
+ film.setName("Name");
+ film.setDescription("Description");
+ film.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film.setDuration(-120);
+
+ assertThrows(ValidationException.class, () -> filmController.addFilm(film));
+ }
+
+ @Test
+ void updateFilmWithInvalidId() {
+ Film film = new Film();
+ film.setId(999L);
+ film.setName("Name");
+ film.setDescription("Description");
+ film.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film.setDuration(120);
+
+ assertThrows(ValidationException.class, () -> filmController.updateFilm(film));
+ }
+
+ @Test
+ void getAllFilms() {
+ Film film1 = createTestFilm("Film 1");
+ Film film2 = createTestFilm("Film 2");
+
+ filmController.addFilm(film1);
+ filmController.addFilm(film2);
+
+ assertEquals(2, filmController.getAllFilms().size());
+ }
+
+ private Film createTestFilm(String name) {
+ Film film = new Film();
+ film.setName(name);
+ film.setDescription("Description");
+ film.setReleaseDate(LocalDate.of(2000, 1, 1));
+ film.setDuration(120);
+ return film;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java
new file mode 100644
index 0000000..a1a9f16
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/UserControllerTest.java
@@ -0,0 +1,113 @@
+package ru.yandex.practicum.filmorate;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import ru.yandex.practicum.filmorate.controller.UserController;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class UserControllerTest {
+ private UserController userController;
+
+ @BeforeEach
+ void setUp() {
+ userController = new UserController();
+ }
+
+ @Test
+ void createValidUser() {
+ User user = new User();
+ user.setEmail("test@mail.com");
+ user.setLogin("testlogin");
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+
+ User createdUser = userController.createUser(user);
+ assertNotNull(createdUser.getId());
+ assertEquals("testlogin", createdUser.getName());
+ assertEquals(1, userController.getAllUsers().size());
+ }
+
+ @Test
+ void createUserWithEmptyEmail() {
+ User user = new User();
+ user.setEmail("");
+ user.setLogin("login");
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+
+ assertThrows(ValidationException.class, () -> userController.createUser(user));
+ }
+
+ @Test
+ void createUserWithInvalidEmail() {
+ User user = new User();
+ user.setEmail("invalid-email");
+ user.setLogin("login");
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+
+ assertThrows(ValidationException.class, () -> userController.createUser(user));
+ }
+
+ @Test
+ void createUserWithEmptyLogin() {
+ User user = new User();
+ user.setEmail("test@mail.com");
+ user.setLogin("");
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+
+ assertThrows(ValidationException.class, () -> userController.createUser(user));
+ }
+
+ @Test
+ void createUserWithLoginContainingSpaces() {
+ User user = new User();
+ user.setEmail("test@mail.com");
+ user.setLogin("login with spaces");
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+
+ assertThrows(ValidationException.class, () -> userController.createUser(user));
+ }
+
+ @Test
+ void createUserWithFutureBirthday() {
+ User user = new User();
+ user.setEmail("test@mail.com");
+ user.setLogin("login");
+ user.setBirthday(LocalDate.now().plusDays(1));
+
+ assertThrows(ValidationException.class, () -> userController.createUser(user));
+ }
+
+ @Test
+ void updateUserWithInvalidId() {
+ User user = new User();
+ user.setId(999L);
+ user.setEmail("test@mail.com");
+ user.setLogin("login");
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+
+ assertThrows(ValidationException.class, () -> userController.updateUser(user));
+ }
+
+ @Test
+ void getAllUsers() {
+ User user1 = createTestUser("user1@mail.com", "user1");
+ User user2 = createTestUser("user2@mail.com", "user2");
+
+ userController.createUser(user1);
+ userController.createUser(user2);
+
+ assertEquals(2, userController.getAllUsers().size());
+ }
+
+ private User createTestUser(String email, String login) {
+ User user = new User();
+ user.setEmail(email);
+ user.setLogin(login);
+ user.setBirthday(LocalDate.of(1990, 1, 1));
+ return user;
+ }
+}
\ No newline at end of file