diff --git a/pom.xml b/pom.xml index 78ee59d..0651135 100644 --- a/pom.xml +++ b/pom.xml @@ -4,10 +4,10 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.javarush.khmelov - project-ledzeppelin + com.javarush.matsarskaya + project-project-pantera 1.0-SNAPSHOT - ProjectLedzeppelin + ProjectPantera war @@ -15,14 +15,9 @@ 21 21 5.10.2 + 5.18.0 - - org.springframework.boot - spring-boot-starter-parent - 3.3.5 - - @@ -65,11 +60,63 @@ junit-jupiter-engine test + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + + + org.assertj + assertj-core + 3.25.3 + test + + + org.slf4j + slf4j-api + 2.0.9 + + + ch.qos.logback + logback-classic + 1.4.14 + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + prepare-agent + + + + report + test + + report + + + + org.apache.maven.plugins maven-war-plugin @@ -89,6 +136,5 @@ - \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/cmd/Command.java b/src/main/java/com/javarush/khmelov/cmd/Command.java deleted file mode 100644 index fd4035b..0000000 --- a/src/main/java/com/javarush/khmelov/cmd/Command.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.javarush.khmelov.cmd; - -import jakarta.servlet.http.HttpServletRequest; - -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public interface Command { - - default String doGet(HttpServletRequest request) { - return getView(); - } - - default String doPost(HttpServletRequest request) { - return getView(); - } - - default String getView() { - String simpleName = this.getClass().getSimpleName(); - return convertCamelCaseToKebabStyle(simpleName); - } - - private static String convertCamelCaseToKebabStyle(String string) { - String snakeName = string.chars() - .mapToObj(s -> String.valueOf((char) s)) - .flatMap(s -> s.matches("[A-Z]") - ? Stream.of("-", s) - : Stream.of(s)) - .collect(Collectors.joining()) - .toLowerCase(); - return snakeName.startsWith("-") - ? snakeName.substring(1) - : snakeName; - } - - -} diff --git a/src/main/java/com/javarush/khmelov/cmd/EditUser.java b/src/main/java/com/javarush/khmelov/cmd/EditUser.java deleted file mode 100644 index ae191b4..0000000 --- a/src/main/java/com/javarush/khmelov/cmd/EditUser.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.javarush.khmelov.cmd; - -import com.javarush.khmelov.entity.Role; -import com.javarush.khmelov.entity.User; -import com.javarush.khmelov.service.UserService; -import jakarta.servlet.http.HttpServletRequest; - -import java.util.Optional; - - -@SuppressWarnings("unused") -public class EditUser implements Command { - - private final UserService userService; - - public EditUser(UserService userService) { - this.userService = userService; - } - - - @Override - public String doGet(HttpServletRequest req) { - String stringId = req.getParameter("id"); - if (stringId != null) { - long id = Long.parseLong(stringId); - Optional optionalUser = userService.get(id); - if (optionalUser.isPresent()) { - User user = optionalUser.get(); - req.setAttribute("user", user); - } - } - return getView(); - } - - @Override - public String doPost(HttpServletRequest req) { - User user = User.builder() - .login(req.getParameter("login")) - .password(req.getParameter("password")) - .role(Role.valueOf(req.getParameter("role"))) - .build(); - if (req.getParameter("create") != null) { - userService.create(user); - } else if (req.getParameter("update") != null) { - user.setId(Long.parseLong(req.getParameter("id"))); - userService.update(user); - } - return getView() + "?id=" + user.getId(); - } - - -} \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/cmd/ListUser.java b/src/main/java/com/javarush/khmelov/cmd/ListUser.java deleted file mode 100644 index 9257917..0000000 --- a/src/main/java/com/javarush/khmelov/cmd/ListUser.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.javarush.khmelov.cmd; - -import com.javarush.khmelov.entity.User; -import com.javarush.khmelov.service.UserService; -import jakarta.servlet.http.HttpServletRequest; - -import java.util.Collection; - -@SuppressWarnings("unused") -public class ListUser implements Command { - - private final UserService userService; - - public ListUser(UserService userService) { - this.userService = userService; - } - - @Override - public String doGet(HttpServletRequest request) { - Collection users = userService.getAll(); - request.setAttribute("users", users); - return getView(); - } - - -} \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/cmd/StartPage.java b/src/main/java/com/javarush/khmelov/cmd/StartPage.java deleted file mode 100644 index d268f93..0000000 --- a/src/main/java/com/javarush/khmelov/cmd/StartPage.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.javarush.khmelov.cmd; - -@SuppressWarnings("unused") -public class StartPage implements Command { - -} diff --git a/src/main/java/com/javarush/khmelov/config/Winter.java b/src/main/java/com/javarush/khmelov/config/Winter.java deleted file mode 100644 index 48bd8a7..0000000 --- a/src/main/java/com/javarush/khmelov/config/Winter.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.javarush.khmelov.config; - -import lombok.SneakyThrows; - -import java.lang.reflect.Constructor; -import java.util.concurrent.ConcurrentHashMap; - -public class Winter { - - public static ConcurrentHashMap, Object> components = new ConcurrentHashMap<>(); - - - @SuppressWarnings("unchecked") - @SneakyThrows - public static T find(Class aClass) { - Object component = components.get(aClass); - if (component == null) { - Constructor constructor = aClass.getConstructors()[0]; - Class[] parameterTypes = constructor.getParameterTypes(); - Object[] parameters = new Object[parameterTypes.length]; - for (int i = 0; i < parameters.length; i++) { - parameters[i] = Winter.find(parameterTypes[i]); - } - Object newInstance = constructor.newInstance(parameters); - components.put(aClass, newInstance); - } - return (T) components.get(aClass); - } -} diff --git a/src/main/java/com/javarush/khmelov/controller/FrontController.java b/src/main/java/com/javarush/khmelov/controller/FrontController.java deleted file mode 100644 index 33242b2..0000000 --- a/src/main/java/com/javarush/khmelov/controller/FrontController.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.javarush.khmelov.controller; - -import com.javarush.khmelov.cmd.Command; -import com.javarush.khmelov.config.Winter; -import com.javarush.khmelov.entity.Role; -import jakarta.servlet.ServletConfig; -import jakarta.servlet.ServletException; -import jakarta.servlet.annotation.WebServlet; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -@WebServlet({"", "/home", "/list-user", "/edit-user"}) -public class FrontController extends HttpServlet { - - private final HttpResolver httpResolver = Winter.find(HttpResolver.class); - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Command command = httpResolver.resolve(req); - String view = command.doGet(req); - String jsp = getJsp(view); - req.getRequestDispatcher(jsp).forward(req, resp); - } - - @Override - public void init(ServletConfig config) { - config.getServletContext().setAttribute("roles", Role.values()); - } - - private static String getJsp(String view) { - return "/WEB-INF/" + view + ".jsp"; - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Command command = httpResolver.resolve(req); - String redirect = command.doPost(req); - resp.sendRedirect(redirect); - } -} diff --git a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java b/src/main/java/com/javarush/khmelov/controller/HttpResolver.java deleted file mode 100644 index 18bb761..0000000 --- a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.javarush.khmelov.controller; - -import com.javarush.khmelov.cmd.Command; -import com.javarush.khmelov.config.Winter; -import jakarta.servlet.http.HttpServletRequest; - -public class HttpResolver { - - public Command resolve(HttpServletRequest request) { - // /cmd-example - try { - String requestURI = request.getRequestURI(); - requestURI = requestURI.equals("/") ? "/start-page" : requestURI; - String kebabName = requestURI.split("[?#/]")[1]; - String simpleName = convertKebabStyleToCamelCase(kebabName); - String fullName = Command.class.getPackageName() + "." + simpleName; - Class aClass = Class.forName(fullName); - return (Command) Winter.find(aClass); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private String convertKebabStyleToCamelCase(String input) { - StringBuilder result = new StringBuilder(); - boolean capitalizeNext = true; - for (char c : input.toCharArray()) { - if (c == '-') { - capitalizeNext = true; - } else { - if (capitalizeNext) { - result.append(Character.toUpperCase(c)); - capitalizeNext = false; - } else { - result.append(Character.toLowerCase(c)); - } - } - } - return result.toString(); - } -} diff --git a/src/main/java/com/javarush/khmelov/entity/Role.java b/src/main/java/com/javarush/khmelov/entity/Role.java deleted file mode 100644 index 5ae365f..0000000 --- a/src/main/java/com/javarush/khmelov/entity/Role.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.javarush.khmelov.entity; - -public enum Role { - USER, ADMIN, GUEST -} diff --git a/src/main/java/com/javarush/khmelov/entity/User.java b/src/main/java/com/javarush/khmelov/entity/User.java deleted file mode 100644 index f7fa2d6..0000000 --- a/src/main/java/com/javarush/khmelov/entity/User.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.javarush.khmelov.entity; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class User { - - private Long id; - - private String login; - - private String password; - - private Role role; - - public String getImage() { //TODO move to DTO - return "image-" + id; - } - -} diff --git a/src/main/java/com/javarush/khmelov/repository/Repository.java b/src/main/java/com/javarush/khmelov/repository/Repository.java deleted file mode 100644 index f1abdac..0000000 --- a/src/main/java/com/javarush/khmelov/repository/Repository.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.javarush.khmelov.repository; - -import com.javarush.khmelov.entity.User; - -import java.util.Collection; -import java.util.Optional; - -public interface Repository { - - Collection getAll(); - - Optional get(long id); - - void create(T entity); - - void update(T entity); - - void delete(T entity); -} diff --git a/src/main/java/com/javarush/khmelov/repository/UserRepository.java b/src/main/java/com/javarush/khmelov/repository/UserRepository.java deleted file mode 100644 index 58b32ea..0000000 --- a/src/main/java/com/javarush/khmelov/repository/UserRepository.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.javarush.khmelov.repository; - -import com.javarush.khmelov.entity.Role; -import com.javarush.khmelov.entity.User; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicLong; - -public class UserRepository implements Repository { - - private final Map map = new HashMap<>(); - - public static final AtomicLong id = new AtomicLong(System.currentTimeMillis()); - - public UserRepository() { - map.put(1L, new User(1L, "Alisa", "qwerty", Role.USER)); - map.put(2L, new User(2L, "Bob", "", Role.GUEST)); - map.put(3L, new User(3L, "Carl", "admin", Role.ADMIN)); - map.put(4L, new User(4L, "Khmelov", "admin", Role.ADMIN)); - } - - @Override - public Collection getAll() { - return map.values(); - } - - @Override - public Optional get(long id) { - return Optional.ofNullable(map.get(id)); - } - - @Override - public void create(User entity) { - entity.setId(id.incrementAndGet()); - update(entity); - } - - @Override - public void update(User entity) { - map.put(entity.getId(), entity); - } - - @Override - public void delete(User entity) { - map.remove(entity.getId()); - } -} diff --git a/src/main/java/com/javarush/khmelov/service/UserService.java b/src/main/java/com/javarush/khmelov/service/UserService.java deleted file mode 100644 index b17527c..0000000 --- a/src/main/java/com/javarush/khmelov/service/UserService.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.javarush.khmelov.service; - -import com.javarush.khmelov.entity.User; -import com.javarush.khmelov.repository.UserRepository; - -import java.util.Collection; -import java.util.Optional; - -public class UserService { - - private final UserRepository userRepository; - - public UserService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - public void create(User user) { - userRepository.create(user); - } - - public void update(User user) { - userRepository.update(user); - } - - public void delete(User user) { - userRepository.delete(user); - } - - public Collection getAll() { - return userRepository.getAll(); - } - - public Optional get(long id) { - return userRepository.get(id); - } -} diff --git a/src/main/java/com/javarush/matsarskaya/cmd/Command.java b/src/main/java/com/javarush/matsarskaya/cmd/Command.java new file mode 100644 index 0000000..39464a2 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/Command.java @@ -0,0 +1,15 @@ +package com.javarush.matsarskaya.cmd; + +import jakarta.servlet.http.HttpServletRequest; + +public interface Command { + default String doGet(HttpServletRequest request) { + return getView(); + } + + default String doPost(HttpServletRequest request) { + return getView(); + } + + String getView(); +} diff --git a/src/main/java/com/javarush/matsarskaya/cmd/HomePage.java b/src/main/java/com/javarush/matsarskaya/cmd/HomePage.java new file mode 100644 index 0000000..f6bb4e1 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/HomePage.java @@ -0,0 +1,19 @@ +package com.javarush.matsarskaya.cmd; + +import jakarta.servlet.http.HttpServletRequest; + +public class HomePage implements Command{ + + @Override + public String doGet(HttpServletRequest request) { + return getView(); + } + @Override + public String doPost(HttpServletRequest request) { + return getView(); + } + @Override + public String getView() { + return "/WEB-INF/home-page.jsp"; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/cmd/LoginPage.java b/src/main/java/com/javarush/matsarskaya/cmd/LoginPage.java new file mode 100644 index 0000000..61ca7ff --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/LoginPage.java @@ -0,0 +1,54 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.exception.InvalidCredentialsException; +import com.javarush.matsarskaya.exception.UserNotFoundException; +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginPage implements Command { + private static final Logger logger = LoggerFactory.getLogger(LoginPage.class); + private final UserService userService; + + public LoginPage(UserService userService) { + this.userService = userService; + } + + @Override + public String doGet(HttpServletRequest request) { + logger.debug("Displaying the login page"); + return getView(); + } + + @Override + public String doPost(HttpServletRequest request) { + String username = request.getParameter("username"); + String password = request.getParameter("password"); + logger.info("Processing the POST login request for the user: {}", username); + + try { + userService.loginUser(username, password); + HttpSession session = request.getSession(); + logger.info("Successful user login: {}", username); + session.setAttribute("username", username); + return "/home-page"; + } catch (UserNotFoundException e) { + logger.warn("Failed login attempt: user {} not found", username); + request.setAttribute("error", "User not found"); + } catch (InvalidCredentialsException e) { + logger.warn("Failed login attempt: invalid password for user {}", username); + request.setAttribute("error", "Invalid username or password"); + } catch (Exception e) { + logger.error("Unexpected error when user logs in {}: {}", username, e.getMessage(), e); + request.setAttribute("error", "Error occurred when logging in"); + } + return getView(); + } + + @Override + public String getView() { + return "/WEB-INF/login-page.jsp"; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/cmd/LogoutPage.java b/src/main/java/com/javarush/matsarskaya/cmd/LogoutPage.java new file mode 100644 index 0000000..da09368 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/LogoutPage.java @@ -0,0 +1,28 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.http.HttpServletRequest; + +public class LogoutPage implements Command{ + private final UserService userService; + + public LogoutPage(UserService userService) { + this.userService = userService; + } + + @Override + public String doPost(HttpServletRequest request) { + userService.logout(request); + return getView(); + } + + @Override + public String doGet(HttpServletRequest request) { + return doPost(request); + } + + @Override + public String getView() { + return "/home-page"; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/cmd/QuestDragon.java b/src/main/java/com/javarush/matsarskaya/cmd/QuestDragon.java new file mode 100644 index 0000000..57736f3 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/QuestDragon.java @@ -0,0 +1,101 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.service.StatisticService; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.Optional; + +public class QuestDragon implements Command{ + private final StatisticService statisticService; + + public QuestDragon(StatisticService statisticService) { + this.statisticService = statisticService; + } + + @Override + public String getView() { + return "/WEB-INF/quest-dragon.jsp"; + } + + @Override + public String doGet(HttpServletRequest request) { + return getView(); + } + + @Override + public String doPost(HttpServletRequest request) { + String stageParam = request.getParameter("stage"); + String choice = request.getParameter("choice"); + String playerNameInput = request.getParameter("playerNameInput"); + String quest = request.getParameter("quest"); + + var session = request.getSession(); + + if (quest != null && quest.equals("the way of the dragon rider")) { + session.setAttribute("stage", 0); + session.setAttribute("trust", 50); + session.setAttribute("questFinished", false); + + Optional.ofNullable((String) session.getAttribute("username")) + .ifPresent(statisticService::registerAttempt); + return getView(); + } else if (stageParam != null && Integer.parseInt(stageParam) == 0) { + session.setAttribute("stage", 1); + } else if (stageParam != null && Integer.parseInt(stageParam) == 1 + && playerNameInput != null && !playerNameInput.isEmpty()) { + + session.setAttribute("playerName", playerNameInput); + session.setAttribute("stage", 2); + + } else if (stageParam != null && choice != null) { + + int currentStage = Integer.parseInt(stageParam); + + String username = (String) session.getAttribute("username"); + Boolean finished = Optional.ofNullable((Boolean) session.getAttribute("questFinished")) + .orElse(false); + + if (currentStage >= 2 && currentStage <= 10) { + Integer trust = Optional.ofNullable((Integer) session.getAttribute("trust")) + .orElse(50); + + int trustChange = Integer.parseInt(choice); + trust += trustChange; + trust = Math.max(0, Math.min(100, trust)); + + session.setAttribute("trust", trust); + + boolean isLossCondition = false; + if (currentStage >= 4 && currentStage <= 7 && trust < 50) { + isLossCondition = true; + } else if (currentStage >= 8 && currentStage <= 11 && trust < 70) { + isLossCondition = true; + } + + if (!finished && isLossCondition) { + Optional.ofNullable(username).ifPresent(statisticService::registerLoss); + session.setAttribute("questFinished", true); + return getView(); + } + } + + int nextStage = currentStage + 1; + session.setAttribute("stage", nextStage); + + Integer trust = (Integer) session.getAttribute("trust"); + + if (!finished && nextStage == 11) { + session.setAttribute("questFinished", true); + if (trust != null && trust >= 70) { + Optional.ofNullable(username).ifPresent(statisticService::registerWin); + } else { + Optional.ofNullable(username).ifPresent(statisticService::registerLoss); + } + } + + } else { + return "/WEB-INF/home-page.jsp"; + } + return getView(); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/matsarskaya/cmd/RegisterPage.java b/src/main/java/com/javarush/matsarskaya/cmd/RegisterPage.java new file mode 100644 index 0000000..49dc192 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/RegisterPage.java @@ -0,0 +1,41 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.exception.UserAlreadyExistsException; +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +public class RegisterPage implements Command{ + private final UserService userService; + + public RegisterPage(UserService userService) { + this.userService = userService; + } + + @Override + public String doGet(HttpServletRequest request) { + return getView(); + } + + @Override + public String doPost(HttpServletRequest request) { + String username = request.getParameter("username"); + String password = request.getParameter("password"); + + try { + userService.registerUser(username, password); + HttpSession session = request.getSession(); + session.setAttribute("username", username); + return "/home-page"; + } catch (UserAlreadyExistsException e) { + request.setAttribute("error", "The user already exists"); + } + + return getView(); + } + + @Override + public String getView() { + return "/WEB-INF/register-page.jsp"; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/cmd/StatisticPage.java b/src/main/java/com/javarush/matsarskaya/cmd/StatisticPage.java new file mode 100644 index 0000000..a154f63 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/cmd/StatisticPage.java @@ -0,0 +1,31 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.entity.Statistic; +import com.javarush.matsarskaya.service.StatisticService; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Optional; + +public class StatisticPage implements Command{ + private final StatisticService statisticService; + + public StatisticPage(StatisticService statisticService) { + this.statisticService = statisticService; + } + + @Override + public String doGet(HttpServletRequest request) { + Optional username = Optional.ofNullable((String) request.getSession().getAttribute("username")); + + username.ifPresent(un -> { + Optional statistic = statisticService.getStatistic(un); + statistic.ifPresent(s -> request.setAttribute("statistic", s)); + }); + + return getView(); + } + + @Override + public String getView() { + return "/WEB-INF/statistic-page.jsp"; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/config/FileStorageConfig.java b/src/main/java/com/javarush/matsarskaya/config/FileStorageConfig.java new file mode 100644 index 0000000..85d6e1e --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/config/FileStorageConfig.java @@ -0,0 +1,15 @@ +package com.javarush.matsarskaya.config; + +public class FileStorageConfig { + private static final String USERS_FILE_PATH = "users.txt"; + private static final String STATISTICS_FILE_PATH = "statistics.txt"; + + private FileStorageConfig() { + } + public static String getUsersFilePath() { + return USERS_FILE_PATH; + } + public static String getStatisticsFilePath() { + return STATISTICS_FILE_PATH; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/controller/FrontController.java b/src/main/java/com/javarush/matsarskaya/controller/FrontController.java new file mode 100644 index 0000000..f7982bd --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/controller/FrontController.java @@ -0,0 +1,66 @@ +package com.javarush.matsarskaya.controller; + +import com.javarush.matsarskaya.cmd.Command; +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; + + +@WebServlet({"/home-page", "/quest-dragon", "/login-page", "/register-page", "/logout", "/statistic-page"}) +public class FrontController extends HttpServlet { + private static final Logger logger = LoggerFactory.getLogger(FrontController.class); + private final HttpResolver httpResolver; + + public FrontController() { + this(new HttpResolver()); + } + + public FrontController(HttpResolver httpResolver) { + this.httpResolver = httpResolver; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + processRequest(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + processRequest(req, resp); + } + + private void processRequest(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + String pathInfo = req.getServletPath(); + String method = req.getMethod(); + + logger.info("Received {} request for path: {}", method, pathInfo); + + if (isProtectedPath(pathInfo) && !UserService.isAuthenticated(req)) { + logger.warn("An attempt to access a secure page without authorization: {}", pathInfo); + resp.sendRedirect(req.getContextPath() + "/home-page"); + return; + } + + try { + Command command = httpResolver.resolve(pathInfo); + String viewPath = "GET".equals(req.getMethod()) ? command.doGet(req) : command.doPost(req); + logger.debug("Request processed successfully, redirection to: {}", viewPath); + req.getRequestDispatcher(viewPath).forward(req, resp); + } catch (Exception e) { + logger.error("Request processing error {}: {}", pathInfo, e.getMessage(), e); + throw e; + } + } + private boolean isProtectedPath(String pathInfo) { + return "/quest-dragon".equals(pathInfo) || "/statistic-page".equals(pathInfo); + } +} + + diff --git a/src/main/java/com/javarush/matsarskaya/controller/HttpResolver.java b/src/main/java/com/javarush/matsarskaya/controller/HttpResolver.java new file mode 100644 index 0000000..86a0047 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/controller/HttpResolver.java @@ -0,0 +1,38 @@ +package com.javarush.matsarskaya.controller; + +import com.javarush.matsarskaya.cmd.*; +import com.javarush.matsarskaya.entity.UserFileStorage; +import com.javarush.matsarskaya.repository.FileStatisticRepository; +import com.javarush.matsarskaya.repository.FileUserRepository; +import com.javarush.matsarskaya.repository.StatisticRepository; +import com.javarush.matsarskaya.repository.UserRepository; +import com.javarush.matsarskaya.service.StatisticService; +import com.javarush.matsarskaya.service.UserService; + +import java.util.Map; + +public class HttpResolver { + private final Map commandMap; + + public HttpResolver() { + UserFileStorage storage = new UserFileStorage(); + UserRepository userRepository = new FileUserRepository(storage); + UserService userService = new UserService(userRepository); + + StatisticRepository statisticRepository = new FileStatisticRepository(); + StatisticService statisticService = new StatisticService(statisticRepository); + + this.commandMap = Map.of( + "/home-page", new HomePage(), + "/quest-dragon", new QuestDragon(statisticService), + "/login-page", new LoginPage(userService), + "/register-page", new RegisterPage(userService), + "/logout", new LogoutPage(userService), + "/statistic-page", new StatisticPage(statisticService) + ); + } + + public Command resolve(String pathInfo) { + return commandMap.getOrDefault(pathInfo, new HomePage()); + } +} diff --git a/src/main/java/com/javarush/matsarskaya/entity/QuestStage.java b/src/main/java/com/javarush/matsarskaya/entity/QuestStage.java new file mode 100644 index 0000000..02ae87b --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/entity/QuestStage.java @@ -0,0 +1,42 @@ +package com.javarush.matsarskaya.entity; + +import java.util.Arrays; + +public enum QuestStage { + START(0), + NAME_INPUT(1), + STAGE_2(2), STAGE_3(3), STAGE_4(4), STAGE_5(5), + STAGE_6(6), STAGE_7(7), STAGE_8(8), STAGE_9(9), + PRE_FINAL(10), + FINAL(11); + + private final int stageNumber; + + QuestStage(int stageNumber) { + this.stageNumber = stageNumber; + } + + public int getStageNumber() { + return stageNumber; + } + + public static QuestStage fromNumber(int number) { + return Arrays.stream(values()) + .filter(stage -> stage.stageNumber == number) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Invalid stage number: " + number)); + } + + public QuestStage next() { + return fromNumber(stageNumber + 1); + } + + public boolean isLossStage(int trust) { + if (stageNumber >= 3 && stageNumber <= 6) { + return trust < 50; + } else if (stageNumber >= 7 && stageNumber <= 10) { + return trust < 70; + } + return false; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/entity/QuestStatus.java b/src/main/java/com/javarush/matsarskaya/entity/QuestStatus.java new file mode 100644 index 0000000..e4caab7 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/entity/QuestStatus.java @@ -0,0 +1,7 @@ +package com.javarush.matsarskaya.entity; + +public enum QuestStatus { + IN_PROGRESS, + WON, + LOST +} diff --git a/src/main/java/com/javarush/matsarskaya/entity/Statistic.java b/src/main/java/com/javarush/matsarskaya/entity/Statistic.java new file mode 100644 index 0000000..58cab4f --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/entity/Statistic.java @@ -0,0 +1,43 @@ +package com.javarush.matsarskaya.entity; + +public class Statistic { + private final String username; + private int attempts; + private int wins; + private int losses; + + public Statistic(String username, int attempts, int wins, int losses) { + this.username = username; + this.attempts = attempts; + this.wins = wins; + this.losses = losses; + } + + public String getUsername() { + return username; + } + + public int getAttempts() { + return attempts; + } + + public int getWins() { + return wins; + } + + public int getLosses() { + return losses; + } + + public void incrementAttempts() { + attempts++; + } + + public void incrementWins() { + wins++; + } + + public void incrementLosses() { + losses++; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/entity/TrustLevel.java b/src/main/java/com/javarush/matsarskaya/entity/TrustLevel.java new file mode 100644 index 0000000..a8b2f5f --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/entity/TrustLevel.java @@ -0,0 +1,34 @@ +package com.javarush.matsarskaya.entity; + +import java.util.Arrays; + +public enum TrustLevel { + CRITICAL(0, 49), + LOW(50, 69), + HIGH(70, 100); + + private final int min; + private final int max; + + TrustLevel(int min, int max) { + this.min = min; + this.max = max; + } + + public static TrustLevel fromValue(int trust) { + return Arrays.stream(values()) + .filter(level -> trust >= level.min && trust <= level.max) + .findFirst() + .orElse(CRITICAL); + } + + public boolean isSufficientForStage(int stageNumber) { + if (stageNumber <= 2 || stageNumber >= 11) { + return true; + } + if (stageNumber >= 7) { + return this == HIGH; + } + return this != CRITICAL; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/entity/User.java b/src/main/java/com/javarush/matsarskaya/entity/User.java new file mode 100644 index 0000000..51cb711 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/entity/User.java @@ -0,0 +1,19 @@ +package com.javarush.matsarskaya.entity; + +public class User { + private String username; + private String password; + + public User(String username, String password) { + this.username = username; + this.password = password; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} diff --git a/src/main/java/com/javarush/matsarskaya/entity/UserFileStorage.java b/src/main/java/com/javarush/matsarskaya/entity/UserFileStorage.java new file mode 100644 index 0000000..370594e --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/entity/UserFileStorage.java @@ -0,0 +1,77 @@ +package com.javarush.matsarskaya.entity; + +import com.javarush.matsarskaya.config.FileStorageConfig; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class UserFileStorage { + private final Map users = new HashMap<>(); + private boolean loaded = false; + + public UserFileStorage() { + loadUsersIfNeeded(); + } + + public void saveUser(String username, String password) { + loadUsersIfNeeded(); + users.put(username, password); + saveUsersToFile(); + } + + public Optional getPasswordByUsername(String username) { + loadUsersIfNeeded(); + return Optional.ofNullable(users.get(username)); + } + + public boolean userExists(String username) { + loadUsersIfNeeded(); + return users.containsKey(username); + } + + public Map getAllUsers() { + loadUsersIfNeeded(); + return new HashMap<>(users); + } + + private void loadUsersIfNeeded() { + if (!loaded) { + loadUsersFromFile(); + loaded = true; + } + } + + private void saveUsersToFile() { + String filePath = FileStorageConfig.getUsersFilePath(); + try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) { + for (Map.Entry entry : users.entrySet()) { + writer.println(entry.getKey() + ":" + entry.getValue()); + } + } catch (IOException e) { + System.err.println("Error when writing to a file: " + e.getMessage()); + } + } + + private void loadUsersFromFile() { + String filePath = FileStorageConfig.getUsersFilePath(); + if (!Files.exists(Paths.get(filePath))) { + return; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split(":", 2); + if (parts.length == 2) { + users.put(parts[0], parts[1]); + } + } + } catch (IOException e) { + System.err.println("Error when reading from a file: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/javarush/matsarskaya/exception/InvalidCredentialsException.java b/src/main/java/com/javarush/matsarskaya/exception/InvalidCredentialsException.java new file mode 100644 index 0000000..893d6cd --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/exception/InvalidCredentialsException.java @@ -0,0 +1,11 @@ +package com.javarush.matsarskaya.exception; + +public class InvalidCredentialsException extends RuntimeException { + public InvalidCredentialsException() { + super("Invalid username or password"); + } + + public InvalidCredentialsException(String message) { + super(message); + } +} diff --git a/src/main/java/com/javarush/matsarskaya/exception/UserAlreadyExistsException.java b/src/main/java/com/javarush/matsarskaya/exception/UserAlreadyExistsException.java new file mode 100644 index 0000000..d68819d --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/exception/UserAlreadyExistsException.java @@ -0,0 +1,11 @@ +package com.javarush.matsarskaya.exception; + +public class UserAlreadyExistsException extends RuntimeException { + public UserAlreadyExistsException(String username) { + super("A user named '\" username + \"' already exists"); + } + + public UserAlreadyExistsException(String username, Throwable cause) { + super("A user named '\" username + \"' already exists", cause); + } +} diff --git a/src/main/java/com/javarush/matsarskaya/exception/UserNotFoundException.java b/src/main/java/com/javarush/matsarskaya/exception/UserNotFoundException.java new file mode 100644 index 0000000..ca13d5f --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/exception/UserNotFoundException.java @@ -0,0 +1,11 @@ +package com.javarush.matsarskaya.exception; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String username) { + super("The user named '\" username + \"' was not found"); + } + + public UserNotFoundException(String username, Throwable cause) { + super("The user named '\" username + \"' was not found", cause); + } +} diff --git a/src/main/java/com/javarush/matsarskaya/repository/FileStatisticRepository.java b/src/main/java/com/javarush/matsarskaya/repository/FileStatisticRepository.java new file mode 100644 index 0000000..e458849 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/repository/FileStatisticRepository.java @@ -0,0 +1,69 @@ +package com.javarush.matsarskaya.repository; + +import com.javarush.matsarskaya.config.FileStorageConfig; +import com.javarush.matsarskaya.entity.Statistic; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class FileStatisticRepository implements StatisticRepository { + private final Map statistics = new HashMap<>(); + private boolean loaded = false; + + private void loadIfNeeded() { + if (loaded) return; + loaded = true; + + String filePath = FileStorageConfig.getStatisticsFilePath(); + if (!Files.exists(Paths.get(filePath))) { + return; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split(":"); + if (parts.length == 4) { + String username = parts[0]; + int attempts = Integer.parseInt(parts[1]); + int wins = Integer.parseInt(parts[2]); + int losses = Integer.parseInt(parts[3]); + + statistics.put(username, new Statistic(username, attempts, wins, losses)); + } + } + } catch (IOException e) { + throw new RuntimeException("Reading error " + filePath, e); + } + } + + @Override + public Optional findByUsername(String username) { + loadIfNeeded(); + return Optional.ofNullable(statistics.get(username)); + } + + @Override + public void save(Statistic statistic) { + loadIfNeeded(); + statistics.put(statistic.getUsername(), statistic); + + String filePath = FileStorageConfig.getStatisticsFilePath(); + try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) { + for (Statistic s : statistics.values()) { + writer.println( + s.getUsername() + ":" + + s.getAttempts() + ":" + + s.getWins() + ":" + + s.getLosses() + ); + } + } catch (IOException e) { + throw new RuntimeException("Recording error " + filePath, e); + } + } +} diff --git a/src/main/java/com/javarush/matsarskaya/repository/FileUserRepository.java b/src/main/java/com/javarush/matsarskaya/repository/FileUserRepository.java new file mode 100644 index 0000000..c387bfe --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/repository/FileUserRepository.java @@ -0,0 +1,48 @@ +package com.javarush.matsarskaya.repository; + +import com.javarush.matsarskaya.entity.User; +import com.javarush.matsarskaya.entity.UserFileStorage; +import com.javarush.matsarskaya.exception.UserAlreadyExistsException; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FileUserRepository implements UserRepository{ + private static final Logger logger = LoggerFactory.getLogger(FileUserRepository.class); + private final UserFileStorage storage; + + public FileUserRepository(UserFileStorage storage) { + this.storage = storage; + logger.info("FileUserRepository initialized"); + } + + @Override + public boolean save(User user) { + logger.debug("Saving the user: {}", user.getUsername()); + if (storage.userExists(user.getUsername())) { + logger.warn("The user {} already exists in the repository", user.getUsername()); + throw new UserAlreadyExistsException(user.getUsername()); + } + storage.saveUser(user.getUsername(), user.getPassword()); + logger.info("User {} saved successfully", user.getUsername()); + return true; + } + + @Override + public Optional findByUsername(String username) { + logger.debug("Search for a user by name: {}", username); + Optional password = storage.getPasswordByUsername(username); + if (password.isPresent()) { + logger.debug("User {} found", username); + } else { + logger.debug("User {} not found", username); + } + return password.map(pwd -> new User(username, pwd)); + } + + @Override + public boolean existsByUsername(String username) { + logger.debug("Verifying the user's existence: {}", username); + return storage.userExists(username); + } +} diff --git a/src/main/java/com/javarush/matsarskaya/repository/StatisticRepository.java b/src/main/java/com/javarush/matsarskaya/repository/StatisticRepository.java new file mode 100644 index 0000000..11a2aeb --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/repository/StatisticRepository.java @@ -0,0 +1,10 @@ +package com.javarush.matsarskaya.repository; + +import com.javarush.matsarskaya.entity.Statistic; + +import java.util.Optional; + +public interface StatisticRepository { + Optional findByUsername(String username); + void save(Statistic statistic); +} diff --git a/src/main/java/com/javarush/matsarskaya/repository/UserRepository.java b/src/main/java/com/javarush/matsarskaya/repository/UserRepository.java new file mode 100644 index 0000000..f239fb6 --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/repository/UserRepository.java @@ -0,0 +1,11 @@ +package com.javarush.matsarskaya.repository; + +import com.javarush.matsarskaya.entity.User; + +import java.util.Optional; + +public interface UserRepository { + boolean save(User user); + Optional findByUsername(String username); + boolean existsByUsername(String username); +} diff --git a/src/main/java/com/javarush/matsarskaya/service/StatisticService.java b/src/main/java/com/javarush/matsarskaya/service/StatisticService.java new file mode 100644 index 0000000..de69b0f --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/service/StatisticService.java @@ -0,0 +1,49 @@ +package com.javarush.matsarskaya.service; + +import com.javarush.matsarskaya.entity.Statistic; +import com.javarush.matsarskaya.repository.StatisticRepository; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StatisticService { + private static final Logger logger = LoggerFactory.getLogger(StatisticService.class); + private final StatisticRepository repository; + + public StatisticService(StatisticRepository repository) { + this.repository = repository; + logger.info("StatisticService initialized"); + } + + private Statistic getOrCreate(String username) { + logger.debug("Getting or creating statistics for a user: {}", username); + return repository.findByUsername(username) + .orElseGet(() -> new Statistic(username, 0, 0, 0)); + } + + public void registerAttempt(String username) { + logger.debug("Registering an attempt for a user: {}", username); + Statistic statistic = getOrCreate(username); + statistic.incrementAttempts(); + repository.save(statistic); + } + + public void registerWin(String username) { + logger.info("Victory registration for the user: {}", username); + Statistic statistic = getOrCreate(username); + statistic.incrementWins(); + repository.save(statistic); + } + + public void registerLoss(String username) { + logger.info("Defeat registration for the user: {}", username); + Statistic statistic = getOrCreate(username); + statistic.incrementLosses(); + repository.save(statistic); + } + + public Optional getStatistic(String username) { + logger.debug("Getting statistics for the user: {}", username); + return repository.findByUsername(username); + } +} diff --git a/src/main/java/com/javarush/matsarskaya/service/UserService.java b/src/main/java/com/javarush/matsarskaya/service/UserService.java new file mode 100644 index 0000000..640b00b --- /dev/null +++ b/src/main/java/com/javarush/matsarskaya/service/UserService.java @@ -0,0 +1,77 @@ +package com.javarush.matsarskaya.service; + +import com.javarush.matsarskaya.entity.User; +import com.javarush.matsarskaya.exception.InvalidCredentialsException; +import com.javarush.matsarskaya.exception.UserAlreadyExistsException; +import com.javarush.matsarskaya.exception.UserNotFoundException; +import com.javarush.matsarskaya.repository.UserRepository; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class UserService { + private static final Logger logger = LoggerFactory.getLogger(UserService.class); + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + logger.info("UserService is initialized"); + } + + public void registerUser(String username, String password) { + logger.info("User Registration Attempt: {}", username); + + if (userRepository.existsByUsername(username)) { + logger.warn("The user named {} already exists", username); + throw new UserAlreadyExistsException(username); + } + + User newUser = new User(username, password); + userRepository.save(newUser); + logger.info("User {} successfully registered", username); + } + + public Optional loginUser(String username, String password) { + logger.info("User Login Attempt: {}", username); + Optional user = userRepository.findByUsername(username); + + if (user.isEmpty()) { + logger.warn("User {} not found", username); + throw new UserNotFoundException(username); + } + + if (!user.get().getPassword().equals(password)) { + logger.warn("Invalid password for the user: {}", username); + throw new InvalidCredentialsException(); + } + logger.info("User {} successfully logged in", username); + return user; + } + + public static boolean isAuthenticated(HttpServletRequest request) { + HttpSession session = request.getSession(false); + boolean authenticated = session != null && session.getAttribute("username") != null; + + if (authenticated) { + String username = (String) session.getAttribute("username"); + LoggerFactory.getLogger(UserService.class).debug("Authorization check: user {} is authorized", username); + } + + return authenticated; + } + + public void logout(HttpServletRequest request) { + HttpSession session = request.getSession(false); + if (session != null) { + String username = (String) session.getAttribute("username"); + session.invalidate(); + logger.info("User {} logged out", username); + } else { + logger.debug("Attempt to log out without an active session"); + } + } +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..9df70c6 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,27 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + logs/application.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + logs/application.%d{yyyy-MM-dd}.log + 30 + + + + + + + + + + diff --git a/src/main/webapp/WEB-INF/edit-user.jsp b/src/main/webapp/WEB-INF/edit-user.jsp deleted file mode 100644 index f274104..0000000 --- a/src/main/webapp/WEB-INF/edit-user.jsp +++ /dev/null @@ -1,73 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp" %> - -
-
-
- - - Edit user: - - -
- -
- - min 3 symbols -
-
- - -
- -
- - min 8 symb -
-
- - - -
- -
- -
-
- - -
- -
- - - - - - - -
-
- -
-
-
- - diff --git a/src/main/webapp/WEB-INF/head.jsp b/src/main/webapp/WEB-INF/head.jsp deleted file mode 100644 index 2f7b9f2..0000000 --- a/src/main/webapp/WEB-INF/head.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> - - - Title - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/header.jsp b/src/main/webapp/WEB-INF/header.jsp new file mode 100644 index 0000000..d1b0433 --- /dev/null +++ b/src/main/webapp/WEB-INF/header.jsp @@ -0,0 +1,24 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="jakarta.tags.core" prefix="c" %> + + +
+ +
+ diff --git a/src/main/webapp/WEB-INF/home-page.jsp b/src/main/webapp/WEB-INF/home-page.jsp new file mode 100644 index 0000000..3488e1e --- /dev/null +++ b/src/main/webapp/WEB-INF/home-page.jsp @@ -0,0 +1,26 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + Квесты + + + + + +

Здесь начинается ваше приключение!"

+

Выберите квест чтобы продолжить

+ +
+ + +
+ + +

Зарегистрируйтесь/ войдите чтобы продолжить

+
+ +

${message}

+ + diff --git a/src/main/webapp/WEB-INF/list-user.jsp b/src/main/webapp/WEB-INF/list-user.jsp deleted file mode 100644 index dd52c55..0000000 --- a/src/main/webapp/WEB-INF/list-user.jsp +++ /dev/null @@ -1,9 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp"%> - - - ${user.login} - - - - diff --git a/src/main/webapp/WEB-INF/login-page.jsp b/src/main/webapp/WEB-INF/login-page.jsp new file mode 100644 index 0000000..d35e834 --- /dev/null +++ b/src/main/webapp/WEB-INF/login-page.jsp @@ -0,0 +1,29 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + Вход + + + + + + +

Вход

+ +

${error}

+
+
+

+ + +

+

+ + +

+ +
+

Назад

+ + diff --git a/src/main/webapp/WEB-INF/quest-dragon.jsp b/src/main/webapp/WEB-INF/quest-dragon.jsp new file mode 100644 index 0000000..aa7cbf9 --- /dev/null +++ b/src/main/webapp/WEB-INF/quest-dragon.jsp @@ -0,0 +1,259 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + Как приручить дракона + + + + + + +

Как приручить дракона

+ + + + + + +

Доверие дракона: ${trust}%

+
+ + + + +

❌ Поражение ❌

+ + + +

Дракон резко отдёргивается и делает угрожающий шаг назад.
+ Наставник качает головой: доверие нельзя требовать — его нужно заслужить.

+
+ +

Дракон фыркает и отворачивается.
+ — Видишь, ${playerName}, — вздыхает наставник, — с таким подходом партнёров не заводят.

+
+ +

Наставник строго смотрит на тебя.
+ — Полёт без понимания — это падение с задержкой.

+
+ +

Дракон нервно переступает с лапы на лапу.
+ Он не чувствует в тебе надёжного всадника.

+
+ +

Дракон резко приземляется, не желая продолжать.
+ — Страх чувствуется сразу, — говорит наставник.

+
+
+ +
+ +
+
+ + +

❌ Поражение ❌

+ + + +

Дракон начинает сильно нервничать и теряет уверенность.
+ Полёт приходится прервать раньше времени.

+
+ +

Дракон резко уходит вниз, избегая столкновения.
+ Он больше не чувствует поддержки.

+
+ +

Приземление выходит резким.
+ Дракон отстраняется, явно недовольный тем, как всё закончилось.

+
+
+ +
+ +
+
+ + + + + +

+ Ты всегда знал, что однажды этот день настанет.
+ После долгого пути ты оказываешься в долине всадников — месте, где люди и драконы становятся напарниками на всю жизнь.
+ Сегодня тебе предстоит пройти испытание, от которого зависит, примет ли тебя дракон… +

+ +
+ + +
+
+ + +

+ Перед тобой медленно открываются массивные ворота долины.
+ Старый наставник говорит:
+ — Ну что ж, смелости тебе не занимать. Назови своё имя, будущий всадник. +

+ +
+ + + +
+
+ + +

Наставник ведёт тебя в просторный вольер.
+ Перед тобой — четыре дракона, каждый оценивает тебя по-своему.

+ + + +
+ +
+
+
+ +
+
+ + +

Ты подходишь ближе. Дракон внимательно следит за тобой.

+ +
+ +
+
+
+ +
+
+ + +

Наставник кивает и протягивает тебе мешочек с угощением. + — Посмотрим, ${playerName}, умеешь ли ты договариваться не только словами, + — говорит он. Дракон принюхивается и слегка наклоняет голову. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + +

Наставник начинает подробно рассказывать о правилах первого полёта. + Где-то на середине объяснений ты ловишь себя на мысли, + что информации стало слишком много, а дракон смотрит на тебя так, + будто тоже не всё понял. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + +

Перед взлётом тебе предлагают экипировку. + Наставник хмурится и добавляет: — Это не обязательно, + ${playerName}… но падать без неё больнее. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + +

Дракон напрягается, расправляет крылья и делает первый резкий рывок. + Земля начинает уходить из-под ног, и становится ясно — пути назад нет. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + +

Вы уже высоко над землёй. Ветер шумит в ушах, а долина остаётся далеко внизу — красиво, + захватывающе и немного страшно. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + +

Впереди неожиданно появляется стая крупных птиц. + Дракон напряжённо взмахивает крыльями, готовясь маневрировать, и ждёт твоей реакции. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + +

Дракон плавно идёт на снижение. Земля быстро приближается, + и сейчас от твоих действий зависит, чем закончится этот полёт. + Твои действия:

+ +
+ +
+
+
+ +
+
+ + + + +

🏁 Успех 🏁

+

Дракон склоняет голову, принимая тебя как своего всадника. + Сегодня ты сделал первый шаг на пути, который изменит вашу жизнь.

+
+ +

❌ Провал ❌

+

Дракон отступает, ясно давая понять — вы пока не готовы быть командой. + Возможно, в следующий раз ты сможешь подружиться с драконом, нужна практика.

+
+
+
+ +
+
+
+
+ + + diff --git a/src/main/webapp/WEB-INF/register-page.jsp b/src/main/webapp/WEB-INF/register-page.jsp new file mode 100644 index 0000000..2e9f88c --- /dev/null +++ b/src/main/webapp/WEB-INF/register-page.jsp @@ -0,0 +1,29 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + Регистрация + + + + + + +

Регистрация

+ +

${error}

+
+
+

+ + +

+

+ + +

+ +
+

Назад

+ + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/start-page.jsp b/src/main/webapp/WEB-INF/start-page.jsp deleted file mode 100644 index 0531c1c..0000000 --- a/src/main/webapp/WEB-INF/start-page.jsp +++ /dev/null @@ -1,8 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp"%> - -

<%= "Hello World!" %> -

-
-List Users - diff --git a/src/main/webapp/WEB-INF/statistic-page.jsp b/src/main/webapp/WEB-INF/statistic-page.jsp new file mode 100644 index 0000000..0a4431a --- /dev/null +++ b/src/main/webapp/WEB-INF/statistic-page.jsp @@ -0,0 +1,32 @@ +<%@ page contentType="text/html;charset=UTF-8" %> + + + + + Статистика + + + + + + + +

Ваша статистика

+ + + + + + + + + + + + +
ПопыткиПобедыПоражения
${statistic.attempts}${statistic.wins}${statistic.losses}
+ +На главную + + + \ No newline at end of file diff --git a/src/main/webapp/css/styles.css b/src/main/webapp/css/styles.css new file mode 100644 index 0000000..a77686c --- /dev/null +++ b/src/main/webapp/css/styles.css @@ -0,0 +1,344 @@ +/* === Basic Styles === */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg-dark: #1a1612; + --bg-medium: #2d2620; + --bg-light: #3d352d; + --text-primary: #e8dcc8; + --text-secondary: #b8a99a; + --accent-gold: #c9a227; + --accent-bronze: #8b6914; + --accent-red: #8b3a3a; + --accent-green: #4a6b4a; + --border: #5c4d3d; +} + +body { + font-family: 'Georgia', 'Times New Roman', serif; + background-color: var(--bg-dark); + background-image: + radial-gradient(circle at 20% 30%, rgba(201, 162, 39, 0.05) 0%, transparent 50%), + radial-gradient(circle at 80% 70%, rgba(139, 105, 20, 0.05) 0%, transparent 50%); + min-height: 100vh; + color: var(--text-primary); + line-height: 1.7; +} + +/* === Site header === */ +header { + background: linear-gradient(180deg, var(--bg-medium) 0%, var(--bg-dark) 100%); + padding: 1.2rem 2rem; + border-bottom: 2px solid var(--accent-bronze); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); + position: sticky; + top: 0; + z-index: 100; +} + +nav { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 1200px; + margin: 0 auto; +} + +.logo h1 { + font-size: 2rem; + color: var(--accent-gold); + font-weight: normal; + letter-spacing: 2px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +.nav-links { + display: flex; + list-style: none; + gap: 1rem; +} + +.nav-links a { + color: var(--text-secondary); + text-decoration: none; + padding: 0.6rem 1.2rem; + border: 1px solid var(--border); + border-radius: 4px; + transition: all 0.2s ease; + font-size: 0.95rem; +} + +.nav-links a:hover { + background: var(--bg-light); + color: var(--accent-gold); + border-color: var(--accent-gold); +} + +/* === Main content === */ +h2 { + font-size: 2.2rem; + margin: 2.5rem 0 1.5rem; + text-align: center; + color: var(--accent-gold); + font-weight: normal; + letter-spacing: 1px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +h3 { + font-size: 1.5rem; + margin: 1.8rem 0 1.2rem; + color: var(--text-primary); + font-weight: normal; +} + +p { + margin: 1.2rem 0; + font-size: 1.1rem; + max-width: 750px; + margin-left: auto; + margin-right: auto; + color: var(--text-secondary); +} + +/* === Forms === */ +form { + max-width: 500px; + margin: 2.5rem auto; + padding: 2.5rem; + background: var(--bg-medium); + border: 2px solid var(--border); + border-radius: 8px; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4); +} + +form p { + margin-bottom: 1.5rem; + max-width: 100%; + color: var(--text-primary); +} + +label { + display: block; + margin-bottom: 0.6rem; + color: var(--accent-gold); + font-size: 0.95rem; +} + +input[type="text"], +input[type="password"] { + width: 100%; + padding: 12px 16px; + border: 1px solid var(--border); + border-radius: 4px; + font-size: 1rem; + background: var(--bg-dark); + color: var(--text-primary); + font-family: inherit; +} + +input[type="text"]:focus, +input[type="password"]:focus { + outline: none; + border-color: var(--accent-gold); + box-shadow: 0 0 0 2px rgba(201, 162, 39, 0.2); +} + +/* === Buttons === */ +button { + padding: 12px 24px; + background: linear-gradient(180deg, var(--accent-bronze) 0%, var(--accent-gold) 100%); + border: 2px solid var(--accent-gold); + border-radius: 4px; + color: var(--bg-dark); + font-size: 1rem; + font-weight: bold; + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; + text-transform: uppercase; + letter-spacing: 1px; +} + +button:hover { + background: linear-gradient(180deg, var(--accent-gold) 0%, var(--accent-bronze) 100%); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(201, 162, 39, 0.3); +} + +button:active { + transform: translateY(0); +} + +/* Buttons in the quest with animation */ +form button { + width: 100%; + margin: 0.8rem 0; + padding: 16px 20px; + background: linear-gradient(180deg, var(--bg-light) 0%, var(--bg-medium) 100%); + border: 2px solid var(--border); + color: var(--text-primary); + text-transform: none; + letter-spacing: normal; + font-weight: normal; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +form button::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(201, 162, 39, 0.2), transparent); + transition: left 0.5s ease; +} + +form button:hover::before { + left: 100%; +} + +form button:hover { + background: linear-gradient(180deg, var(--bg-medium) 0%, var(--bg-light) 100%); + border-color: var(--accent-gold); + color: var(--accent-gold); + transform: translateY(-3px); + box-shadow: 0 6px 20px rgba(201, 162, 39, 0.3); +} + +/* === Links === */ +a { + color: var(--accent-gold); + text-decoration: none; + transition: color 0.2s ease; +} + +a:hover { + color: var(--text-primary); + text-decoration: underline; +} + +/* === Statistics table === */ +table { + width: 100%; + max-width: 500px; + margin: 2.5rem auto; + border-collapse: collapse; + background: var(--bg-medium); + border: 2px solid var(--border); + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4); +} + +table th, +table td { + padding: 16px 20px; + text-align: center; + border-bottom: 1px solid var(--border); +} + +table th { + background: var(--bg-light); + color: var(--accent-gold); + font-weight: normal; + text-transform: uppercase; + letter-spacing: 1px; + font-size: 0.9rem; +} + +table tr:hover { + background: var(--bg-light); +} + +table tr:last-child td { + border-bottom: none; +} + +/* === Images === */ +img { + display: block; + margin: 2.5rem auto; + border-radius: 4px; + max-width: 100%; + border: 3px solid var(--border); + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); +} + +/* === Messages === */ +p[style*="color: #ff3f3f"], +p[style*="color: #ff3535"] { + background: rgba(139, 58, 58, 0.2); + border: 1px solid var(--accent-red); + border-radius: 4px; + padding: 1rem; + margin: 1.5rem auto; + max-width: 500px; + text-align: center; + color: #ff9999; +} + +p[style*="color: #4099ff"] { + background: rgba(74, 107, 74, 0.2); + border: 1px solid var(--accent-green); + border-radius: 4px; + padding: 1rem; + margin: 1.5rem auto; + max-width: 500px; + text-align: center; + color: #99cc99; +} + +/* === Adaptability === */ +@media (max-width: 768px) { + nav { + flex-direction: column; + gap: 1rem; + } + + .nav-links { + flex-wrap: wrap; + justify-content: center; + } + + .logo h1 { + font-size: 1.6rem; + } + + h2 { + font-size: 1.8rem; + } + + form { + padding: 2rem; + margin: 1.5rem; + } + + p { + font-size: 1rem; + padding: 0 1rem; + } +} + +/* === Scrollbar === */ +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track { + background: var(--bg-dark); +} + +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 2px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--accent-bronze); +} diff --git a/src/main/webapp/images/cat.png b/src/main/webapp/images/cat.png deleted file mode 100644 index 41771a1..0000000 Binary files a/src/main/webapp/images/cat.png and /dev/null differ diff --git a/src/main/webapp/images/fourDragonImg.jpg b/src/main/webapp/images/fourDragonImg.jpg new file mode 100644 index 0000000..22d2ebd Binary files /dev/null and b/src/main/webapp/images/fourDragonImg.jpg differ diff --git a/src/test/java/com/javarush/matsarskaya/cmd/HomePageTest.java b/src/test/java/com/javarush/matsarskaya/cmd/HomePageTest.java new file mode 100644 index 0000000..d85871d --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/cmd/HomePageTest.java @@ -0,0 +1,49 @@ +package com.javarush.matsarskaya.cmd; + +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Тесты для HomePage") +class HomePageTest { + @Mock + private HttpServletRequest request; + + private HomePage homePage; + + @BeforeEach + void setUp() { + homePage = new HomePage(); + } + + @Test + @DisplayName("The GET request returns the path to the home page") + void testDoGet() { + String result = homePage.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/home-page.jsp"); + } + + @Test + @DisplayName("The POST request returns the path to the home page") + void testDoPost() { + String result = homePage.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/home-page.jsp"); + } + + @Test + @DisplayName("Getting the path to the view") + void testGetView() { + String result = homePage.getView(); + + assertThat(result).isEqualTo("/WEB-INF/home-page.jsp"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/cmd/LoginPageTest.java b/src/test/java/com/javarush/matsarskaya/cmd/LoginPageTest.java new file mode 100644 index 0000000..90752b9 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/cmd/LoginPageTest.java @@ -0,0 +1,102 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.entity.User; +import com.javarush.matsarskaya.exception.InvalidCredentialsException; +import com.javarush.matsarskaya.exception.UserNotFoundException; +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Тесты для LoginPage") +class LoginPageTest { + @Mock + private UserService userService; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpSession session; + + private LoginPage loginPage; + + @BeforeEach + void setUp() { + loginPage = new LoginPage(userService); + } + + @Test + @DisplayName("The GET request returns the path to the login page") + void testDoGet() { + String result = loginPage.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/login-page.jsp"); + } + + @Test + @DisplayName("Successful user login") + void testDoPostSuccess() { + when(request.getParameter("username")).thenReturn("testuser"); + when(request.getParameter("password")).thenReturn("password123"); + when(userService.loginUser("testuser", "password123")) + .thenReturn(Optional.of(new User("testuser", "password123"))); + when(request.getSession()).thenReturn(session); + + String result = loginPage.doPost(request); + + assertThat(result).isEqualTo("/home-page"); + verify(userService).loginUser("testuser", "password123"); + verify(request).getSession(); + verify(session).setAttribute("username", "testuser"); + } + + @Test + @DisplayName("Login with a non-existent user") + void testDoPostUserNotFound() { + when(request.getParameter("username")).thenReturn("nonexistent"); + when(request.getParameter("password")).thenReturn("password123"); + when(userService.loginUser("nonexistent", "password123")) + .thenThrow(new UserNotFoundException("nonexistent")); + + String result = loginPage.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/login-page.jsp"); + verify(request).setAttribute("error", "User not found"); + verify(userService).loginUser("nonexistent", "password123"); + } + + @Test + @DisplayName("Log in with an incorrect password") + void testDoPostInvalidPassword() { + when(request.getParameter("username")).thenReturn("testuser"); + when(request.getParameter("password")).thenReturn("wrongpassword"); + when(userService.loginUser("testuser", "wrongpassword")) + .thenThrow(new InvalidCredentialsException()); + + String result = loginPage.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/login-page.jsp"); + verify(request).setAttribute("error", "Invalid username or password"); + verify(userService).loginUser("testuser", "wrongpassword"); + } + + @Test + @DisplayName("Getting the path to the view") + void testGetView() { + String result = loginPage.getView(); + + assertThat(result).isEqualTo("/WEB-INF/login-page.jsp"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/cmd/LogoutPageTest.java b/src/test/java/com/javarush/matsarskaya/cmd/LogoutPageTest.java new file mode 100644 index 0000000..53393b8 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/cmd/LogoutPageTest.java @@ -0,0 +1,60 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for LogoutPage") +class LogoutPageTest { + @Mock + private UserService userService; + + @Mock + private HttpServletRequest request; + + private LogoutPage logoutPage; + + @BeforeEach + void setUp() { + logoutPage = new LogoutPage(userService); + } + + @Test + @DisplayName("The POST request performs a logout") + void testDoPost() { + doNothing().when(userService).logout(request); + + String result = logoutPage.doPost(request); + + assertThat(result).isEqualTo("/home-page"); + verify(userService).logout(request); + } + + @Test + @DisplayName("The GET request calls doPost") + void testDoGet() { + doNothing().when(userService).logout(request); + + String result = logoutPage.doGet(request); + + assertThat(result).isEqualTo("/home-page"); + verify(userService).logout(request); + } + + @Test + @DisplayName("Getting the path to the view") + void testGetView() { + String result = logoutPage.getView(); + + assertThat(result).isEqualTo("/home-page"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/cmd/QuestDragonTest.java b/src/test/java/com/javarush/matsarskaya/cmd/QuestDragonTest.java new file mode 100644 index 0000000..49babb8 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/cmd/QuestDragonTest.java @@ -0,0 +1,163 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.service.StatisticService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for QuestDragon") +class QuestDragonTest { + @Mock + private StatisticService statisticService; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpSession session; + + private QuestDragon questDragon; + + @BeforeEach + void setUp() { + questDragon = new QuestDragon(statisticService); + } + + @Test + @DisplayName("The GET request returns the path to the quest page") + void testDoGet() { + String result = questDragon.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + } + + @Test + @DisplayName("The beginning of the quest") + void testDoPostStartQuest() { + when(request.getParameter("quest")).thenReturn("the way of the dragon rider"); + when(request.getParameter("stage")).thenReturn(null); + when(request.getParameter("choice")).thenReturn(null); + when(request.getParameter("playerNameInput")).thenReturn(null); + when(request.getSession()).thenReturn(session); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(session).setAttribute("stage", 0); + verify(session).setAttribute("trust", 50); + verify(session).setAttribute("questFinished", false); + } + + @Test + @DisplayName("Transition to stage 1") + void testDoPostStage0() { + when(request.getParameter("stage")).thenReturn("0"); + when(request.getSession()).thenReturn(session); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(session).setAttribute("stage", 1); + } + + @Test + @DisplayName("Entering the player's name") + void testDoPostStage1WithName() { + when(request.getParameter("stage")).thenReturn("1"); + when(request.getParameter("playerNameInput")).thenReturn("PlayerName"); + when(request.getParameter("choice")).thenReturn(null); + when(request.getSession()).thenReturn(session); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(session).setAttribute("playerName", "PlayerName"); + verify(session).setAttribute("stage", 2); + } + + @Test + @DisplayName("Moving to the next stage with a choice") + void testDoPostNextStage() { + when(request.getParameter("stage")).thenReturn("2"); + when(request.getParameter("choice")).thenReturn("10"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + when(session.getAttribute("questFinished")).thenReturn(false); + when(session.getAttribute("trust")).thenReturn(50); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(session).setAttribute("trust", 60); + verify(session).setAttribute("stage", 3); + } + + @Test + @DisplayName("Defeat at a low level of trust") + void testDoPostLossCondition() { + when(request.getParameter("stage")).thenReturn("4"); + when(request.getParameter("choice")).thenReturn("-10"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + when(session.getAttribute("questFinished")).thenReturn(false); + when(session.getAttribute("trust")).thenReturn(45); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(statisticService).registerLoss("testuser"); + verify(session).setAttribute("questFinished", true); + } + + @Test + @DisplayName("Winning with a high level of trust") + void testDoPostWinCondition() { + when(request.getParameter("stage")).thenReturn("9"); + when(request.getParameter("choice")).thenReturn("10"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + when(session.getAttribute("questFinished")).thenReturn(false); + when(session.getAttribute("trust")).thenReturn(60); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(session).setAttribute("trust", 70); + verify(session).setAttribute("stage", 10); + } + + @Test + @DisplayName("Winning the final stage with a high level of trust") + void testDoPostFinalStageWin() { + when(request.getParameter("stage")).thenReturn("10"); + when(request.getParameter("choice")).thenReturn("10"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + when(session.getAttribute("questFinished")).thenReturn(false); + when(session.getAttribute("trust")).thenReturn(70); + + String result = questDragon.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + verify(statisticService).registerWin("testuser"); + verify(session).setAttribute("questFinished", true); + verify(session).setAttribute("stage", 11); + } + + @Test + @DisplayName("Getting the path to the view") + void testGetView() { + String result = questDragon.getView(); + + assertThat(result).isEqualTo("/WEB-INF/quest-dragon.jsp"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/cmd/RegisterPageTest.java b/src/test/java/com/javarush/matsarskaya/cmd/RegisterPageTest.java new file mode 100644 index 0000000..711496f --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/cmd/RegisterPageTest.java @@ -0,0 +1,82 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.exception.UserAlreadyExistsException; +import com.javarush.matsarskaya.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for RegisterPage") +class RegisterPageTest { + @Mock + private UserService userService; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpSession session; + + private RegisterPage registerPage; + + @BeforeEach + void setUp() { + registerPage = new RegisterPage(userService); + } + + @Test + @DisplayName("The GET request returns the path to the registration page") + void testDoGet() { + String result = registerPage.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/register-page.jsp"); + } + + @Test + @DisplayName("Successful registration of a new user") + void testDoPostSuccess() { + when(request.getParameter("username")).thenReturn("newuser"); + when(request.getParameter("password")).thenReturn("password123"); + doNothing().when(userService).registerUser("newuser", "password123"); + when(request.getSession()).thenReturn(session); + + String result = registerPage.doPost(request); + + assertThat(result).isEqualTo("/home-page"); + verify(userService).registerUser("newuser", "password123"); + verify(request).getSession(); + verify(session).setAttribute("username", "newuser"); + } + + @Test + @DisplayName("Registering an existing user") + void testDoPostUserAlreadyExists() { + when(request.getParameter("username")).thenReturn("existinguser"); + when(request.getParameter("password")).thenReturn("password123"); + doThrow(new UserAlreadyExistsException("existinguser")) + .when(userService).registerUser("existinguser", "password123"); + + String result = registerPage.doPost(request); + + assertThat(result).isEqualTo("/WEB-INF/register-page.jsp"); + verify(request).setAttribute("error", "The user already exists"); + verify(userService).registerUser("existinguser", "password123"); + } + + @Test + @DisplayName("Getting the path to the view") + void testGetView() { + String result = registerPage.getView(); + + assertThat(result).isEqualTo("/WEB-INF/register-page.jsp"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/cmd/StatisticPageTest.java b/src/test/java/com/javarush/matsarskaya/cmd/StatisticPageTest.java new file mode 100644 index 0000000..16c0ec3 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/cmd/StatisticPageTest.java @@ -0,0 +1,92 @@ +package com.javarush.matsarskaya.cmd; + +import com.javarush.matsarskaya.entity.Statistic; +import com.javarush.matsarskaya.service.StatisticService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for StatisticPage") +class StatisticPageTest { + @Mock + private StatisticService statisticService; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpSession session; + + private StatisticPage statisticPage; + + @BeforeEach + void setUp() { + statisticPage = new StatisticPage(statisticService); + } + + @Test + @DisplayName("GET a request with existing statistics") + void testDoGetWithStatistic() { + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + Statistic stat = new Statistic("testuser", 10, 5, 5); + when(statisticService.getStatistic("testuser")).thenReturn(Optional.of(stat)); + + String result = statisticPage.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/statistic-page.jsp"); + verify(request).getSession(); + verify(session).getAttribute("username"); + verify(statisticService).getStatistic("testuser"); + verify(request).setAttribute("statistic", stat); + } + + @Test + @DisplayName("GET a request without statistics") + void testDoGetWithoutStatistic() { + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + when(statisticService.getStatistic("testuser")).thenReturn(Optional.empty()); + + String result = statisticPage.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/statistic-page.jsp"); + verify(request).getSession(); + verify(session).getAttribute("username"); + verify(statisticService).getStatistic("testuser"); + verify(request, never()).setAttribute(eq("statistic"), any()); + } + + @Test + @DisplayName("GET a request without an authorized user") + void testDoGetWithoutUser() { + when(request.getSession()).thenReturn(session); + when(session.getAttribute("username")).thenReturn(null); + + String result = statisticPage.doGet(request); + + assertThat(result).isEqualTo("/WEB-INF/statistic-page.jsp"); + verify(request).getSession(); + verify(session).getAttribute("username"); + verify(statisticService, never()).getStatistic(anyString()); + } + + @Test + @DisplayName("Getting the path to the view") + void testGetView() { + String result = statisticPage.getView(); + + assertThat(result).isEqualTo("/WEB-INF/statistic-page.jsp"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/config/FileStorageConfigTest.java b/src/test/java/com/javarush/matsarskaya/config/FileStorageConfigTest.java new file mode 100644 index 0000000..faa9bae --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/config/FileStorageConfigTest.java @@ -0,0 +1,39 @@ +package com.javarush.matsarskaya.config; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for FileStorageConfig") +class FileStorageConfigTest { + + @Test + @DisplayName("Getting the path to the user's file") + void testGetUsersFilePath() { + String path = FileStorageConfig.getUsersFilePath(); + + assertThat(path).isEqualTo("users.txt"); + } + + @Test + @DisplayName("Getting the path to the statistics file") + void testGetStatisticsFilePath() { + String path = FileStorageConfig.getStatisticsFilePath(); + + assertThat(path).isEqualTo("statistics.txt"); + } + + @Test + @DisplayName("Paths are not null") + void testPathsAreNotNull() { + assertThat(FileStorageConfig.getUsersFilePath()).isNotNull(); + assertThat(FileStorageConfig.getStatisticsFilePath()).isNotNull(); + } + + @Test + @DisplayName("Paths are not empty") + void testPathsAreNotEmpty() { + assertThat(FileStorageConfig.getUsersFilePath()).isNotEmpty(); + assertThat(FileStorageConfig.getStatisticsFilePath()).isNotEmpty(); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/controller/FrontControllerTest.java b/src/test/java/com/javarush/matsarskaya/controller/FrontControllerTest.java new file mode 100644 index 0000000..e26a989 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/controller/FrontControllerTest.java @@ -0,0 +1,114 @@ +package com.javarush.matsarskaya.controller; + +import com.javarush.matsarskaya.cmd.Command; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for the FrontController") +class FrontControllerTest { + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private HttpSession session; + + @Mock + private RequestDispatcher requestDispatcher; + + @Mock + private Command command; + + @Mock + private HttpResolver httpResolver; + + private FrontController frontController; + + @BeforeEach + void setUp() { + frontController = new FrontController(httpResolver); + } + + @Test + @DisplayName("The GET request is being processed correctly") + void testDoGet() throws ServletException, IOException { + when(request.getServletPath()).thenReturn("/home-page"); + when(request.getMethod()).thenReturn("GET"); + when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); + when(httpResolver.resolve("/home-page")).thenReturn(command); + when(command.doGet(request)).thenReturn("/WEB-INF/home-page.jsp"); + + frontController.doGet(request, response); + + verify(httpResolver).resolve("/home-page"); + verify(command).doGet(request); + verify(request).getRequestDispatcher(anyString()); + verify(requestDispatcher).forward(request, response); + } + + @Test + @DisplayName("The POST request is being processed correctly") + void testDoPost() throws ServletException, IOException { + when(request.getServletPath()).thenReturn("/login-page"); + when(request.getMethod()).thenReturn("POST"); + when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); + when(httpResolver.resolve("/login-page")).thenReturn(command); + when(command.doPost(request)).thenReturn("/WEB-INF/login-page.jsp"); + + frontController.doPost(request, response); + + verify(httpResolver).resolve("/login-page"); + verify(command).doPost(request); + verify(request).getRequestDispatcher(anyString()); + verify(requestDispatcher).forward(request, response); + } + + @Test + @DisplayName("Redirecting to the home page when trying to access a secure page without authorization") + void testProtectedPathWithoutAuthentication() throws ServletException, IOException { + when(request.getServletPath()).thenReturn("/quest-dragon"); + when(request.getMethod()).thenReturn("GET"); + when(request.getContextPath()).thenReturn(""); + + frontController.doGet(request, response); + + verify(response).sendRedirect("/home-page"); + } + + @Test + @DisplayName("Access to a secure page with authorization") + void testProtectedPathWithAuthentication() throws ServletException, IOException { + when(request.getServletPath()).thenReturn("/quest-dragon"); + when(request.getMethod()).thenReturn("GET"); + when(request.getSession(false)).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); + when(httpResolver.resolve("/quest-dragon")).thenReturn(command); + when(command.doGet(request)).thenReturn("/WEB-INF/quest-dragon.jsp"); + + frontController.doGet(request, response); + + verify(httpResolver).resolve("/quest-dragon"); + verify(command).doGet(request); + verify(request).getRequestDispatcher(anyString()); + verify(requestDispatcher).forward(request, response); + verify(response, never()).sendRedirect(anyString()); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/controller/HttpResolverTest.java b/src/test/java/com/javarush/matsarskaya/controller/HttpResolverTest.java new file mode 100644 index 0000000..784fea4 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/controller/HttpResolverTest.java @@ -0,0 +1,87 @@ +package com.javarush.matsarskaya.controller; + +import com.javarush.matsarskaya.cmd.Command; +import com.javarush.matsarskaya.cmd.HomePage; +import com.javarush.matsarskaya.cmd.LoginPage; +import com.javarush.matsarskaya.cmd.QuestDragon; +import com.javarush.matsarskaya.cmd.RegisterPage; +import com.javarush.matsarskaya.cmd.StatisticPage; +import com.javarush.matsarskaya.cmd.LogoutPage; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for HttpResolver") +class HttpResolverTest { + private HttpResolver httpResolver; + + @BeforeEach + void setUp() { + httpResolver = new HttpResolver(); + } + + @Test + @DisplayName("Team permission for the home page") + void testResolveHomePage() { + Command command = httpResolver.resolve("/home-page"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(HomePage.class); + } + + @Test + @DisplayName("Team permission for the quest page") + void testResolveQuestDragon() { + Command command = httpResolver.resolve("/quest-dragon"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(QuestDragon.class); + } + + @Test + @DisplayName("Team permission for the login page") + void testResolveLoginPage() { + Command command = httpResolver.resolve("/login-page"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(LoginPage.class); + } + + @Test + @DisplayName("Team permission for the registration page") + void testResolveRegisterPage() { + Command command = httpResolver.resolve("/register-page"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(RegisterPage.class); + } + + @Test + @DisplayName("Allowing the command to exit") + void testResolveLogout() { + Command command = httpResolver.resolve("/logout"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(LogoutPage.class); + } + + @Test + @DisplayName("Team permission for the statistics page") + void testResolveStatisticPage() { + Command command = httpResolver.resolve("/statistic-page"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(StatisticPage.class); + } + + @Test + @DisplayName("Resolving an unknown path returns the home page") + void testResolveUnknownPath() { + Command command = httpResolver.resolve("/unknown-path"); + + assertThat(command).isNotNull(); + assertThat(command).isInstanceOf(HomePage.class); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/entity/QuestStageTest.java b/src/test/java/com/javarush/matsarskaya/entity/QuestStageTest.java new file mode 100644 index 0000000..698d052 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/entity/QuestStageTest.java @@ -0,0 +1,94 @@ +package com.javarush.matsarskaya.entity; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DisplayName("Tests for the enum QuestStage") +class QuestStageTest { + + @Test + @DisplayName("Getting the stage number") + void testGetStageNumber() { + assertThat(QuestStage.START.getStageNumber()).isEqualTo(0); + assertThat(QuestStage.NAME_INPUT.getStageNumber()).isEqualTo(1); + assertThat(QuestStage.STAGE_2.getStageNumber()).isEqualTo(2); + assertThat(QuestStage.FINAL.getStageNumber()).isEqualTo(11); + } + + @Test + @DisplayName("Getting a stage by number") + void testFromNumber() { + assertThat(QuestStage.fromNumber(0)).isEqualTo(QuestStage.START); + assertThat(QuestStage.fromNumber(1)).isEqualTo(QuestStage.NAME_INPUT); + assertThat(QuestStage.fromNumber(5)).isEqualTo(QuestStage.STAGE_5); + assertThat(QuestStage.fromNumber(11)).isEqualTo(QuestStage.FINAL); + } + + @Test + @DisplayName("Throwing an exception for an incorrect stage number") + void testFromNumberWithInvalidNumber() { + assertThatThrownBy(() -> QuestStage.fromNumber(12)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid stage number: 12"); + + assertThatThrownBy(() -> QuestStage.fromNumber(-1)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid stage number: -1"); + } + + @Test + @DisplayName("Moving on to the next stage") + void testNext() { + assertThat(QuestStage.START.next()).isEqualTo(QuestStage.NAME_INPUT); + assertThat(QuestStage.NAME_INPUT.next()).isEqualTo(QuestStage.STAGE_2); + assertThat(QuestStage.PRE_FINAL.next()).isEqualTo(QuestStage.FINAL); + } + + @ParameterizedTest + @CsvSource({ + "3, 49, true", + "3, 50, false", + "4, 30, true", + "4, 50, false", + "5, 45, true", + "5, 50, false", + "6, 49, true", + "6, 50, false" + }) + @DisplayName("Checking the defeat condition for stages 3-6") + void testIsLossStageForStages3To6(int stageNumber, int trust, boolean expected) { + QuestStage stage = QuestStage.fromNumber(stageNumber); + assertThat(stage.isLossStage(trust)).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource({ + "7, 69, true", + "7, 70, false", + "8, 50, true", + "8, 70, false", + "9, 60, true", + "9, 70, false", + "10, 69, true", + "10, 70, false" + }) + @DisplayName("Checking the damage condition for stages 7-10") + void testIsLossStageForStages7To10(int stageNumber, int trust, boolean expected) { + QuestStage stage = QuestStage.fromNumber(stageNumber); + assertThat(stage.isLossStage(trust)).isEqualTo(expected); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2, 11}) + @DisplayName("Stages 0, 1, 2, and 11 do not have a defeat condition.") + void testIsLossStageForNonLossStages(int stageNumber) { + QuestStage stage = QuestStage.fromNumber(stageNumber); + assertThat(stage.isLossStage(0)).isFalse(); + assertThat(stage.isLossStage(100)).isFalse(); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/entity/StatisticTest.java b/src/test/java/com/javarush/matsarskaya/entity/StatisticTest.java new file mode 100644 index 0000000..04cac50 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/entity/StatisticTest.java @@ -0,0 +1,81 @@ +package com.javarush.matsarskaya.entity; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for the Statistical entity") +class StatisticTest { + + private Statistic statistic; + + @BeforeEach + void setUp() { + statistic = new Statistic("testuser", 0, 0, 0); + } + + @Test + @DisplayName("Creating statistics with zero values") + void testStatisticCreationWithZeros() { + assertThat(statistic.getUsername()).isEqualTo("testuser"); + assertThat(statistic.getAttempts()).isEqualTo(0); + assertThat(statistic.getWins()).isEqualTo(0); + assertThat(statistic.getLosses()).isEqualTo(0); + } + + @Test + @DisplayName("Creating statistics with initial values") + void testStatisticCreationWithValues() { + Statistic stat = new Statistic("player1", 10, 5, 5); + + assertThat(stat.getUsername()).isEqualTo("player1"); + assertThat(stat.getAttempts()).isEqualTo(10); + assertThat(stat.getWins()).isEqualTo(5); + assertThat(stat.getLosses()).isEqualTo(5); + } + + @Test + @DisplayName("Increasing the number of attempts") + void testIncrementAttempts() { + statistic.incrementAttempts(); + assertThat(statistic.getAttempts()).isEqualTo(1); + + statistic.incrementAttempts(); + statistic.incrementAttempts(); + assertThat(statistic.getAttempts()).isEqualTo(3); + } + + @Test + @DisplayName("Increasing the number of wins") + void testIncrementWins() { + statistic.incrementWins(); + assertThat(statistic.getWins()).isEqualTo(1); + + statistic.incrementWins(); + statistic.incrementWins(); + assertThat(statistic.getWins()).isEqualTo(3); + } + + @Test + @DisplayName("An increase in the number of lesions") + void testIncrementLosses() { + statistic.incrementLosses(); + assertThat(statistic.getLosses()).isEqualTo(1); + + statistic.incrementLosses(); + statistic.incrementLosses(); + assertThat(statistic.getLosses()).isEqualTo(3); + } + + @Test + @DisplayName("Getters return correct values.") + void testGetters() { + Statistic stat = new Statistic("user2", 15, 8, 7); + + assertThat(stat.getUsername()).isEqualTo("user2"); + assertThat(stat.getAttempts()).isEqualTo(15); + assertThat(stat.getWins()).isEqualTo(8); + assertThat(stat.getLosses()).isEqualTo(7); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/entity/TrustLevelTest.java b/src/test/java/com/javarush/matsarskaya/entity/TrustLevelTest.java new file mode 100644 index 0000000..8a9d59b --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/entity/TrustLevelTest.java @@ -0,0 +1,77 @@ +package com.javarush.matsarskaya.entity; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for enum TrustLevel") +class TrustLevelTest { + + @Test + @DisplayName("Getting the confidence level by value") + void testFromValue() { + assertThat(TrustLevel.fromValue(0)).isEqualTo(TrustLevel.CRITICAL); + assertThat(TrustLevel.fromValue(25)).isEqualTo(TrustLevel.CRITICAL); + assertThat(TrustLevel.fromValue(49)).isEqualTo(TrustLevel.CRITICAL); + + assertThat(TrustLevel.fromValue(50)).isEqualTo(TrustLevel.LOW); + assertThat(TrustLevel.fromValue(60)).isEqualTo(TrustLevel.LOW); + assertThat(TrustLevel.fromValue(69)).isEqualTo(TrustLevel.LOW); + + assertThat(TrustLevel.fromValue(70)).isEqualTo(TrustLevel.HIGH); + assertThat(TrustLevel.fromValue(85)).isEqualTo(TrustLevel.HIGH); + assertThat(TrustLevel.fromValue(100)).isEqualTo(TrustLevel.HIGH); + } + + @ParameterizedTest + @CsvSource({ + "CRITICAL, 3, false", + "CRITICAL, 4, false", + "CRITICAL, 5, false", + "CRITICAL, 6, false", + "LOW, 3, true", + "LOW, 4, true", + "LOW, 5, true", + "LOW, 6, true", + "HIGH, 3, true", + "HIGH, 4, true", + "HIGH, 5, true", + "HIGH, 6, true" + }) + @DisplayName("Verification of the sufficiency of the trust level for stages 3-6") + void testIsSufficientForStageForStages3To6(TrustLevel level, int stageNumber, boolean expected) { + assertThat(level.isSufficientForStage(stageNumber)).isEqualTo(expected); + } + + @ParameterizedTest + @CsvSource({ + "CRITICAL, 7, false", + "CRITICAL, 8, false", + "CRITICAL, 9, false", + "CRITICAL, 10, false", + "LOW, 7, false", + "LOW, 8, false", + "LOW, 9, false", + "LOW, 10, false", + "HIGH, 7, true", + "HIGH, 8, true", + "HIGH, 9, true", + "HIGH, 10, true" + }) + @DisplayName("Verification of the sufficiency of the trust level for stages 7-10") + void testIsSufficientForStageForStages7To10(TrustLevel level, int stageNumber, boolean expected) { + assertThat(level.isSufficientForStage(stageNumber)).isEqualTo(expected); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2, 11}) + @DisplayName("Verification of the sufficiency of the trust level for stages 0, 1, 2 and 11") + void testIsSufficientForStageForNonCriticalStages(int stageNumber) { + assertThat(TrustLevel.CRITICAL.isSufficientForStage(stageNumber)).isTrue(); + assertThat(TrustLevel.LOW.isSufficientForStage(stageNumber)).isTrue(); + assertThat(TrustLevel.HIGH.isSufficientForStage(stageNumber)).isTrue(); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/entity/UserTest.java b/src/test/java/com/javarush/matsarskaya/entity/UserTest.java new file mode 100644 index 0000000..841cd96 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/entity/UserTest.java @@ -0,0 +1,45 @@ +package com.javarush.matsarskaya.entity; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for the User entity") +class UserTest { + + @Test + @DisplayName("Creating a user with correct data") + void testUserCreation() { + User user = new User("testuser", "password123"); + + assertThat(user.getUsername()).isEqualTo("testuser"); + assertThat(user.getPassword()).isEqualTo("password123"); + } + + @Test + @DisplayName("Creating a user with an empty name") + void testUserWithEmptyUsername() { + User user = new User("", "password123"); + + assertThat(user.getUsername()).isEmpty(); + assertThat(user.getPassword()).isEqualTo("password123"); + } + + @Test + @DisplayName("Creating a user with an empty password") + void testUserWithEmptyPassword() { + User user = new User("testuser", ""); + + assertThat(user.getUsername()).isEqualTo("testuser"); + assertThat(user.getPassword()).isEmpty(); + } + + @Test + @DisplayName("Getters return correct values.") + void testGetters() { + User user = new User("admin", "admin123"); + + assertThat(user.getUsername()).isEqualTo("admin"); + assertThat(user.getPassword()).isEqualTo("admin123"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/exception/InvalidCredentialsExceptionTest.java b/src/test/java/com/javarush/matsarskaya/exception/InvalidCredentialsExceptionTest.java new file mode 100644 index 0000000..3142acc --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/exception/InvalidCredentialsExceptionTest.java @@ -0,0 +1,26 @@ +package com.javarush.matsarskaya.exception; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for InvalidCredentialsException") +class InvalidCredentialsExceptionTest { + + @Test + @DisplayName("Creating an exception without a message") + void testExceptionWithoutMessage() { + InvalidCredentialsException exception = new InvalidCredentialsException(); + + assertThat(exception.getMessage()).contains("Invalid username or password"); + } + + @Test + @DisplayName("Creating an exception with a message") + void testExceptionWithMessage() { + String customMessage = "Custom error message"; + InvalidCredentialsException exception = new InvalidCredentialsException(customMessage); + + assertThat(exception.getMessage()).isEqualTo(customMessage); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/exception/UserAlreadyExistsExceptionTest.java b/src/test/java/com/javarush/matsarskaya/exception/UserAlreadyExistsExceptionTest.java new file mode 100644 index 0000000..b53aea2 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/exception/UserAlreadyExistsExceptionTest.java @@ -0,0 +1,28 @@ +package com.javarush.matsarskaya.exception; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for UserAlreadyExistsException") +class UserAlreadyExistsExceptionTest { + + @Test + @DisplayName("Creating an exception with a username") + void testExceptionWithUsername() { + UserAlreadyExistsException exception = new UserAlreadyExistsException("testuser"); + + assertThat(exception.getMessage()).contains("username"); + assertThat(exception.getMessage()).contains("already exists"); + } + + @Test + @DisplayName("Creating an exception with a username and reason") + void testExceptionWithUsernameAndCause() { + Throwable cause = new RuntimeException("Database error"); + UserAlreadyExistsException exception = new UserAlreadyExistsException("testuser", cause); + + assertThat(exception.getMessage()).contains("username"); + assertThat(exception.getCause()).isEqualTo(cause); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/exception/UserNotFoundExceptionTest.java b/src/test/java/com/javarush/matsarskaya/exception/UserNotFoundExceptionTest.java new file mode 100644 index 0000000..e9d35a5 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/exception/UserNotFoundExceptionTest.java @@ -0,0 +1,28 @@ +package com.javarush.matsarskaya.exception; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for UserNotFoundException") +class UserNotFoundExceptionTest { + + @Test + @DisplayName("Creating an exception with a username") + void testExceptionWithUsername() { + UserNotFoundException exception = new UserNotFoundException("testuser"); + + assertThat(exception.getMessage()).contains("username"); + assertThat(exception.getMessage()).contains("not found"); + } + + @Test + @DisplayName("Creating an exception with a username and reason") + void testExceptionWithUsernameAndCause() { + Throwable cause = new RuntimeException("Database error"); + UserNotFoundException exception = new UserNotFoundException("testuser", cause); + + assertThat(exception.getMessage()).contains("username"); + assertThat(exception.getCause()).isEqualTo(cause); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/repository/FileStatisticRepositoryTest.java b/src/test/java/com/javarush/matsarskaya/repository/FileStatisticRepositoryTest.java new file mode 100644 index 0000000..8b9fdab --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/repository/FileStatisticRepositoryTest.java @@ -0,0 +1,102 @@ +package com.javarush.matsarskaya.repository; + +import com.javarush.matsarskaya.entity.Statistic; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Tests for FileStatisticRepository") +class FileStatisticRepositoryTest { + + @TempDir + Path tempDir; + + private FileStatisticRepository repository; + private String testFilePath; + + @Test + @DisplayName("Saving and uploading statistics") + void testSaveAndLoadStatistic() throws IOException { + testFilePath = tempDir.resolve("test_statistics.txt").toString(); + System.setProperty("statistics.file.path", testFilePath); + + repository = new FileStatisticRepository(); + + Statistic stat = new Statistic("testuser", 10, 5, 5); + repository.save(stat); + + Optional loaded = repository.findByUsername("testuser"); + + assertThat(loaded).isPresent(); + assertThat(loaded.get().getUsername()).isEqualTo("testuser"); + assertThat(loaded.get().getAttempts()).isEqualTo(10); + assertThat(loaded.get().getWins()).isEqualTo(5); + assertThat(loaded.get().getLosses()).isEqualTo(5); + } + + @Test + @DisplayName("Search for non-existent statistics") + void testFindNonExistingStatistic() throws IOException { + testFilePath = tempDir.resolve("test_statistics.txt").toString(); + System.setProperty("statistics.file.path", testFilePath); + + repository = new FileStatisticRepository(); + + Optional result = repository.findByUsername("nonexistent"); + + assertThat(result).isEmpty(); + } + + @Test + @DisplayName("Updating existing statistics") + void testUpdateExistingStatistic() throws IOException { + testFilePath = tempDir.resolve("test_statistics.txt").toString(); + System.setProperty("statistics.file.path", testFilePath); + + repository = new FileStatisticRepository(); + + Statistic stat1 = new Statistic("testuser", 5, 2, 3); + repository.save(stat1); + + Statistic stat2 = new Statistic("testuser", 10, 6, 4); + repository.save(stat2); + + Optional loaded = repository.findByUsername("testuser"); + + assertThat(loaded).isPresent(); + assertThat(loaded.get().getAttempts()).isEqualTo(10); + assertThat(loaded.get().getWins()).isEqualTo(6); + assertThat(loaded.get().getLosses()).isEqualTo(4); + } + + @Test + @DisplayName("Saving statistics for multiple users") + void testSaveMultipleStatistics() throws IOException { + testFilePath = tempDir.resolve("test_statistics.txt").toString(); + System.setProperty("statistics.file.path", testFilePath); + + repository = new FileStatisticRepository(); + + repository.save(new Statistic("user1", 10, 5, 5)); + repository.save(new Statistic("user2", 15, 8, 7)); + repository.save(new Statistic("user3", 20, 12, 8)); + + Optional stat1 = repository.findByUsername("user1"); + Optional stat2 = repository.findByUsername("user2"); + Optional stat3 = repository.findByUsername("user3"); + + assertThat(stat1).isPresent(); + assertThat(stat2).isPresent(); + assertThat(stat3).isPresent(); + + assertThat(stat1.get().getAttempts()).isEqualTo(10); + assertThat(stat2.get().getAttempts()).isEqualTo(15); + assertThat(stat3.get().getAttempts()).isEqualTo(20); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/repository/FileUserRepositoryTest.java b/src/test/java/com/javarush/matsarskaya/repository/FileUserRepositoryTest.java new file mode 100644 index 0000000..56ee156 --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/repository/FileUserRepositoryTest.java @@ -0,0 +1,106 @@ +package com.javarush.matsarskaya.repository; + +import com.javarush.matsarskaya.entity.User; +import com.javarush.matsarskaya.entity.UserFileStorage; +import com.javarush.matsarskaya.exception.UserAlreadyExistsException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for FileUserRepository") +class FileUserRepositoryTest { + @Mock + private UserFileStorage storage; + + private FileUserRepository repository; + + @BeforeEach + void setUp() { + repository = new FileUserRepository(storage); + } + + @Test + @DisplayName("Successful saving of a new user") + void testSaveNewUser() { + when(storage.userExists("newuser")).thenReturn(false); + + User user = new User("newuser", "password123"); + boolean result = repository.save(user); + + assertThat(result).isTrue(); + verify(storage).userExists("newuser"); + verify(storage).saveUser("newuser", "password123"); + } + + @Test + @DisplayName("Saving an existing user throws an exception") + void testSaveExistingUser() { + when(storage.userExists("existinguser")).thenReturn(true); + + User user = new User("existinguser", "password123"); + + assertThatThrownBy(() -> repository.save(user)) + .isInstanceOf(UserAlreadyExistsException.class) + .hasMessageContaining("username"); + + verify(storage).userExists("existinguser"); + verify(storage, never()).saveUser(anyString(), anyString()); + } + + @Test + @DisplayName("Search for an existing user by name") + void testFindByUsernameExistingUser() { + when(storage.getPasswordByUsername("testuser")).thenReturn(Optional.of("password123")); + + Optional result = repository.findByUsername("testuser"); + + assertThat(result).isPresent(); + assertThat(result.get().getUsername()).isEqualTo("testuser"); + assertThat(result.get().getPassword()).isEqualTo("password123"); + verify(storage).getPasswordByUsername("testuser"); + } + + @Test + @DisplayName("Search for a non-existent user by name") + void testFindByUsernameNonExistingUser() { + when(storage.getPasswordByUsername("nonexistent")).thenReturn(Optional.empty()); + + Optional result = repository.findByUsername("nonexistent"); + + assertThat(result).isEmpty(); + verify(storage).getPasswordByUsername("nonexistent"); + } + + @Test + @DisplayName("Verification of user's existence - user exists") + void testExistsByUsernameTrue() { + when(storage.userExists("existinguser")).thenReturn(true); + + boolean result = repository.existsByUsername("existinguser"); + + assertThat(result).isTrue(); + verify(storage).userExists("existinguser"); + } + + @Test + @DisplayName("Verification of user's existence - user does not exist") + void testExistsByUsernameFalse() { + when(storage.userExists("nonexistent")).thenReturn(false); + + boolean result = repository.existsByUsername("nonexistent"); + + assertThat(result).isFalse(); + verify(storage).userExists("nonexistent"); + } +} diff --git a/src/test/java/com/javarush/matsarskaya/service/UserServiceTest.java b/src/test/java/com/javarush/matsarskaya/service/UserServiceTest.java new file mode 100644 index 0000000..35c58bc --- /dev/null +++ b/src/test/java/com/javarush/matsarskaya/service/UserServiceTest.java @@ -0,0 +1,152 @@ +package com.javarush.matsarskaya.service; + +import com.javarush.matsarskaya.entity.User; +import com.javarush.matsarskaya.exception.InvalidCredentialsException; +import com.javarush.matsarskaya.exception.UserAlreadyExistsException; +import com.javarush.matsarskaya.exception.UserNotFoundException; +import com.javarush.matsarskaya.repository.UserRepository; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Tests for UserService") +class UserServiceTest { + @Mock + private UserRepository userRepository; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpSession session; + + private UserService userService; + + @BeforeEach + void setUp() { + userService = new UserService(userRepository); + } + + @Test + @DisplayName("Successful registration of a new user") + void testRegisterUserSuccess() { + when(userRepository.existsByUsername("newuser")).thenReturn(false); + when(userRepository.save(any(User.class))).thenReturn(true); + + userService.registerUser("newuser", "password123"); + + verify(userRepository).existsByUsername("newuser"); + verify(userRepository).save(any(User.class)); + } + + @Test + @DisplayName("Registering an existing user throws an exception") + void testRegisterUserAlreadyExists() { + when(userRepository.existsByUsername("existinguser")).thenReturn(true); + + assertThatThrownBy(() -> userService.registerUser("existinguser", "password123")) + .isInstanceOf(UserAlreadyExistsException.class) + .hasMessageContaining("username"); + + verify(userRepository).existsByUsername("existinguser"); + verify(userRepository, never()).save(any(User.class)); + } + + @Test + @DisplayName("Successful user login") + void testLoginUserSuccess() { + User user = new User("testuser", "password123"); + when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(user)); + + Optional result = userService.loginUser("testuser", "password123"); + + assertThat(result).isPresent(); + assertThat(result.get().getUsername()).isEqualTo("testuser"); + verify(userRepository).findByUsername("testuser"); + } + + @Test + @DisplayName("Logging in with a non-existent user throws an exception") + void testLoginUserNotFound() { + when(userRepository.findByUsername("nonexistent")).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> userService.loginUser("nonexistent", "password123")) + .isInstanceOf(UserNotFoundException.class) + .hasMessageContaining("username"); + + verify(userRepository).findByUsername("nonexistent"); + } + + @Test + @DisplayName("Logging in with an incorrect password throws an exception.") + void testLoginUserInvalidPassword() { + User user = new User("testuser", "correctpassword"); + when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(user)); + + assertThatThrownBy(() -> userService.loginUser("testuser", "wrongpassword")) + .isInstanceOf(InvalidCredentialsException.class); + + verify(userRepository).findByUsername("testuser"); + } + + @Test + @DisplayName("Checking authorization without a session") + void testIsAuthenticatedWithoutSession() { + when(request.getSession(false)).thenReturn(null); + + boolean result = UserService.isAuthenticated(request); + + assertThat(result).isFalse(); + verify(request).getSession(false); + } + + @Test + @DisplayName("Verifying authorization with a session without username") + void testIsAuthenticatedWithSessionWithoutUsername() { + when(request.getSession(false)).thenReturn(session); + when(session.getAttribute("username")).thenReturn(null); + + boolean result = UserService.isAuthenticated(request); + + assertThat(result).isFalse(); + verify(request).getSession(false); + verify(session).getAttribute("username"); + } + + @Test + @DisplayName("Successful logout") + void testLogoutSuccess() { + when(request.getSession(false)).thenReturn(session); + when(session.getAttribute("username")).thenReturn("testuser"); + + userService.logout(request); + + verify(request).getSession(false); + verify(session).getAttribute("username"); + verify(session).invalidate(); + } + + @Test + @DisplayName("Log out without an active session") + void testLogoutWithoutSession() { + when(request.getSession(false)).thenReturn(null); + + userService.logout(request); + + verify(request).getSession(false); + verify(session, never()).invalidate(); + } +}