From 2e6daf58063073d6c1639e7fa312c077aec8323b Mon Sep 17 00:00:00 2001 From: wladimir-schneider Date: Tue, 3 Feb 2026 14:15:40 +0100 Subject: [PATCH 1/3] Java Classes added --- .../com/javarush/khmelov/cmd/Command.java | 37 ------------- .../com/javarush/khmelov/cmd/EditUser.java | 52 ------------------- .../com/javarush/khmelov/cmd/ListUser.java | 26 ---------- .../com/javarush/khmelov/cmd/StartPage.java | 6 --- .../com/javarush/khmelov/config/Winter.java | 29 ----------- .../khmelov/controller/FrontController.java | 43 --------------- .../khmelov/controller/HttpResolver.java | 41 --------------- .../com/javarush/khmelov/entity/Role.java | 5 -- .../com/javarush/khmelov/entity/User.java | 26 ---------- .../khmelov/repository/Repository.java | 19 ------- .../khmelov/repository/UserRepository.java | 50 ------------------ .../javarush/khmelov/service/UserService.java | 36 ------------- 12 files changed, 370 deletions(-) delete mode 100644 src/main/java/com/javarush/khmelov/cmd/Command.java delete mode 100644 src/main/java/com/javarush/khmelov/cmd/EditUser.java delete mode 100644 src/main/java/com/javarush/khmelov/cmd/ListUser.java delete mode 100644 src/main/java/com/javarush/khmelov/cmd/StartPage.java delete mode 100644 src/main/java/com/javarush/khmelov/config/Winter.java delete mode 100644 src/main/java/com/javarush/khmelov/controller/FrontController.java delete mode 100644 src/main/java/com/javarush/khmelov/controller/HttpResolver.java delete mode 100644 src/main/java/com/javarush/khmelov/entity/Role.java delete mode 100644 src/main/java/com/javarush/khmelov/entity/User.java delete mode 100644 src/main/java/com/javarush/khmelov/repository/Repository.java delete mode 100644 src/main/java/com/javarush/khmelov/repository/UserRepository.java delete mode 100644 src/main/java/com/javarush/khmelov/service/UserService.java 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); - } -} From 802c7c0134d44ee6e51f1c57f3fc3de7034bcac3 Mon Sep 17 00:00:00 2001 From: wladimir-schneider Date: Tue, 3 Feb 2026 14:18:37 +0100 Subject: [PATCH 2/3] Java Classes added --- .../com/javarush/wladimir/AdminServlet.java | 60 ++++++++++ .../javarush/wladimir/AppContextListener.java | 19 +++ .../javarush/wladimir/CreateQuestServlet.java | 112 ++++++++++++++++++ .../com/javarush/wladimir/GameServlet.java | 63 ++++++++++ .../com/javarush/wladimir/LoginServlet.java | 52 ++++++++ .../com/javarush/wladimir/LogoutServlet.java | 20 ++++ .../javarush/wladimir/PlayQuestServlet.java | 110 +++++++++++++++++ .../javarush/wladimir/RegisterServlet.java | 55 +++++++++ .../javarush/wladimir/UserQuestsServlet.java | 32 +++++ .../wladimir/dao/InMemoryUserDao.java | 62 ++++++++++ .../com/javarush/wladimir/model/Quest.java | 79 ++++++++++++ .../javarush/wladimir/model/QuestStep.java | 61 ++++++++++ .../com/javarush/wladimir/model/Role.java | 6 + .../com/javarush/wladimir/model/User.java | 50 ++++++++ 14 files changed, 781 insertions(+) create mode 100644 src/main/java/com/javarush/wladimir/AdminServlet.java create mode 100644 src/main/java/com/javarush/wladimir/AppContextListener.java create mode 100644 src/main/java/com/javarush/wladimir/CreateQuestServlet.java create mode 100644 src/main/java/com/javarush/wladimir/GameServlet.java create mode 100644 src/main/java/com/javarush/wladimir/LoginServlet.java create mode 100644 src/main/java/com/javarush/wladimir/LogoutServlet.java create mode 100644 src/main/java/com/javarush/wladimir/PlayQuestServlet.java create mode 100644 src/main/java/com/javarush/wladimir/RegisterServlet.java create mode 100644 src/main/java/com/javarush/wladimir/UserQuestsServlet.java create mode 100644 src/main/java/com/javarush/wladimir/dao/InMemoryUserDao.java create mode 100644 src/main/java/com/javarush/wladimir/model/Quest.java create mode 100644 src/main/java/com/javarush/wladimir/model/QuestStep.java create mode 100644 src/main/java/com/javarush/wladimir/model/Role.java create mode 100644 src/main/java/com/javarush/wladimir/model/User.java diff --git a/src/main/java/com/javarush/wladimir/AdminServlet.java b/src/main/java/com/javarush/wladimir/AdminServlet.java new file mode 100644 index 0000000..dc48f8b --- /dev/null +++ b/src/main/java/com/javarush/wladimir/AdminServlet.java @@ -0,0 +1,60 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.User; + +import java.io.IOException; +import java.util.List; +import java.util.logging.Logger; + +@WebServlet(name = "AdminServlet", value = "/admin") +public class AdminServlet extends HttpServlet { + private static final Logger log = Logger.getLogger(AdminServlet.class.getName()); + private InMemoryUserDao dao; + + @Override + public void init() { + dao = (InMemoryUserDao) getServletContext().getAttribute("userDao"); + } + + private boolean isAdmin(HttpServletRequest req) { + Object o = req.getSession().getAttribute("currentUser"); + if (o instanceof User u) { + log.info("Admin panel opened"); + return u.getRole() != null && u.getRole().name().equals("ADMIN"); + } + return false; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (!isAdmin(req)) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + log.warning("Unauthorized admin access attempt"); + return; + } + List users = dao.listAll(); + req.setAttribute("users", users); + req.getRequestDispatcher("/admin.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (!isAdmin(req)) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN); + log.warning("Unauthorized admin access attempt"); + return; + } + String action = req.getParameter("action"); + String username = req.getParameter("username"); + if ("delete".equals(action) && username != null) { + dao.delete(username); + } + resp.sendRedirect(req.getContextPath() + "/admin"); + } +} diff --git a/src/main/java/com/javarush/wladimir/AppContextListener.java b/src/main/java/com/javarush/wladimir/AppContextListener.java new file mode 100644 index 0000000..e504195 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/AppContextListener.java @@ -0,0 +1,19 @@ +package com.javarush.wladimir; + +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; +import com.javarush.wladimir.dao.InMemoryUserDao; + +import java.util.logging.Logger; + +@WebListener +public class AppContextListener implements ServletContextListener { + private static final Logger log = Logger.getLogger(AppContextListener.class.getName()); + @Override + public void contextInitialized(ServletContextEvent sce) { + log.info("Application is started"); + InMemoryUserDao dao = new InMemoryUserDao(); + sce.getServletContext().setAttribute("userDao", dao); + } +} diff --git a/src/main/java/com/javarush/wladimir/CreateQuestServlet.java b/src/main/java/com/javarush/wladimir/CreateQuestServlet.java new file mode 100644 index 0000000..18f1f7f --- /dev/null +++ b/src/main/java/com/javarush/wladimir/CreateQuestServlet.java @@ -0,0 +1,112 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.Quest; +import com.javarush.wladimir.model.QuestStep; +import com.javarush.wladimir.model.User; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +@WebServlet(name = "CreateQuestServlet", value = "/quests/create") +public class CreateQuestServlet extends HttpServlet { + private InMemoryUserDao dao; + private static final Logger log = Logger.getLogger(CreateQuestServlet.class.getName()); + + @Override + public void init() { + dao = (InMemoryUserDao) getServletContext().getAttribute("userDao"); + if (dao == null) { + log.severe("UserDao not found in ServletContext"); + } else { + log.info("CreateQuestServlet initialized"); + } + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + log.fine("POST /quests/create received"); + req.getRequestDispatcher("/createQuest.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + log.fine("POST /quests/create received"); + Object o = req.getSession().getAttribute("currentUser"); + if (!(o instanceof User u)) { + log.warning("Unauthorized quest creation attempt"); + resp.sendRedirect(req.getContextPath() + "/login"); + return; + } + String title = req.getParameter("title"); + if (title == null || title.isBlank()) { + log.warning("Quest creation failed: missing title (user=" + u.getUsername() + ")"); + req.setAttribute("error", "Title required"); + doGet(req, resp); + return; + } + + Quest q = new Quest(title, u.getUsername()); + + String questStructure = req.getParameter("questStructure"); + Map stepOrder = new HashMap<>(); + + if (questStructure != null && !questStructure.isBlank()) { + String[] stepLines = questStructure.split("\n"); + String startStepId = null; + int stepNum = 0; + + for (String line : stepLines) { + line = line.trim(); + if (line.isEmpty()) continue; + + String[] parts = line.split("\\|"); + if (parts.length < 2) continue; + + String stepId = parts[0].trim(); + String description = parts[1].trim(); + + if (stepNum == 0) { + startStepId = stepId; + } + stepNum++; + + QuestStep step = new QuestStep(stepId, description); + log.info("Creating quest: title=" + title + ", user=" + u.getUsername()); + + for (int i = 2; i < parts.length; i++) { + String choiceStr = parts[i].trim(); + if (choiceStr.contains("->")) { + String[] choiceParts = choiceStr.split("->"); + if (choiceParts.length == 2) { + String choiceText = choiceParts[0].trim(); + String nextStepId = choiceParts[1].trim(); + step.addChoice(new QuestStep.QuestChoice(choiceText, nextStepId)); + } + } else if (choiceStr.equals("END")) { + step.setEnding(true); + step.setEndingText(description); + q.addEnding(stepId); + } + } + + q.addStep(step); + } + + if (startStepId != null) { + q.setStartStepId(startStepId); + } + } + + dao.addQuest(u.getUsername(), q); + log.info("Quest created successfully: " + title); + resp.sendRedirect(req.getContextPath() + "/quests"); + } +} diff --git a/src/main/java/com/javarush/wladimir/GameServlet.java b/src/main/java/com/javarush/wladimir/GameServlet.java new file mode 100644 index 0000000..013893a --- /dev/null +++ b/src/main/java/com/javarush/wladimir/GameServlet.java @@ -0,0 +1,63 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.model.User; + +import java.io.IOException; + +@WebServlet(name = "GameServlet", value = "/game") +public class GameServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Object o = req.getSession().getAttribute("currentUser"); + if (!(o instanceof User)) { + resp.sendRedirect(req.getContextPath() + "/login"); + return; + } + User u = (User) o; + req.setAttribute("progress", u.getProgress()); + req.getRequestDispatcher("/game.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Object o = req.getSession().getAttribute("currentUser"); + if (!(o instanceof User)) { + resp.sendRedirect(req.getContextPath() + "/login"); + return; + } + User u = (User) o; + String action = req.getParameter("action"); + String progress = u.getProgress(); + if (action == null) { + resp.sendRedirect(req.getContextPath() + "/game"); + return; + } + if ("start".equals(action)) { + u.setProgress("zone"); + } else if ("explore_ruins".equals(action) && "zone".equals(progress)) { + u.setProgress("ruins"); + } else if ("enter_anomaly".equals(action) && "zone".equals(progress)) { + u.setProgress("anomaly"); + } else if ("search_artifacts".equals(action) && "ruins".equals(progress)) { + u.setProgress("artifact_found"); + } else if ("danger_zone".equals(action) && "ruins".equals(progress)) { + u.setProgress("mutant_encounter"); + } else if ("jump_anomaly".equals(action) && "anomaly".equals(progress)) { + u.setProgress("teleported"); + } else if ("escape_anomaly".equals(action) && "anomaly".equals(progress)) { + u.setProgress("escaped"); + } else if ("fight".equals(action) && "mutant_encounter".equals(progress)) { + u.setProgress("fought_mutant"); + } else if ("flee".equals(action) && "mutant_encounter".equals(progress)) { + u.setProgress("fled"); + } else if ("reset".equals(action)) { + u.setProgress("start"); + } + resp.sendRedirect(req.getContextPath() + "/game"); + } +} diff --git a/src/main/java/com/javarush/wladimir/LoginServlet.java b/src/main/java/com/javarush/wladimir/LoginServlet.java new file mode 100644 index 0000000..8a45d93 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/LoginServlet.java @@ -0,0 +1,52 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.User; + +import java.io.IOException; +import java.util.logging.Logger; + +@WebServlet(name = "LoginServlet", value = "/login") +public class LoginServlet extends HttpServlet { + private static final Logger log = Logger.getLogger(LoginServlet.class.getName()); + private InMemoryUserDao dao; + + @Override + public void init() { + dao = (InMemoryUserDao) getServletContext().getAttribute("userDao"); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getRequestDispatcher("/login.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + log.fine("POST /login received"); + String username = req.getParameter("username"); + String password = req.getParameter("password"); + log.info("Login attempt for user: " + username); + if (username == null || password == null) { + req.setAttribute("error", "Invalid credentials"); + log.warning("Invalid credentials for username: " + username); + doGet(req, resp); + return; + } + java.util.Optional u = dao.authenticate(username.trim(), password); + if (u.isEmpty()) { + req.setAttribute("error", "Invalid credentials"); + log.warning("Invalid credentials for username: " + username); + doGet(req, resp); + return; + } + log.info("Login successful for user: " + username); + req.getSession().setAttribute("currentUser", u.get()); + resp.sendRedirect(req.getContextPath() + "/game"); + } +} diff --git a/src/main/java/com/javarush/wladimir/LogoutServlet.java b/src/main/java/com/javarush/wladimir/LogoutServlet.java new file mode 100644 index 0000000..c774683 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/LogoutServlet.java @@ -0,0 +1,20 @@ +package com.javarush.wladimir; + +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.util.logging.Logger; + +@WebServlet(name = "LogoutServlet", value = "/logout") +public class LogoutServlet extends HttpServlet { + private static final Logger log = Logger.getLogger(LogoutServlet.class.getName()); + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + req.getSession().invalidate(); + log.info("User logged out"); + resp.sendRedirect(req.getContextPath() + "/"); + } +} diff --git a/src/main/java/com/javarush/wladimir/PlayQuestServlet.java b/src/main/java/com/javarush/wladimir/PlayQuestServlet.java new file mode 100644 index 0000000..f487bd6 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/PlayQuestServlet.java @@ -0,0 +1,110 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.Quest; +import com.javarush.wladimir.model.QuestStep; +import com.javarush.wladimir.model.User; + +import java.io.IOException; + +@WebServlet(name = "PlayQuestServlet", value = "/play-quest") +public class PlayQuestServlet extends HttpServlet { + private InMemoryUserDao dao; + + @Override + public void init() { + dao = (InMemoryUserDao) getServletContext().getAttribute("userDao"); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + User currentUser = (User) req.getSession().getAttribute("currentUser"); + if (currentUser == null) { + resp.sendRedirect(req.getContextPath() + "/login"); + return; + } + + String questId = req.getParameter("questId"); + String stepId = req.getParameter("stepId"); + + if (questId == null) { + resp.sendRedirect(req.getContextPath() + "/quests"); + return; + } + + Quest quest = dao.getQuest(questId); + if (quest == null) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Quest not found"); + return; + } + + if (stepId == null) { + stepId = quest.getStartStepId(); + if (stepId == null) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Quest has no start step"); + return; + } + } + + QuestStep currentStep = quest.getStep(stepId); + if (currentStep == null) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid step"); + return; + } + + req.setAttribute("quest", quest); + req.setAttribute("currentStep", currentStep); + req.setAttribute("stepId", stepId); + req.setAttribute("isEnding", currentStep.isEnding()); + + req.getRequestDispatcher("/playQuest.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + User currentUser = (User) req.getSession().getAttribute("currentUser"); + if (currentUser == null) { + resp.sendRedirect(req.getContextPath() + "/login"); + return; + } + + String questId = req.getParameter("questId"); + String choiceIndex = req.getParameter("choiceIndex"); + + if (questId == null || choiceIndex == null) { + resp.sendRedirect(req.getContextPath() + "/quests"); + return; + } + + Quest quest = dao.getQuest(questId); + if (quest == null) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Quest not found"); + return; + } + + String currentStepId = req.getParameter("currentStepId"); + QuestStep currentStep = quest.getStep(currentStepId); + if (currentStep == null) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid step"); + return; + } + + try { + int index = Integer.parseInt(choiceIndex); + if (index < 0 || index >= currentStep.getChoices().size()) { + resp.sendRedirect(req.getContextPath() + "/play-quest?questId=" + questId + "&stepId=" + currentStepId); + return; + } + + String nextStepId = currentStep.getChoices().get(index).getNextStepId(); + resp.sendRedirect(req.getContextPath() + "/play-quest?questId=" + questId + "&stepId=" + nextStepId); + } catch (NumberFormatException e) { + resp.sendRedirect(req.getContextPath() + "/quests"); + } + } +} diff --git a/src/main/java/com/javarush/wladimir/RegisterServlet.java b/src/main/java/com/javarush/wladimir/RegisterServlet.java new file mode 100644 index 0000000..789f8ae --- /dev/null +++ b/src/main/java/com/javarush/wladimir/RegisterServlet.java @@ -0,0 +1,55 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.User; + +import java.io.IOException; +import java.util.logging.Logger; + +@WebServlet(name = "RegisterServlet", value = "/register") +public class RegisterServlet extends HttpServlet { + private static final Logger log = Logger.getLogger(RegisterServlet.class.getName()); + private InMemoryUserDao dao; + + + @Override + public void init() { + dao = (InMemoryUserDao) getServletContext().getAttribute("userDao"); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getRequestDispatcher("/register.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + log.fine("POST /register received"); + String username = req.getParameter("username"); + String password = req.getParameter("password"); + if (username == null || password == null || username.isBlank()) { + req.setAttribute("error", "Invalid input"); + doGet(req, resp); + return; + } + log.info("Registering new user: " + username); + User u = new User(); + u.setUsername(username.trim()); + u.setPassword(password); + boolean ok = dao.add(u); + if (!ok) { + req.setAttribute("error", "User already exists"); + log.warning("Registration failed, username exists: " + username); + doGet(req, resp); + return; + } + req.getSession().setAttribute("currentUser", u); + log.info("User registered: " + username); + resp.sendRedirect(req.getContextPath() + "/game"); + } +} diff --git a/src/main/java/com/javarush/wladimir/UserQuestsServlet.java b/src/main/java/com/javarush/wladimir/UserQuestsServlet.java new file mode 100644 index 0000000..2381058 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/UserQuestsServlet.java @@ -0,0 +1,32 @@ +package com.javarush.wladimir; + +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 com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.User; + +import java.io.IOException; + +@WebServlet(name = "UserQuestsServlet", value = "/quests") +public class UserQuestsServlet extends HttpServlet { + private InMemoryUserDao dao; + + @Override + public void init() { + dao = (InMemoryUserDao) getServletContext().getAttribute("userDao"); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Object o = req.getSession().getAttribute("currentUser"); + if (!(o instanceof User u)) { + resp.sendRedirect(req.getContextPath() + "/login"); + return; + } + req.setAttribute("quests", dao.listQuests(u.getUsername())); + req.getRequestDispatcher("/userQuests.jsp").forward(req, resp); + } +} diff --git a/src/main/java/com/javarush/wladimir/dao/InMemoryUserDao.java b/src/main/java/com/javarush/wladimir/dao/InMemoryUserDao.java new file mode 100644 index 0000000..9101188 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/dao/InMemoryUserDao.java @@ -0,0 +1,62 @@ +package com.javarush.wladimir.dao; + +import com.javarush.wladimir.model.Quest; +import com.javarush.wladimir.model.Role; +import com.javarush.wladimir.model.User; + +import java.util.*; + +public class InMemoryUserDao { + private final Map users = Collections.synchronizedMap(new LinkedHashMap<>()); + private final Map> userQuests = Collections.synchronizedMap(new HashMap<>()); + private final Map allQuests = Collections.synchronizedMap(new HashMap<>()); + + public InMemoryUserDao() { + User admin = new User(); + admin.setUsername("admin"); + admin.setPassword("admin"); + admin.setRole(Role.ADMIN); + users.put(admin.getUsername(), admin); + } + + public Optional findByUsername(String username) { + return Optional.ofNullable(users.get(username)); + } + + public List listQuests(String username) { + return new ArrayList<>(userQuests.getOrDefault(username, List.of())); + } + + public boolean addQuest(String username, Quest q) { + if (username == null || q == null) return false; + synchronized (userQuests) { + userQuests.computeIfAbsent(username, k -> new ArrayList<>()).add(q); + allQuests.put(q.getId(), q); + return true; + } + } + + public Quest getQuest(String questId) { + return allQuests.get(questId); + } + public boolean add(User u) { + if (u == null || u.getUsername() == null) return false; + synchronized (users) { + if (users.containsKey(u.getUsername())) return false; + users.put(u.getUsername(), u); + return true; + } + } + + public boolean delete(String username) { + return users.remove(username) != null; + } + + public List listAll() { + return new ArrayList<>(users.values()); + } + + public Optional authenticate(String username, String password) { + return findByUsername(username).filter(u -> Objects.equals(u.getPassword(), password)); + } +} diff --git a/src/main/java/com/javarush/wladimir/model/Quest.java b/src/main/java/com/javarush/wladimir/model/Quest.java new file mode 100644 index 0000000..68cd039 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/model/Quest.java @@ -0,0 +1,79 @@ +package com.javarush.wladimir.model; + +import java.util.*; + +public class Quest { + private final String id = UUID.randomUUID().toString(); + private String title; + private String creator; + private Map steps = new HashMap<>(); + private String startStepId; + private List endingIds = new ArrayList<>(); + + public Quest() {} + + public Quest(String title, String creator) { + this.title = title; + this.creator = creator; + } + + // Backwards-compatible constructor: from linear list of step descriptions + public Quest(String title, List stepsList, String creator) { + this.title = title; + this.creator = creator; + this.steps = new HashMap<>(); + if (stepsList != null) { + int idx = 1; + for (String desc : stepsList) { + String id = "step" + idx++; + QuestStep step = new QuestStep(id, desc == null ? "" : desc); + this.steps.put(id, step); + } + if (!stepsList.isEmpty()) this.startStepId = "step1"; + } + } + + public String getId() { return id; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + + public String getCreator() { return creator; } + public void setCreator(String creator) { this.creator = creator; } + + public Map getSteps() { return steps; } + public void setSteps(Map steps) { this.steps = steps; } + + // Backwards-compatible setter: accept a List of step descriptions + public void setSteps(List stepsList) { + this.steps = new HashMap<>(); + if (stepsList != null) { + int idx = 1; + for (String desc : stepsList) { + String id = "step" + idx++; + QuestStep step = new QuestStep(id, desc == null ? "" : desc); + this.steps.put(id, step); + } + if (!stepsList.isEmpty()) this.startStepId = "step1"; + } + } + + public String getStartStepId() { return startStepId; } + public void setStartStepId(String startStepId) { this.startStepId = startStepId; } + + public List getEndingIds() { return endingIds; } + public void setEndingIds(List endingIds) { this.endingIds = endingIds; } + + public void addStep(QuestStep step) { + if (steps == null) steps = new HashMap<>(); + steps.put(step.getId(), step); + } + + public QuestStep getStep(String stepId) { + return steps != null ? steps.get(stepId) : null; + } + + public void addEnding(String endingId) { + if (endingIds == null) endingIds = new ArrayList<>(); + endingIds.add(endingId); + } +} diff --git a/src/main/java/com/javarush/wladimir/model/QuestStep.java b/src/main/java/com/javarush/wladimir/model/QuestStep.java new file mode 100644 index 0000000..a902024 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/model/QuestStep.java @@ -0,0 +1,61 @@ +package com.javarush.wladimir.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a single step in a branching quest. + * Each step has a description and optional choices leading to other steps. + */ +public class QuestStep { + private String id; + private String description; + private List choices = new ArrayList<>(); + private boolean isEnding = false; + private String endingText; + + public QuestStep() {} + + public QuestStep(String id, String description) { + this.id = id; + this.description = description; + } + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + + public List getChoices() { return choices; } + public void setChoices(List choices) { this.choices = choices; } + + public boolean isEnding() { return isEnding; } + public void setEnding(boolean ending) { isEnding = ending; } + + public String getEndingText() { return endingText; } + public void setEndingText(String endingText) { this.endingText = endingText; } + + public void addChoice(QuestChoice choice) { + if (choices == null) choices = new ArrayList<>(); + choices.add(choice); + } + + public static class QuestChoice { + private String text; + private String nextStepId; + + public QuestChoice() {} + + public QuestChoice(String text, String nextStepId) { + this.text = text; + this.nextStepId = nextStepId; + } + + public String getText() { return text; } + public void setText(String text) { this.text = text; } + + public String getNextStepId() { return nextStepId; } + public void setNextStepId(String nextStepId) { this.nextStepId = nextStepId; } + } +} diff --git a/src/main/java/com/javarush/wladimir/model/Role.java b/src/main/java/com/javarush/wladimir/model/Role.java new file mode 100644 index 0000000..09e2d98 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/model/Role.java @@ -0,0 +1,6 @@ +package com.javarush.wladimir.model; + +public enum Role { + USER, + ADMIN +} diff --git a/src/main/java/com/javarush/wladimir/model/User.java b/src/main/java/com/javarush/wladimir/model/User.java new file mode 100644 index 0000000..8c04528 --- /dev/null +++ b/src/main/java/com/javarush/wladimir/model/User.java @@ -0,0 +1,50 @@ +package com.javarush.wladimir.model; + +import java.util.UUID; + +public class User { + private final String id = UUID.randomUUID().toString(); + private String username; + private String password; + private Role role = Role.USER; + private String progress = "start"; + + public User() { + } + + public String getId() { + return id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + + public String getProgress() { + return progress; + } + + public void setProgress(String progress) { + this.progress = progress; + } +} From 895744c91c8a67b8a2bf37b350237b952f7ca2d5 Mon Sep 17 00:00:00 2001 From: wladimir-schneider Date: Tue, 3 Feb 2026 14:40:30 +0100 Subject: [PATCH 3/3] Tests and jsps added and fixed --- pom.xml | 77 +++---- src/main/resources/messages.properties | 75 +++++++ src/main/resources/messages_de.properties | 72 +++++++ src/main/resources/messages_ru.properties | 74 +++++++ src/main/webapp/admin.jsp | 50 +++++ src/main/webapp/createQuest.jsp | 63 ++++++ src/main/webapp/game.jsp | 65 ++++++ src/main/webapp/index.jsp | 47 +++++ src/main/webapp/login.jsp | 39 ++++ src/main/webapp/playQuest.jsp | 86 ++++++++ src/main/webapp/register.jsp | 39 ++++ src/main/webapp/userQuests.jsp | 68 ++++++ .../pantera/textquest/AdminServletTest.java | 138 +++++++++++++ .../pantera/textquest/GameServletTest.java | 195 ++++++++++++++++++ .../textquest/RegisterServletTest.java | 126 +++++++++++ .../dao/InMemoryUserDaoExtendedTest.java | 146 +++++++++++++ .../textquest/dao/InMemoryUserDaoTest.java | 23 +++ .../pantera/textquest/model/QuestTest.java | 45 ++++ .../pantera/textquest/model/UserTest.java | 48 +++++ 19 files changed, 1432 insertions(+), 44 deletions(-) create mode 100644 src/main/resources/messages.properties create mode 100644 src/main/resources/messages_de.properties create mode 100644 src/main/resources/messages_ru.properties create mode 100644 src/main/webapp/admin.jsp create mode 100644 src/main/webapp/createQuest.jsp create mode 100644 src/main/webapp/game.jsp create mode 100644 src/main/webapp/index.jsp create mode 100644 src/main/webapp/login.jsp create mode 100644 src/main/webapp/playQuest.jsp create mode 100644 src/main/webapp/register.jsp create mode 100644 src/main/webapp/userQuests.jsp create mode 100644 src/test/java/pantera/textquest/AdminServletTest.java create mode 100644 src/test/java/pantera/textquest/GameServletTest.java create mode 100644 src/test/java/pantera/textquest/RegisterServletTest.java create mode 100644 src/test/java/pantera/textquest/dao/InMemoryUserDaoExtendedTest.java create mode 100644 src/test/java/pantera/textquest/dao/InMemoryUserDaoTest.java create mode 100644 src/test/java/pantera/textquest/model/QuestTest.java create mode 100644 src/test/java/pantera/textquest/model/UserTest.java diff --git a/pom.xml b/pom.xml index 78ee59d..20662cc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,91 +4,80 @@ 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 + Pantera + text-quest 1.0-SNAPSHOT - ProjectLedzeppelin + text-quest war UTF-8 21 21 - 5.10.2 + 5.13.2 - - org.springframework.boot - spring-boot-starter-parent - 3.3.5 - - - - - - org.springframework.boot - spring-boot-dependencies - pom - import - 3.3.5 - - - - jakarta.servlet jakarta.servlet-api + 6.1.0 provided - - jakarta.servlet.jsp.jstl - jakarta.servlet.jsp.jstl-api - org.glassfish.web jakarta.servlet.jsp.jstl - - - - org.projectlombok - lombok - provided + 2.0.0 org.junit.jupiter junit-jupiter-api + ${junit.version} test org.junit.jupiter junit-jupiter-engine + ${junit.version} + test + + + org.mockito + mockito-core + 5.2.0 + test + + + org.mockito + mockito-junit-jupiter + 5.2.0 test - org.apache.maven.plugins - maven-war-plugin - 3.4.0 + maven-compiler-plugin + 3.11.0 + + 21 + org.apache.maven.plugins - maven-compiler-plugin + maven-surefire-plugin + 3.0.0-M9 - - - org.projectlombok - lombok - 1.18.34 - - + false + + org.apache.maven.plugins + maven-war-plugin + 3.4.0 + - \ No newline at end of file diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000..692900b --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,75 @@ +# ===== General ===== +app.title=Text Quest +button.create=Create +button.cancel=Cancel +button.play=Play +button.backHome=Back to Home +button.login=Login +button.register=Register +button.logout=Logout +link.home=Home + + +# ===== Index ===== +index.welcome=Welcome to Text Quest +index.start=Start Your Adventure +index.loginHint=Login to create and play quests +index.hello=Hello + +# ===== Login ===== +login.title=Login +login.username=Username +login.password=Password +login.submit=Login +login.noAccount=No account yet? +login.registerLink=Register here + +# ===== Register ===== +register.title=Register +register.submit=Create Account +register.haveAccount=Already have an account? +register.loginLink=Login here + +# ===== Create Quest ===== +quest.create.title=Create a New Quest +quest.create.structure=Quest Structure (with branching) +quest.create.format=Format: +quest.create.format.desc=stepId|description|choice1->next_step|choice2->next_step +quest.create.important=Important: +quest.create.important.desc=Each line is one step. Use | as separator. Use -> for branching. +quest.create.ending=Ending marker: +quest.create.ending.desc=Use |END to mark an ending. +quest.create.example=Example: +quest.create.ttitle=Title +quest.create.new=Create Quest + +# ===== My Quests ===== +quests.title=My Quests +quests.createNew=Create New Quest +quests.createdBy=Created by +quests.steps=Steps +quests.description=Quests with branching paths and player choices +quests.none=You haven't created any quests yet. +quests.first=Create your first quest! + +# ===== Play Quest ===== +play.choose=Choose your action +play.end=The End +play.restart=Restart Quest + +# ===== Game / Admin ===== +admin.title=Admin Panel +admin.users=Users +admin.quests=Quests +admin.action=Action +admin.role=Role +admin.uname=Username +admin.del=Delete +button.role.admin=Admin + +# ===== Play Quest ===== +play.quest.playq=Play Quest +play.quest.backq=Back to Quests +play.quest.message=This step has no choices available. +play.quest.back=Back +play.quest.comp=Quest Completed! \ No newline at end of file diff --git a/src/main/resources/messages_de.properties b/src/main/resources/messages_de.properties new file mode 100644 index 0000000..927472f --- /dev/null +++ b/src/main/resources/messages_de.properties @@ -0,0 +1,72 @@ +# ===== Allgemein ===== +app.title=Text Quest +button.create=Erstellen +button.cancel=Abbrechen +button.play=Spielen +button.backHome=Zurück zur Startseite +button.login=Anmelden +button.register=Registrieren +button.logout=Abmelden +link.home=Startseite + +# ===== Index ===== +index.welcome=Willkommen bei Text Quest +index.start=Starte dein Abenteuer +index.loginHint=Melde dich an, um Quests zu erstellen und zu spielen +index.hello=Hallo + +# ===== Anmeldung ===== +login.title=Anmeldung +login.username=Benutzername +login.password=Passwort +login.submit=Anmelden +login.noAccount=Noch kein Konto? +login.registerLink=Hier registrieren + +# ===== Registrierung ===== +register.title=Registrierung +register.submit=Konto erstellen +register.haveAccount=Bereits ein Konto? +register.loginLink=Hier anmelden + +# ===== Quest erstellen ===== +quest.create.title=Neue Quest erstellen +quest.create.structure=Quest-Struktur (mit Verzweigungen) +quest.create.format=Format: +quest.create.format.desc=stepId|Beschreibung|Wahl1->nächster_Schritt|Wahl2->nächster_Schritt +quest.create.important=Wichtig: +quest.create.important.desc=Jede Zeile ist ein Schritt. Verwende | als Trennzeichen. Verwende -> für Verzweigungen. +quest.create.ending=Endmarkierung: +quest.create.ending.desc=Verwende |END, um ein Ende zu markieren. +quest.create.example=Beispiel: + +# ===== Meine Quests ===== +quests.title=Meine Quests +quests.createNew=Neue Quest erstellen +quests.createdBy=Erstellt von +quests.steps=Schritte +quests.description=Quests mit Verzweigungen und Spielerentscheidungen +quests.none=Du hast noch keine Quests erstellt. +quests.first=Erstelle deine erste Quest! + +# ===== Quest spielen ===== +play.choose=Wähle deine Aktion +play.end=Das Ende +play.restart=Quest neu starten + +# ===== Spiel / Admin ===== +admin.title=Admin-Bereich +admin.users=Benutzer +admin.quests=Quests +admin.action=Aktion +admin.role=Rolle +admin.uname=Benutzername +admin.del=Löschen +button.role.admin=Admin + +# ===== Play Quest ===== +play.quest.playq=Quest spielen +play.quest.backq=Zurück zu den Quests +play.quest.message=Bei diesem Schritt gibt es keine Auswahlmöglichkeiten. +play.quest.back=Zurück +play.quest.comp=Mission abgeschlossen! \ No newline at end of file diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties new file mode 100644 index 0000000..ef41ce1 --- /dev/null +++ b/src/main/resources/messages_ru.properties @@ -0,0 +1,74 @@ +# ===== General ===== +app.title=????? ????? +button.create=??????? +button.cancel=?????? +button.play=?????? +button.backHome=?? ??????? +button.login=????? +button.register=??????????? +button.logout=????? +link.home=??????? + +# ===== Index ===== +index.welcome=????? ?????????? ? ????? ????? +index.start=??????? ???? ??????????? +index.loginHint=???????, ????? ????????? ? ?????? ? ?????? +index.hello=?????? + +# ===== Login ===== +login.title=???? +login.username=??? ???????????? +login.password=?????? +login.submit=????? +login.noAccount=??? ??? ????????? +login.registerLink=????????????????? ????? + +# ===== Register ===== +register.title=??????????? +register.submit=??????? ??????? +register.haveAccount=??? ???? ???????? +register.loginLink=????? ????? + +# ===== Create Quest ===== +quest.create.title=??????? ????? ????? +quest.create.structure=????????? ?????? (? ??????????) +quest.create.format=??????: +quest.create.format.desc=stepId|????????|?????1->?????????_???|?????2->?????????_??? +quest.create.important=?????: +quest.create.important.desc=?????? ?????? - ???? ???. ??????????? | ??? ???????????. ??????????? -> ??? ?????????. +quest.create.ending=?????? ?????????: +quest.create.ending.desc=??????????? |END, ????? ???????? ?????. +quest.create.example=??????: +quest.create.ttitle=???????? +quest.create.new=??????? ????? + +# ===== My Quests ===== +quests.title=??? ?????? +quests.createNew=??????? ????? ????? +quests.createdBy=??????? +quests.steps=???? +quests.description=?????? ? ??????????? ? ???????? ?????? +quests.none=?? ??? ?? ??????? ?? ?????? ??????. +quests.first=???????? ???? ?????? ?????! + +# ===== Play Quest ===== +play.choose=???????? ???????? +play.end=????? +play.restart=?????? ????? ?????? + +# ===== Game / Admin ===== +admin.title=?????? ?????????????? +admin.users=???????????? +admin.quests=?????? +admin.action=???????? +admin.role=???? +admin.uname=??? ???????????? +admin.del=??????? +button.role.admin=????????????? + +# ===== Play Quest ===== +play.quest.playq=?????? ? ????? +play.quest.backq=????? ? ??????? +play.quest.message=?? ???? ???? ??? ????????? ???????. +play.quest.back=????? +play.quest.comp=????? ????????! \ No newline at end of file diff --git a/src/main/webapp/admin.jsp b/src/main/webapp/admin.jsp new file mode 100644 index 0000000..505f856 --- /dev/null +++ b/src/main/webapp/admin.jsp @@ -0,0 +1,50 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + java.util.List users = (java.util.List) request.getAttribute("users"); +%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + + <fmt:message key="admin.title"/> + + + +

-

+ + + + + <% if (users != null) { + for (Object _o : users) { + com.javarush.wladimir.model.User u = (com.javarush.wladimir.model.User)_o; + %> + + + + + + <% } + } + %> + +
<%= u.getUsername() %><%= (u.getRole() != null ? u.getRole().name() : "") %> + <% if (!"admin".equals(u.getUsername())) { %> +
+ + + +
+ <% } %> +
+ + diff --git a/src/main/webapp/createQuest.jsp b/src/main/webapp/createQuest.jsp new file mode 100644 index 0000000..400ea66 --- /dev/null +++ b/src/main/webapp/createQuest.jsp @@ -0,0 +1,63 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + String error = (String) request.getAttribute("error"); +%> + +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + + <fmt:message key="quest.create.new"/> + + + + +
+

+ <% if (error != null) { %> +
<%= error %>
+ <% } %> +
+
+ + +
+
+ + +
+
+ :
+ +
+
+
+ start|You are in a dark cave|Go north->north|Go south->south
+ north|You see a dragon!|Fight->fight|Run away->start
+ fight|You win!|END
+ south|You find a treasure!|END +
+
+ + +
+
+ + diff --git a/src/main/webapp/game.jsp b/src/main/webapp/game.jsp new file mode 100644 index 0000000..0ab9fe3 --- /dev/null +++ b/src/main/webapp/game.jsp @@ -0,0 +1,65 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + com.javarush.wladimir.model.User u = (com.javarush.wladimir.model.User) session.getAttribute("currentUser"); + String progress = (String) request.getAttribute("progress"); + String message = ""; + if (progress == null) progress = (u != null ? u.getProgress() : "start"); + if ("start".equals(progress)) { + message = "Welcome, Stalker. You awaken in the Zone..."; + } else if ("zone".equals(progress)) { + message = "You stand in the heart of the Zone. Ruins stretch before you. To the east, a shimmering anomaly pulses with strange energy. What do you do?"; + } else if ("ruins".equals(progress)) { + message = "You explore the decaying ruins. Strange artifacts litter the ground. You hear growls in the distance. The air feels heavy."; + } else if ("anomaly".equals(progress)) { + message = "You approach the anomaly. The air crackles. Gravity seems bent here. Artifacts float nearby. One wrong move could be your last."; + } else if ("artifact_found".equals(progress)) { + message = "You carefully retrieve a glowing artifact! It hums with energy. This will fetch a good price. Quest Complete!"; + } else if ("mutant_encounter".equals(progress)) { + message = "A growl freezes your blood. A mutant emerges from the shadows! Fight or flee?"; + } else if ("teleported".equals(progress)) { + message = "You jump into the anomaly! Reality distorts. You are teleported far away. Disoriented but alive. Quest Complete!"; + } else if ("escaped".equals(progress)) { + message = "You run from the anomaly zone. Your heart pounds. You made it out. Safe, but empty-handed. Quest Complete."; + } else if ("fought_mutant".equals(progress)) { + message = "You fought the mutant! You survived, barely. The Zone respects strength. Quest Complete!"; + } else if ("fled".equals(progress)) { + message = "You fled the mutant and escaped the ruins. Alive is what matters. Quest Complete."; + } else { + message = "You wander the Zone, unsure of your path."; + } +%> + + + + + Game - Text Quest + + + +

Text Quest

+

<%= message %>

+
+
+ <% if ("start".equals(progress)) { %> + + <% } else if ("zone".equals(progress)) { %> + + + <% } else if ("ruins".equals(progress)) { %> + + + <% } else if ("anomaly".equals(progress)) { %> + + + <% } else if ("mutant_encounter".equals(progress)) { %> + + + <% } else if ("artifact_found".equals(progress) || "teleported".equals(progress) || "escaped".equals(progress) || "fought_mutant".equals(progress) || "fled".equals(progress)) { %> + Quest Completed + <% } %> + +
+
+ Home + + diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..0c5222c --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,47 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + Object _user = session.getAttribute("currentUser"); +%> + +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + + Text Quest + + + +

+ +
+ + <% if (_user != null) { + com.javarush.wladimir.model.User u = (com.javarush.wladimir.model.User)_user; + %> +

, <%= u.getUsername() %>!

+ + + + <% if (u.getRole() != null && "ADMIN".equals(u.getRole().name())) { %> + + <% } %> + <% } else { %> + + + <% } %> +
+ + \ No newline at end of file diff --git a/src/main/webapp/login.jsp b/src/main/webapp/login.jsp new file mode 100644 index 0000000..735fee7 --- /dev/null +++ b/src/main/webapp/login.jsp @@ -0,0 +1,39 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + String error = (String) request.getAttribute("error"); +%> + +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + <fmt:message key="login.title"/> - <fmt:message key="app.title"/> + + + +

+ <% if (error != null) { %> +
<%= error %>
+ <% } %> +
+
+ + +
+
+ + +
+ + +
+ + diff --git a/src/main/webapp/playQuest.jsp b/src/main/webapp/playQuest.jsp new file mode 100644 index 0000000..3980f8c --- /dev/null +++ b/src/main/webapp/playQuest.jsp @@ -0,0 +1,86 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> +<%@ page import="com.javarush.wladimir.model.Quest" %> +<%@ page import="com.javarush.wladimir.model.QuestStep" %> +<%@ page import="com.javarush.wladimir.model.User" %> + + +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + + + <fmt:message key="play.quest.playq"/> + + + + +<% + Quest quest = (Quest) request.getAttribute("quest"); + QuestStep currentStep = (QuestStep) request.getAttribute("currentStep"); + String stepId = (String) request.getAttribute("stepId"); + Boolean isEnding = (Boolean) request.getAttribute("isEnding"); + User user = (User) session.getAttribute("currentUser"); +%> + +
+

<%= quest.getTitle() %>

+ +
+

<%= currentStep.getDescription() %>

+
+ + <% if (isEnding != null && isEnding) { %> +
+
+

<%= currentStep.getEndingText() %>

+
+ + <% } else if (currentStep.getChoices() != null && !currentStep.getChoices().isEmpty()) { %> +
+ + + + <% for (int i = 0; i < currentStep.getChoices().size(); i++) { + QuestStep.QuestChoice choice = currentStep.getChoices().get(i); + %> +
+ +
+ <% } %> +
+ <% } else { %> +
+ +
+ + <% } %> +
+ + + + diff --git a/src/main/webapp/register.jsp b/src/main/webapp/register.jsp new file mode 100644 index 0000000..715bca1 --- /dev/null +++ b/src/main/webapp/register.jsp @@ -0,0 +1,39 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + String error = (String) request.getAttribute("error"); +%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + + <fmt:message key="register.title"/> - <fmt:message key="app.title"/> + + + +

+ <% if (error != null) { %> +
<%= error %>
+ <% } %> +
+
+ + +
+
+ + +
+ + +
+ + diff --git a/src/main/webapp/userQuests.jsp b/src/main/webapp/userQuests.jsp new file mode 100644 index 0000000..7d5fe5e --- /dev/null +++ b/src/main/webapp/userQuests.jsp @@ -0,0 +1,68 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + java.util.List quests = (java.util.List) request.getAttribute("quests"); +%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + + + + + <fmt:message key="quests.title"/> + + + + +
+

+ + + + <% if (quests != null && !quests.isEmpty()) { %> + <% for (Object _o : quests) { + com.javarush.wladimir.model.Quest q = (com.javarush.wladimir.model.Quest)_o; + %> +
+
<%= q.getTitle() %>
+
+ : <%= q.getCreator() %>
+ : <%= q.getSteps() != null ? q.getSteps().size() : 0 %> +
+
+ +
+ +
+ <% } %> + <% } else { %> +
+ + +
+ <% } %> + +
+ ↠+
+
+ + diff --git a/src/test/java/pantera/textquest/AdminServletTest.java b/src/test/java/pantera/textquest/AdminServletTest.java new file mode 100644 index 0000000..7f24c91 --- /dev/null +++ b/src/test/java/pantera/textquest/AdminServletTest.java @@ -0,0 +1,138 @@ +package pantera.textquest; + +import com.javarush.wladimir.AdminServlet; +import com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.Role; +import com.javarush.wladimir.model.User; +import jakarta.servlet.*; +import jakarta.servlet.http.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class AdminServletTest { + + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + @Mock + private HttpSession session; + @Mock + private ServletContext servletContext; + @Mock + private ServletConfig servletConfig; + @Mock + private RequestDispatcher dispatcher; + + private AdminServlet servlet; + private InMemoryUserDao dao; + + @BeforeEach + void setUp() throws ServletException { + servlet = new AdminServlet(); + dao = new InMemoryUserDao(); + + when(servletConfig.getServletContext()).thenReturn(servletContext); + when(servletContext.getAttribute("userDao")).thenReturn(dao); + when(request.getSession()).thenReturn(session); + when(request.getContextPath()).thenReturn("/text-quest"); + when(request.getRequestDispatcher("/admin.jsp")).thenReturn(dispatcher); + + servlet.init(servletConfig); + } + + + @Test + void doGet_adminAccess_allowed() throws ServletException, IOException { + User admin = dao.findByUsername("admin").get(); + when(request.getMethod()).thenReturn("GET"); + when(session.getAttribute("currentUser")).thenReturn(admin); + + servlet.service(request, response); + + verify(dispatcher).forward(request, response); + } + + @Test + void doGet_nonAdmin_denied() throws ServletException, IOException { + User user = new User(); + user.setUsername("user"); + user.setRole(Role.USER); + + when(request.getMethod()).thenReturn("GET"); + when(session.getAttribute("currentUser")).thenReturn(user); + + servlet.service(request, response); + + verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); + } + + @Test + void doGet_notLoggedIn_denied() throws ServletException, IOException { + when(request.getMethod()).thenReturn("GET"); + when(session.getAttribute("currentUser")).thenReturn(null); + + servlet.service(request, response); + + verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); + } + + + @Test + void doPost_adminDeletesUser() throws ServletException, IOException { + User admin = dao.findByUsername("admin").get(); + User toDelete = new User(); + toDelete.setUsername("todelete"); + dao.add(toDelete); + + when(request.getMethod()).thenReturn("POST"); + when(session.getAttribute("currentUser")).thenReturn(admin); + when(request.getParameter("action")).thenReturn("delete"); + when(request.getParameter("username")).thenReturn("todelete"); + + servlet.service(request, response); + + assertFalse(dao.findByUsername("todelete").isPresent()); + verify(response).sendRedirect("/text-quest/admin"); + } + + @Test + void doPost_nonAdmin_denied() throws ServletException, IOException { + User user = new User(); + user.setUsername("user"); + user.setRole(Role.USER); + + when(request.getMethod()).thenReturn("POST"); + when(session.getAttribute("currentUser")).thenReturn(user); + when(request.getParameter("action")).thenReturn("delete"); + + servlet.service(request, response); + + verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); + } + + @Test + void doPost_invalidAction_ignored() throws ServletException, IOException { + User admin = dao.findByUsername("admin").get(); + User target = new User(); + target.setUsername("target"); + dao.add(target); + + when(request.getMethod()).thenReturn("POST"); + when(session.getAttribute("currentUser")).thenReturn(admin); + when(request.getParameter("action")).thenReturn("invalid"); + + servlet.service(request, response); + + assertTrue(dao.findByUsername("target").isPresent()); + } +} diff --git a/src/test/java/pantera/textquest/GameServletTest.java b/src/test/java/pantera/textquest/GameServletTest.java new file mode 100644 index 0000000..50da9e6 --- /dev/null +++ b/src/test/java/pantera/textquest/GameServletTest.java @@ -0,0 +1,195 @@ +package pantera.textquest; + +import com.javarush.wladimir.GameServlet; +import com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.User; +import jakarta.servlet.*; +import jakarta.servlet.http.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class GameServletTest { + + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + @Mock + private HttpSession session; + @Mock + private ServletContext servletContext; + @Mock + private ServletConfig servletConfig; + @Mock + private RequestDispatcher dispatcher; + + private GameServlet servlet; + private InMemoryUserDao dao; + + @BeforeEach + void setUp() throws ServletException { + servlet = new GameServlet(); + dao = new InMemoryUserDao(); + + when(servletConfig.getServletContext()).thenReturn(servletContext); + when(servletContext.getAttribute("userDao")).thenReturn(dao); + when(request.getRequestDispatcher("/game.jsp")).thenReturn(dispatcher); + + servlet.init(servletConfig); + } + + + @Test + void doGet_withoutLogin_redirectsToLogin() throws ServletException, IOException { + when(request.getMethod()).thenReturn("GET"); + when(request.getSession()).thenReturn(session); + when(request.getContextPath()).thenReturn("/text-quest"); + when(session.getAttribute("currentUser")).thenReturn(null); + + servlet.service(request, response); + + verify(response).sendRedirect("/text-quest/login"); + } + + @Test + void doGet_withLogin_forwardsToGameJsp() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("start"); + + when(request.getMethod()).thenReturn("GET"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("currentUser")).thenReturn(u); + + servlet.service(request, response); + + verify(dispatcher).forward(request, response); + } + + + @Test + void doPost_startAction() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("start"); + dao.add(u); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(request.getContextPath()).thenReturn("/text-quest"); + when(session.getAttribute("currentUser")).thenReturn(u); + when(request.getParameter("action")).thenReturn("start"); + + servlet.service(request, response); + + assertEquals("zone", u.getProgress()); + verify(response).sendRedirect("/text-quest/game"); + } + + @Test + void doPost_exploreRuinsAction() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("zone"); + dao.add(u); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("currentUser")).thenReturn(u); + when(request.getParameter("action")).thenReturn("explore_ruins"); + + servlet.service(request, response); + + assertEquals("ruins", u.getProgress()); + } + + @Test + void doPost_enterAnomalyAction() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("zone"); + dao.add(u); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("currentUser")).thenReturn(u); + when(request.getParameter("action")).thenReturn("enter_anomaly"); + + servlet.service(request, response); + + assertEquals("anomaly", u.getProgress()); + } + + @Test + void doPost_searchArtifactsAction() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("ruins"); + dao.add(u); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("currentUser")).thenReturn(u); + when(request.getParameter("action")).thenReturn("search_artifacts"); + + servlet.service(request, response); + + assertEquals("artifact_found", u.getProgress()); + } + + @Test + void doPost_resetAction() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("treasure"); + dao.add(u); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("currentUser")).thenReturn(u); + when(request.getParameter("action")).thenReturn("reset"); + + servlet.service(request, response); + + assertEquals("start", u.getProgress()); + } + + @Test + void doPost_withoutLogin_redirectsToLogin() throws ServletException, IOException { + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(request.getContextPath()).thenReturn("/text-quest"); + when(session.getAttribute("currentUser")).thenReturn(null); + when(request.getParameter("action")).thenReturn("start"); + + servlet.service(request, response); + + verify(response).sendRedirect("/text-quest/login"); + } + + @Test + void doPost_invalidAction_doesNothing() throws ServletException, IOException { + User u = new User(); + u.setUsername("player"); + u.setProgress("zone"); + dao.add(u); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(session.getAttribute("currentUser")).thenReturn(u); + when(request.getParameter("action")).thenReturn("invalid_action"); + + servlet.service(request, response); + + assertEquals("zone", u.getProgress()); + } +} diff --git a/src/test/java/pantera/textquest/RegisterServletTest.java b/src/test/java/pantera/textquest/RegisterServletTest.java new file mode 100644 index 0000000..5fccf2b --- /dev/null +++ b/src/test/java/pantera/textquest/RegisterServletTest.java @@ -0,0 +1,126 @@ +package pantera.textquest; + +import com.javarush.wladimir.RegisterServlet; +import com.javarush.wladimir.dao.InMemoryUserDao; +import com.javarush.wladimir.model.User; +import jakarta.servlet.*; +import jakarta.servlet.http.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class RegisterServletTest { + + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + @Mock + private HttpSession session; + @Mock + private ServletContext servletContext; + @Mock + private ServletConfig servletConfig; + @Mock + private RequestDispatcher dispatcher; + + private RegisterServlet servlet; + private InMemoryUserDao dao; + + @BeforeEach + void setUp() throws ServletException { + servlet = new RegisterServlet(); + dao = new InMemoryUserDao(); + + when(servletConfig.getServletContext()).thenReturn(servletContext); + when(servletContext.getAttribute("userDao")).thenReturn(dao); + when(request.getRequestDispatcher("/register.jsp")).thenReturn(dispatcher); + + servlet.init(servletConfig); + } + + + @Test + void doGet_forwardsToRegisterJsp() throws ServletException, IOException { + when(request.getMethod()).thenReturn("GET"); + + servlet.service(request, response); + + verify(dispatcher).forward(request, response); + } + + + @Test + void doPost_successfulRegistration() throws ServletException, IOException { + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(request.getContextPath()).thenReturn("/text-quest"); + when(request.getParameter("username")).thenReturn("newuser"); + when(request.getParameter("password")).thenReturn("password123"); + + servlet.service(request, response); + + assertTrue(dao.findByUsername("newuser").isPresent()); + + User user = dao.findByUsername("newuser").get(); + assertEquals("newuser", user.getUsername()); + assertEquals("password123", user.getPassword()); + + verify(session).setAttribute(eq("currentUser"), any(User.class)); + verify(response).sendRedirect("/text-quest/game"); + } + + + @Test + void doPost_duplicateUsername_fails() throws ServletException, IOException { + User existing = new User(); + existing.setUsername("existing"); + existing.setPassword("pass"); + dao.add(existing); + + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(request.getParameter("username")).thenReturn("existing"); + when(request.getParameter("password")).thenReturn("newpass"); + + servlet.service(request, response); + + verify(request).setAttribute(eq("error"), contains("already exists")); + verify(dispatcher).forward(request, response); + } + + + @Test + void doPost_emptyUsername_fails() throws ServletException, IOException { + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(request.getParameter("username")).thenReturn(""); + when(request.getParameter("password")).thenReturn("password"); + + servlet.service(request, response); + + verify(request).setAttribute(eq("error"), contains("Invalid")); + verify(dispatcher).forward(request, response); + } + + @Test + void doPost_nullUsername_fails() throws ServletException, IOException { + when(request.getMethod()).thenReturn("POST"); + when(request.getSession()).thenReturn(session); + when(request.getParameter("username")).thenReturn(null); + when(request.getParameter("password")).thenReturn("password"); + + servlet.service(request, response); + + verify(request).setAttribute(eq("error"), contains("Invalid")); + verify(dispatcher).forward(request, response); + } +} diff --git a/src/test/java/pantera/textquest/dao/InMemoryUserDaoExtendedTest.java b/src/test/java/pantera/textquest/dao/InMemoryUserDaoExtendedTest.java new file mode 100644 index 0000000..367c9e2 --- /dev/null +++ b/src/test/java/pantera/textquest/dao/InMemoryUserDaoExtendedTest.java @@ -0,0 +1,146 @@ +package pantera.textquest.dao; + +import com.javarush.wladimir.dao.InMemoryUserDao; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import com.javarush.wladimir.model.Quest; +import com.javarush.wladimir.model.Role; +import com.javarush.wladimir.model.User; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class InMemoryUserDaoExtendedTest { + private InMemoryUserDao dao; + + @BeforeEach + public void setup() { + dao = new InMemoryUserDao(); + } + + @Test + public void testDefaultAdminExists() { + assertTrue(dao.findByUsername("admin").isPresent()); + User admin = dao.findByUsername("admin").get(); + assertEquals(Role.ADMIN, admin.getRole()); + } + + @Test + public void testAddMultipleUsers() { + User u1 = new User(); + u1.setUsername("user1"); + u1.setPassword("pass1"); + + User u2 = new User(); + u2.setUsername("user2"); + u2.setPassword("pass2"); + + assertTrue(dao.add(u1)); + assertTrue(dao.add(u2)); + assertEquals(3, dao.listAll().size()); // admin + 2 users + } + + @Test + public void testAddDuplicateUserFails() { + User u1 = new User(); + u1.setUsername("duplicate"); + u1.setPassword("pass"); + + assertTrue(dao.add(u1)); + + User u2 = new User(); + u2.setUsername("duplicate"); + u2.setPassword("other"); + + assertFalse(dao.add(u2)); + } + + @Test + public void testAddNullUserFails() { + assertFalse(dao.add(null)); + } + + @Test + public void testDeleteUser() { + User u = new User(); + u.setUsername("todelete"); + u.setPassword("pass"); + dao.add(u); + + assertTrue(dao.findByUsername("todelete").isPresent()); + assertTrue(dao.delete("todelete")); + assertFalse(dao.findByUsername("todelete").isPresent()); + } + + @Test + public void testDeleteNonexistentUserFails() { + assertFalse(dao.delete("nonexistent")); + } + + @Test + public void testAuthenticateSuccess() { + User u = new User(); + u.setUsername("user"); + u.setPassword("secret"); + dao.add(u); + + assertTrue(dao.authenticate("user", "secret").isPresent()); + } + + @Test + public void testAuthenticateWrongPassword() { + User u = new User(); + u.setUsername("user"); + u.setPassword("secret"); + dao.add(u); + + assertFalse(dao.authenticate("user", "wrong").isPresent()); + } + + @Test + public void testListAllUsers() { + List users = dao.listAll(); + assertEquals(1, users.size()); // admin + + User u = new User(); + u.setUsername("newuser"); + dao.add(u); + + assertEquals(2, dao.listAll().size()); + } + + @Test + public void testAddAndListQuests() { + User u = new User(); + u.setUsername("questcreator"); + dao.add(u); + + Quest q1 = new Quest("Quest 1", List.of("Step 1"), "questcreator"); + Quest q2 = new Quest("Quest 2", List.of("A", "B"), "questcreator"); + + assertTrue(dao.addQuest("questcreator", q1)); + assertTrue(dao.addQuest("questcreator", q2)); + + List quests = dao.listQuests("questcreator"); + assertEquals(2, quests.size()); + } + + @Test + public void testListQuestsEmptyUser() { + List quests = dao.listQuests("nonexistent"); + assertNotNull(quests); + assertTrue(quests.isEmpty()); + } + + @Test + public void testAddQuestNullFails() { + assertFalse(dao.addQuest("user", null)); + } + + @Test + public void testAddQuestNullUsernameFails() { + Quest q = new Quest("Q", List.of(), "user"); + assertFalse(dao.addQuest(null, q)); + } +} diff --git a/src/test/java/pantera/textquest/dao/InMemoryUserDaoTest.java b/src/test/java/pantera/textquest/dao/InMemoryUserDaoTest.java new file mode 100644 index 0000000..1f4f2dc --- /dev/null +++ b/src/test/java/pantera/textquest/dao/InMemoryUserDaoTest.java @@ -0,0 +1,23 @@ +package pantera.textquest.dao; + +import com.javarush.wladimir.dao.InMemoryUserDao; +import org.junit.jupiter.api.Test; +import com.javarush.wladimir.model.User; + +import static org.junit.jupiter.api.Assertions.*; + +public class InMemoryUserDaoTest { + @Test + public void addAndFindUser() { + InMemoryUserDao dao = new InMemoryUserDao(); + User u = new User(); + u.setUsername("testuser"); + u.setPassword("pwd"); + assertTrue(dao.add(u)); + assertTrue(dao.findByUsername("testuser").isPresent()); + assertTrue(dao.authenticate("testuser", "pwd").isPresent()); + assertFalse(dao.authenticate("testuser", "wrong").isPresent()); + assertTrue(dao.delete("testuser")); + assertFalse(dao.findByUsername("testuser").isPresent()); + } +} diff --git a/src/test/java/pantera/textquest/model/QuestTest.java b/src/test/java/pantera/textquest/model/QuestTest.java new file mode 100644 index 0000000..75d2594 --- /dev/null +++ b/src/test/java/pantera/textquest/model/QuestTest.java @@ -0,0 +1,45 @@ +package pantera.textquest.model; + +import com.javarush.wladimir.model.Quest; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class QuestTest { + @Test + public void testQuestCreation() { + Quest q = new Quest("Test Quest", List.of("Step 1", "Step 2"), "creator"); + assertEquals("Test Quest", q.getTitle()); + assertEquals(2, q.getSteps().size()); + assertEquals("creator", q.getCreator()); + assertNotNull(q.getId()); + } + + @Test + public void testQuestEmptySteps() { + Quest q = new Quest("Quest", null, "user"); + assertNotNull(q.getSteps()); + assertTrue(q.getSteps().isEmpty()); + } + + @Test + public void testQuestSettersAndGetters() { + Quest q = new Quest(); + q.setTitle("New Quest"); + q.setSteps(List.of("A", "B", "C")); + q.setCreator("admin"); + + assertEquals("New Quest", q.getTitle()); + assertEquals(3, q.getSteps().size()); + assertEquals("admin", q.getCreator()); + } + + @Test + public void testQuestIdUniqueness() { + Quest q1 = new Quest("Q1", List.of(), "user"); + Quest q2 = new Quest("Q2", List.of(), "user"); + assertNotEquals(q1.getId(), q2.getId()); + } +} diff --git a/src/test/java/pantera/textquest/model/UserTest.java b/src/test/java/pantera/textquest/model/UserTest.java new file mode 100644 index 0000000..20670b1 --- /dev/null +++ b/src/test/java/pantera/textquest/model/UserTest.java @@ -0,0 +1,48 @@ +package pantera.textquest.model; + +import com.javarush.wladimir.model.Role; +import com.javarush.wladimir.model.User; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class UserTest { + @Test + public void testUserCreation() { + User u = new User(); + u.setUsername("testuser"); + u.setPassword("password"); + u.setRole(Role.USER); + + assertEquals("testuser", u.getUsername()); + assertEquals("password", u.getPassword()); + assertEquals(Role.USER, u.getRole()); + assertNotNull(u.getId()); + } + + @Test + public void testUserProgress() { + User u = new User(); + assertEquals("start", u.getProgress()); + + u.setProgress("zone"); + assertEquals("zone", u.getProgress()); + + u.setProgress("artifact_found"); + assertEquals("artifact_found", u.getProgress()); + } + + @Test + public void testUserAdmin() { + User u = new User(); + u.setRole(Role.ADMIN); + assertEquals(Role.ADMIN, u.getRole()); + } + + @Test + public void testUserIdUniqueness() { + User u1 = new User(); + User u2 = new User(); + assertNotEquals(u1.getId(), u2.getId()); + } +}