diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..02d3f75
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.5
+
+
+ ru.yandex.practicum
+ filmorate
+ 0.0.1-SNAPSHOT
+ filmorate
+ Кинопоиск для друзей
+
+ 11
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.9.0
+ test
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
new file mode 100644
index 0000000..3c12a84
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/FilmorateApplication.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class FilmorateApplication {
+
+ 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
new file mode 100644
index 0000000..6acb9a4
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -0,0 +1,73 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.server.ResponseStatusException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+@Slf4j
+@RestController
+@RequestMapping("/films")
+public class FilmController {
+ private final Set filmSet = new HashSet<>();
+ private int id = 0;
+
+ public int idGenerate() {
+ return ++id;
+ }
+
+ @GetMapping
+ public Collection getFilms() {
+ return filmSet;
+ }
+
+ @PostMapping
+ public Film create(@RequestBody Film film) {
+ validate(film);
+ film.setId(idGenerate());
+ log.info("Фильм создан {}", film.toString());
+ filmSet.add(film);
+ return film;
+ }
+
+ @PutMapping
+ public Film updateFilm(@RequestBody Film film) {
+ validate(film);
+ for (Film films : filmSet) {
+ if (film.getId() == films.getId()) {
+ filmSet.remove(films);
+ filmSet.add(film);
+ log.info("Фильм обновлен {}", film.toString());
+ } else {
+ throw new ResponseStatusException(HttpStatus.NOT_FOUND);
+ }
+ }
+ return film;
+ }
+
+ private void validate(Film film) {
+ if (film.getName().isBlank()) {
+ log.info("Отсутствует название фильма {}", film.toString());
+ throw new ValidationException("Название не может быть пустым");
+ }
+ if (film.getDescription().length() > 200) {
+ log.info("Максимальная длина описания больше 200 символов {}", film.toString());
+ throw new ValidationException("Максимальная длина описания — 200 символов");
+ }
+ if (film.getReleaseDate().isBefore(LocalDate.of(1895, 12, 28))) {
+ log.info("Дата релиза 28 декабря 1895 года {}", film.toString());
+ throw new ValidationException("Дата релиза не может быть раньше 28 декабря 1895 года");
+ }
+ if (film.getDuration() < 0) {
+ log.info("Введена отрицательная продолжительсность фильма {}", film.toString());
+ throw new ValidationException("Продолжительность фильма должна быть положительной");
+ }
+ }
+}
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..85ff7af
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java
@@ -0,0 +1,72 @@
+package ru.yandex.practicum.filmorate.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.server.ResponseStatusException;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+@Slf4j
+@RestController
+@RequestMapping("/users")
+public class UserController {
+ private Set userSet = new HashSet<>();
+ private int id = 0;
+
+ public int idGenerate() {
+ return ++id;
+ }
+
+ @GetMapping
+ public Collection users() {
+ return userSet;
+ }
+
+ @PostMapping
+ public User newUser(@RequestBody User user) {
+ validate(user);
+ if (user.getName() == null || user.getName().isEmpty() || user.getName().isBlank()) {
+ user.setName(user.getLogin());
+ }
+ user.setId(idGenerate());
+ userSet.add(user);
+ log.info("Пользователь {} создан.", user.toString());
+ return user;
+ }
+
+ @PutMapping
+ public User updateUser(@RequestBody User user) {
+ validate(user);
+ for (User users : userSet) {
+ if (user.getId() == users.getId()) {
+ userSet.remove(users);
+ userSet.add(user);
+ log.info("Пользователь {} обновлен.", user.toString());
+ } else {
+ throw new ResponseStatusException(HttpStatus.NOT_FOUND);
+ }
+ }
+ return user;
+ }
+
+ public void validate(User user) {
+ if (user.getEmail().isBlank() || !user.getEmail().contains("@")) {
+ log.info("Отсутствует электронная почта {}", user.toString());
+ throw new ValidationException("Электронная почта не может быть пустой или должна содержать символ @");
+ }
+ if (user.getLogin().isBlank() || user.getLogin().contains(" ")) {
+ log.info("Отсутствует логин {}", user.toString());
+ throw new ValidationException("Логин не может быть пустым и содержать пробелы");
+ }
+ if (user.getBirthday().isAfter(LocalDate.now())) {
+ log.info("Введена не корректная дата рождения {}", user.toString());
+ throw new ValidationException("Дата рождения не может быть в будущем");
+ }
+ }
+}
\ No newline at end of file
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..52dc49c
--- /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);
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
new file mode 100644
index 0000000..9b15e99
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -0,0 +1,18 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NonNull;
+
+import java.time.LocalDate;
+
+@Data
+@AllArgsConstructor
+public class Film {
+ private Integer id;
+ @NonNull
+ private String name;
+ private String description;
+ private LocalDate releaseDate;
+ private Integer 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..45c2d91
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/User.java
@@ -0,0 +1,16 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+@AllArgsConstructor
+public class User {
+ private Integer id;
+ private String email;
+ private String login;
+ private String name;
+ private LocalDate birthday;
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
new file mode 100644
index 0000000..dc5cfdf
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/FilmorateApplicationTests.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class FilmorateApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java b/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java
new file mode 100644
index 0000000..641a2ea
--- /dev/null
+++ b/src/test/java/ru/yandex/practicum/filmorate/ValidateTest.java
@@ -0,0 +1,156 @@
+package ru.yandex.practicum.filmorate;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import ru.yandex.practicum.filmorate.controller.FilmController;
+import ru.yandex.practicum.filmorate.controller.UserController;
+import ru.yandex.practicum.filmorate.exception.ValidationException;
+import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.User;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class ValidateTest {
+
+ @Test
+ public void createdFilmTest() {
+ final FilmController film = new FilmController();
+ Film fm = new Film(1, "Я Легенда", "Фильм про зомби", LocalDate.of(2010, 12,
+ 24), 90);
+ film.create(fm);
+ assertEquals(1, film.getFilms().size(), "Фильм не создан");
+ }
+
+ @Test
+ public void notNameFilmTest() {
+ final FilmController film = new FilmController();
+ Film fm = new Film(1, "", "Фильм про зомби", LocalDate.of(2010, 12, 24),
+ 90);
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ film.create(fm);
+ }
+ });
+ assertEquals("Название не может быть пустым", ex.getMessage());
+ }
+
+ @Test
+ public void maxSimFilmTest() {
+ final FilmController film = new FilmController();
+ Film fm = new Film(1, "Я Легенда",
+ "Фильм про зомбииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии" +
+ "ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии" +
+ "иииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии",
+ LocalDate.of(2010, 12, 24), 90);
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ film.create(fm);
+ }
+ });
+ assertEquals("Максимальная длина описания — 200 символов", ex.getMessage());
+ }
+
+ @Test
+ public void dataFilmTest() {
+ final FilmController film = new FilmController();
+ Film fm = new Film(1, "Я Легенда", "Фильм про зомби", LocalDate.of(1800, 12, 24),
+ 90);
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ film.create(fm);
+ }
+ });
+ assertEquals("Дата релиза не может быть раньше 28 декабря 1895 года", ex.getMessage());
+ }
+
+ @Test
+ public void durationFilmTest() {
+ final FilmController film = new FilmController();
+ Film fm = new Film(1, "Я Легенда", "Фильм про зомби", LocalDate.of(2010, 12, 24),
+ -1);
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ film.create(fm);
+ }
+ });
+ assertEquals("Продолжительность фильма должна быть положительной", ex.getMessage());
+ }
+
+ @Test
+ public void createdUserTest() {
+ UserController uc = new UserController();
+ User user = new User(1, "maks@yandex.ru", "maks_1992", "Maksim",
+ LocalDate.of(1992, 12, 24));
+ uc.newUser(user);
+ assertEquals(1, uc.users().size(), "Пользователь не создан");
+ }
+
+ @Test
+ public void mailUserTest() {
+ UserController uc = new UserController();
+ User user = new User(1, "maksyandex.ru", "maks_1992", "Maksim",
+ LocalDate.of(1992, 12, 24));
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ uc.newUser(user);
+ }
+ });
+ assertEquals("Электронная почта не может быть пустой или должна содержать символ @",
+ ex.getMessage());
+ }
+
+ @Test
+ public void loginUserTest() {
+ UserController uc = new UserController();
+ User user = new User(1, "maks@yandex.ru", "", "Maksim",
+ LocalDate.of(1992, 12, 24));
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ uc.newUser(user);
+ }
+ });
+ assertEquals("Логин не может быть пустым и содержать пробелы",
+ ex.getMessage());
+ }
+
+ @Test
+ public void bdUserTest() {
+ UserController uc = new UserController();
+ User user = new User(1, "maks@yandex.ru", "maks_1992", "Maksim",
+ LocalDate.of(2025, 12, 24));
+ final ValidationException ex = assertThrows(ValidationException.class,
+ new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ uc.newUser(user);
+ }
+ });
+ assertEquals("Дата рождения не может быть в будущем",
+ ex.getMessage());
+ }
+
+ @Test
+ public void createdUserNotNameTest() {
+ UserController uc = new UserController();
+ User user = new User(1, "maks@yandex.ru", "maks_1992", "",
+ LocalDate.of(1992, 12, 24));
+ uc.newUser(user);
+ assertEquals("maks_1992", user.getName(), "Не назначено имя");
+ }
+}
+