diff --git a/pom.xml b/pom.xml index 78ee59d..cec3cce 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,13 @@ + + + org.apache.commons + commons-lang3 + 3.20.0 + compile + jakarta.servlet jakarta.servlet-api @@ -65,6 +72,17 @@ junit-jupiter-engine test + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-junit-jupiter + 5.21.0 + test + diff --git a/src/main/java/bakhtin/Action.java b/src/main/java/bakhtin/Action.java new file mode 100644 index 0000000..2e8257c --- /dev/null +++ b/src/main/java/bakhtin/Action.java @@ -0,0 +1,18 @@ +package bakhtin; + +import bakhtin.exceptions.IllegalActionException; + +public enum Action { + ANSWER, RESTART, EXIT; + + public static Action parse(String actionStr) { + if (actionStr != null) { + try { + return Action.valueOf(actionStr.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalActionException("Action not found for: " + actionStr); + } + } + throw new IllegalActionException("Action not found for: " + actionStr); + } +} \ No newline at end of file diff --git a/src/main/java/bakhtin/ErrorHandlingFilter.java b/src/main/java/bakhtin/ErrorHandlingFilter.java new file mode 100644 index 0000000..61fdb10 --- /dev/null +++ b/src/main/java/bakhtin/ErrorHandlingFilter.java @@ -0,0 +1,39 @@ +package bakhtin; + +import bakhtin.exceptions.IllegalActionException; +import bakhtin.exceptions.NoActiveQuestException; +import bakhtin.exceptions.NoAnswerGivenException; +import bakhtin.exceptions.NoQuestionException; +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import java.io.IOException; + +import static bakhtin.SessionConstants.*; + +@WebFilter("/*") +public class ErrorHandlingFilter implements Filter { + + @Override + public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) + throws IOException, ServletException { + try { + chain.doFilter(req, resp); + } catch (NoActiveQuestException e) { + // TODO(log): log.error("No active quest exception", e); + req.setAttribute(ERROR_MESSAGE_ATTR, "Квест не найден. Начните квест заново."); + req.getRequestDispatcher("/WEB-INF/hello.jsp").forward(req, resp); + } catch (NoAnswerGivenException e) { + // TODO(log): log.error("No answer given exception", e); + req.setAttribute(ERROR_MESSAGE_ATTR, "Не выбран ответ. Попробуйте ещё раз."); + req.getRequestDispatcher("/WEB-INF/hello.jsp").forward(req, resp); + } catch (NoQuestionException e) { + // TODO(log): log.error("No question exception", e); + req.setAttribute(ERROR_MESSAGE_ATTR, "Вопрос не найден. Начните квест заново."); + req.getRequestDispatcher("/WEB-INF/hello.jsp").forward(req, resp); + } catch (IllegalActionException e) { + // TODO(log): log.error("Illegal action exception", e); + req.setAttribute(ERROR_MESSAGE_ATTR, "Неверное действие. Попробуйте ещё раз"); + req.getRequestDispatcher("/WEB-INF/hello.jsp").forward(req, resp); + } + } +} \ No newline at end of file diff --git a/src/main/java/bakhtin/FrontController.java b/src/main/java/bakhtin/FrontController.java new file mode 100644 index 0000000..d86d8a8 --- /dev/null +++ b/src/main/java/bakhtin/FrontController.java @@ -0,0 +1,22 @@ +package bakhtin; + +import bakhtin.Quest.Question.Answer; +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; +import java.util.ArrayList; +import java.util.List; + +@WebServlet({"/bakhtin", "/bakhtin/", "/bakhtin/home", ""}) +public class FrontController extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + // Перенаправляем на quest сервлет + resp.sendRedirect(req.getContextPath() + "/hello"); + } +} diff --git a/src/main/java/bakhtin/Quest.java b/src/main/java/bakhtin/Quest.java new file mode 100644 index 0000000..733528c --- /dev/null +++ b/src/main/java/bakhtin/Quest.java @@ -0,0 +1,208 @@ +package bakhtin; + +import bakhtin.Quest.Question.Answer; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +@Getter +public class Quest implements Serializable { + + @Setter + Question startQuestion; + String name; + ConcurrentHashMap questions; + + public Quest(String name) { + this.name = name; + questions = new ConcurrentHashMap<>(); + } + + public static QuestBuilder builder() { + return new QuestBuilder(); + } + + + public Question addNewQuestion(String question, List answers) { + Question q = new Question(question, answers); + if (q.getId().equals(1L)) { + startQuestion = q; + } + questions.put(q.getId(), q); + return q; + } + + public Question getQuestion(Long questionId) { + return questions.get(questionId); + } + + public static class QuestBuilder { + + private String name; + private Question startQuestion; + private final Map questions = new ConcurrentHashMap<>(); + + private QuestBuilder() { + } + + public QuestBuilder name(String name) { + this.name = name; + return this; + } + + public QuestBuilder question(Question question) { + if (question.getId() == 1L) { + startQuestion = question; + } + questions.put(question.getId(), question); + return this; + } + + public QuestBuilder startQuestion(Question startQuestion) { + this.startQuestion = startQuestion; + questions.put(startQuestion.getId(), startQuestion); + return this; + } + + public Quest build() { + Quest quest = new Quest(name); + quest.setStartQuestion(startQuestion); + quest.getQuestions().putAll(questions); + return quest; + } + + } + + + @Getter + public static class Question implements Serializable { + + private static final long serialVersionUID = 1L; + private boolean finish; + private boolean win; + private static final AtomicLong idGenerator = new AtomicLong(0); + String question; + ConcurrentHashMap answers = new ConcurrentHashMap<>(); + Long id; + + public static QuestionBuilder builder() { + return new QuestionBuilder(); + } + + public Question(boolean win, String question) { + this.win = win; + this.question = question; + finish = true; + id = idGenerator.incrementAndGet(); + } + + public Question(String question, List answers) { + this.question = question; + id = idGenerator.incrementAndGet(); + for (Answer answer : answers) { + this.answers.put(answer.id, answer); + } + finish = false; + } + + public Answer getAnswer(Long answerId) { + return answers.get(answerId); + } + + public static class QuestionBuilder { + + private String question; + private List answers = new ArrayList<>(); + private boolean terminal; + private boolean win; + + private QuestionBuilder() { + } + + public QuestionBuilder question(String question) { + this.question = question; + return this; + } + + public QuestionBuilder answer(Answer answer) { + answers.add(answer); + return this; + } + + public QuestionBuilder win(boolean win) { + terminal = true; + this.win = win; + return this; + } + + public Question build() { + if (terminal) { + return new Question(win, question); + } else { + return new Question(question, answers); + } + } + + } + + @Getter + public static class Answer implements Serializable { + + private static final long serialVersionUID = 1L; + private static final AtomicLong idGenerator = new AtomicLong(0); + + private final Long id; + private final String text; + private @Setter + Question nextQuestion; + + + public static AnswerBuilder builder() { + return new AnswerBuilder(); + } + + public Answer(String text) { + this.text = text; + this.nextQuestion = null; + id = idGenerator.incrementAndGet(); + } + + public Answer(String text, Question nextQuestion) { + this.text = text; + this.nextQuestion = nextQuestion; + id = idGenerator.incrementAndGet(); + } + + public static class AnswerBuilder { + + private String answer; + private Question nextQuestion; + + public AnswerBuilder answer(String answer) { + this.answer = answer; + return this; + } + + public AnswerBuilder nextQuestion(Question nextQuestion) { + this.nextQuestion = nextQuestion; + return this; + } + + private AnswerBuilder() { + } + + public Answer build() { + return new Answer(answer, nextQuestion); + } + + } + + } + + } +} diff --git a/src/main/java/bakhtin/QuestServlet.java b/src/main/java/bakhtin/QuestServlet.java new file mode 100644 index 0000000..583b994 --- /dev/null +++ b/src/main/java/bakhtin/QuestServlet.java @@ -0,0 +1,176 @@ +package bakhtin; + +import bakhtin.Quest.Question; +import bakhtin.Quest.Question.Answer; +import bakhtin.exceptions.IllegalActionException; +import bakhtin.exceptions.NoActiveQuestException; +import bakhtin.exceptions.NoAnswerGivenException; +import bakhtin.exceptions.NoQuestionException; +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 jakarta.servlet.http.HttpSession; +import java.io.IOException; +import org.apache.commons.lang3.StringUtils; + +import static bakhtin.SessionConstants.*; + +@WebServlet("/quest") +public class QuestServlet extends HttpServlet { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + String actionStr = req.getParameter(SessionConstants.ACTION_PARAM); + Action action = Action.parse(actionStr); + + HttpSession session = req.getSession(); + + switch (action) { + case ANSWER -> doAnswer(req, resp, session); + case RESTART -> doRestart(req, resp, session); + case EXIT -> doExit(req, resp, session); + default -> throw new IllegalActionException("Invalid action: " + actionStr); + } + } + + private void doExit(HttpServletRequest req, HttpServletResponse resp, + HttpSession session) throws ServletException, IOException { + session.invalidate(); + req.getRequestDispatcher("/WEB-INF/hello.jsp").forward(req, resp); + } + + private void doAnswer(HttpServletRequest req, HttpServletResponse resp, HttpSession session) + throws ServletException, IOException { + Object attr = session.getAttribute(CURRENT_QUESTION_ATTR); + if (!(attr instanceof Question question)) { + throw new NoQuestionException("Question not found in session"); + } + String answerId = req.getParameter(ANSWER_ID_PARAM); + long id; + if (StringUtils.isNotEmpty(answerId)) { + id = Long.parseLong(answerId); + } else { + throw new NoAnswerGivenException("Answer ID not found in session"); + } + + Question nextQuestion = getNextQuestion(question, id); + + if (nextQuestion.isFinish()) { + doFinish(req, session, nextQuestion); + } + + session.setAttribute(CURRENT_QUESTION_ATTR, nextQuestion); + req.setAttribute(CURRENT_QUESTION_REQUEST_ATTR, nextQuestion); + req.getRequestDispatcher("/WEB-INF/quest.jsp").forward(req, resp); + } + + private void doFinish(HttpServletRequest req, HttpSession session, + Question nextQuestion) { + if (nextQuestion.isWin()) { + req.setAttribute(WIN_ATTR, "true"); + } else { + req.setAttribute(WIN_ATTR, "false"); + } + + // Увеличиваем счетчик сыгранных игр + int gamesPlayed = (int) session.getAttribute(GAMES_PLAYED_ATTR); + session.setAttribute(GAMES_PLAYED_ATTR, gamesPlayed + 1); + } + + + protected Question getNextQuestion(Question question, Long answerId) { + Answer answer = question.getAnswer(answerId); + if (answer == null) { + throw new NoAnswerGivenException("Answer not found for ID: " + answerId); + } + + Question nextQuestion = answer.getNextQuestion(); + + if (nextQuestion == null) { + throw new NoQuestionException("No next question found for answer ID: " + answerId); + } + return nextQuestion; + } + + protected void doRestart(HttpServletRequest req, HttpServletResponse resp, HttpSession session) + throws ServletException, IOException { + req.removeAttribute(CURRENT_QUESTION_REQUEST_ATTR); + req.removeAttribute(ANSWER_ID_PARAM); + + Object objQuest = session.getAttribute(QUEST_ATTR); + + if (objQuest == null) { + throw new NoQuestionException("Quest not found in session"); + } + + Quest quest = (Quest) objQuest; + + Question startQuestion = quest.getStartQuestion(); + session.setAttribute(CURRENT_QUESTION_ATTR, startQuestion); + req.setAttribute(CURRENT_QUESTION_REQUEST_ATTR, startQuestion); + + req.getRequestDispatcher("/WEB-INF/quest.jsp").forward(req, resp); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + HttpSession session = req.getSession(); + + if (session.getAttribute(FIRST_GET_PROCESSED_ATTR) == null) { + prepareSession(session, req); + } + req.getRequestDispatcher("/WEB-INF/quest.jsp").forward(req, resp); + } + + private void prepareSession(HttpSession session, HttpServletRequest request) { + session.setAttribute(FIRST_GET_PROCESSED_ATTR, true); + + if (session.getAttribute(GAMES_PLAYED_ATTR) == null) { + session.setAttribute(GAMES_PLAYED_ATTR, 0); + } + + Quest newQuest = getStartQuest(); + + session.setAttribute(QUEST_ATTR, newQuest); + session.setAttribute(CURRENT_QUESTION_ATTR, newQuest.getStartQuestion()); + request.setAttribute(CURRENT_QUESTION_REQUEST_ATTR, newQuest.getStartQuestion()); + } + + protected Quest getStartQuest() { + Quest newQuest = Quest.builder().name("base_quest").startQuestion( + Question.builder().question("Tы потерял память. Принять вызов НЛО?") + .answer(Answer.builder().answer("Принять вызов").nextQuestion( + Question.builder().question( + "Ты принял вызов. Поднимаешься на мостик к капитану?") + .answer(Answer.builder().answer("Подняться на мостик") + .nextQuestion(Question.builder() + .question("Ты поднялся на мостик. Ты кто?") + .answer(Answer.builder() + .answer("Рассказать правду о себе") + .nextQuestion(Question.builder() + .question( + "Тебя вернули домой. Победа!") + .win(true).build()).build()) + .answer(Answer.builder() + .answer("Солгать о себе") + .nextQuestion(Question.builder() + .question( + "Твою ложь разоблачили. Поражение.") + .win(false).build()) + .build()).build()).build()) + .answer(Answer.builder() + .answer("Отказаться подниматься на мостик") + .nextQuestion(Question.builder().question( + "Ты не пошел на переговоры. Поражение.") + .win(false).build()).build()).build()) + .build()).answer(Answer.builder().answer("Отклонить вызов") + .nextQuestion(Question.builder().question("Ты отклонил вызов. Поражение.") + .win(false).build()).build()).build()).build(); + return newQuest; + } +} diff --git a/src/main/java/bakhtin/SessionConstants.java b/src/main/java/bakhtin/SessionConstants.java new file mode 100644 index 0000000..bb9e163 --- /dev/null +++ b/src/main/java/bakhtin/SessionConstants.java @@ -0,0 +1,26 @@ +package bakhtin; + +/** + * Константы для атрибутов сессии, запроса и параметров запроса + */ +public final class SessionConstants { + + // Параметры запроса + public static final String ACTION_PARAM = "action"; + public static final String ANSWER_ID_PARAM = "answerId"; + + // Атрибуты сессии + public static final String QUEST_ATTR = "quest"; + public static final String CURRENT_QUESTION_ATTR = "currentQuestion"; + public static final String FIRST_GET_PROCESSED_ATTR = "firstGetProcessed"; + public static final String GAMES_PLAYED_ATTR = "gamesPlayed"; + + // Атрибуты запроса + public static final String CURRENT_QUESTION_REQUEST_ATTR = "currentQuestion"; + public static final String WIN_ATTR = "win"; + public static final String ERROR_MESSAGE_ATTR = "errorMessage"; + + private SessionConstants() { + // Приватный конструктор для предотвращения создания экземпляров + } +} \ No newline at end of file diff --git a/src/main/java/bakhtin/exceptions/IllegalActionException.java b/src/main/java/bakhtin/exceptions/IllegalActionException.java new file mode 100644 index 0000000..98eaed4 --- /dev/null +++ b/src/main/java/bakhtin/exceptions/IllegalActionException.java @@ -0,0 +1,8 @@ +package bakhtin.exceptions; + +public class IllegalActionException extends QuestException { + + public IllegalActionException(String message) { + super(message); + } +} diff --git a/src/main/java/bakhtin/exceptions/NoActiveQuestException.java b/src/main/java/bakhtin/exceptions/NoActiveQuestException.java new file mode 100644 index 0000000..9275e91 --- /dev/null +++ b/src/main/java/bakhtin/exceptions/NoActiveQuestException.java @@ -0,0 +1,8 @@ +package bakhtin.exceptions; + +public class NoActiveQuestException extends QuestException { + + public NoActiveQuestException(String message) { + super(message); + } +} diff --git a/src/main/java/bakhtin/exceptions/NoAnswerGivenException.java b/src/main/java/bakhtin/exceptions/NoAnswerGivenException.java new file mode 100644 index 0000000..214c10a --- /dev/null +++ b/src/main/java/bakhtin/exceptions/NoAnswerGivenException.java @@ -0,0 +1,8 @@ +package bakhtin.exceptions; + +public class NoAnswerGivenException extends QuestException { + + public NoAnswerGivenException(String message) { + super(message); + } +} diff --git a/src/main/java/bakhtin/exceptions/NoQuestionException.java b/src/main/java/bakhtin/exceptions/NoQuestionException.java new file mode 100644 index 0000000..06bb1db --- /dev/null +++ b/src/main/java/bakhtin/exceptions/NoQuestionException.java @@ -0,0 +1,8 @@ +package bakhtin.exceptions; + +public class NoQuestionException extends QuestException { + + public NoQuestionException(String message) { + super(message); + } +} diff --git a/src/main/java/bakhtin/exceptions/QuestException.java b/src/main/java/bakhtin/exceptions/QuestException.java new file mode 100644 index 0000000..ba69cbd --- /dev/null +++ b/src/main/java/bakhtin/exceptions/QuestException.java @@ -0,0 +1,8 @@ +package bakhtin.exceptions; + +public class QuestException extends RuntimeException { + + public QuestException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/controller/FrontController.java b/src/main/java/com/javarush/khmelov/controller/FrontController.java index 33242b2..e85c6a1 100644 --- a/src/main/java/com/javarush/khmelov/controller/FrontController.java +++ b/src/main/java/com/javarush/khmelov/controller/FrontController.java @@ -12,7 +12,7 @@ import java.io.IOException; -@WebServlet({"", "/home", "/list-user", "/edit-user"}) +@WebServlet({"/home", "/list-user", "/edit-user"}) public class FrontController extends HttpServlet { private final HttpResolver httpResolver = Winter.find(HttpResolver.class); diff --git a/src/main/webapp/WEB-INF/hello.jsp b/src/main/webapp/WEB-INF/hello.jsp new file mode 100644 index 0000000..6cee019 --- /dev/null +++ b/src/main/webapp/WEB-INF/hello.jsp @@ -0,0 +1,38 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib tagdir="/WEB-INF/tags" prefix="my" %> + + + + Quest Pantera + + + + + + + + + + + + + + + Добро пожаловать, пользователь! + + + + + 🎮 Начать квест + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/quest.jsp b/src/main/webapp/WEB-INF/quest.jsp new file mode 100644 index 0000000..c2a2ba8 --- /dev/null +++ b/src/main/webapp/WEB-INF/quest.jsp @@ -0,0 +1,97 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib tagdir="/WEB-INF/tags" prefix="my" %> + + + + Quest Game + + + + + + + + + + + + + 📊 Сыграно игр: ${requestScope[GAMES_PLAYED_ATTR]} + + + + + + + + + + + + 🎉 Победа! + 💀 Поражение + + ${requestScope[CURRENT_QUESTION_ATTR].question} + + + + + + ${requestScope[CURRENT_QUESTION_ATTR].question} + + + + + + + + + + ${entry.value.text} + + + + + + + + + + + 🔄 Заново + + + + + + 🚪 Выйти + + + + + + + + + + ← Вернуться + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tags/constants.tag b/src/main/webapp/WEB-INF/tags/constants.tag new file mode 100644 index 0000000..e417d80 --- /dev/null +++ b/src/main/webapp/WEB-INF/tags/constants.tag @@ -0,0 +1,11 @@ +<%@ tag description="Provides access to session constants" pageEncoding="UTF-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/bakhtin/ActionTest.java b/src/test/java/bakhtin/ActionTest.java new file mode 100644 index 0000000..9ba616b --- /dev/null +++ b/src/test/java/bakhtin/ActionTest.java @@ -0,0 +1,22 @@ +package bakhtin; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class ActionTest { + + @Test + void testAllActionsAreAvailable() { + assertNotNull(Action.ANSWER); + assertNotNull(Action.RESTART); + assertNotNull(Action.EXIT); + } + + @Test + void testNames() { + assertEquals("ANSWER", Action.ANSWER.name()); + assertEquals("RESTART", Action.RESTART.name()); + assertEquals("EXIT", Action.EXIT.name()); + } +} \ No newline at end of file diff --git a/src/test/java/bakhtin/QuestServletTest.java b/src/test/java/bakhtin/QuestServletTest.java new file mode 100644 index 0000000..d201993 --- /dev/null +++ b/src/test/java/bakhtin/QuestServletTest.java @@ -0,0 +1,63 @@ +package bakhtin; + +import bakhtin.Quest.Question; +import bakhtin.Quest.Question.Answer; +import bakhtin.exceptions.IllegalActionException; +import bakhtin.exceptions.NoAnswerGivenException; +import bakhtin.exceptions.NoQuestionException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class QuestServletTest { + + @Test + void getNextQuestion_validAnswer() { + Question nextQuestion = Question.builder().question("testQuestion").build(); + Answer answer = Answer.builder().answer("testAnswer").nextQuestion(nextQuestion).build(); + Question currentQuestion = Question.builder().question("currentTestQuestion").answer(answer) + .build(); + QuestServlet questServlet = new QuestServlet(); + Long id = answer.getId(); + Question expected = questServlet.getNextQuestion(currentQuestion, id); + Assertions.assertEquals(expected, nextQuestion); + } + + @Test + void getNextQuestion_validNoAnswerGivenException() { + Question currentQuestion = Question.builder().question("currentTestQuestion").build(); + QuestServlet questServlet = new QuestServlet(); + Long id = -1L; + Assertions.assertThrows(NoAnswerGivenException.class, () -> { + Question expected = questServlet.getNextQuestion(currentQuestion, id); + }); + } + + @Test + void getNextQuestion_validNoQuestionException() { + Answer answer = Answer.builder().answer("testAnswer").build(); + Question currentQuestion = Question.builder().question("currentTestQuestion").answer(answer) + .build(); + QuestServlet questServlet = new QuestServlet(); + Long id = answer.getId(); + Assertions.assertThrows(NoQuestionException.class, () -> { + questServlet.getNextQuestion(currentQuestion, id); + }); + } + + @Test + void getAction_validAnswer() { + QuestServlet questServlet = new QuestServlet(); + Question nextQuestion = Question.builder().question("testQuestion").build(); + var actual = Action.parse("restart"); + Assertions.assertEquals(Action.RESTART, actual); + } + + @Test + void getAction_illegalActionException() { + QuestServlet questServlet = new QuestServlet(); + + Assertions.assertThrows(IllegalActionException.class, () -> { + Action.parse("***"); + }); + } +} \ No newline at end of file diff --git a/src/test/java/bakhtin/QuestTest.java b/src/test/java/bakhtin/QuestTest.java new file mode 100644 index 0000000..2d250b9 --- /dev/null +++ b/src/test/java/bakhtin/QuestTest.java @@ -0,0 +1,36 @@ +package bakhtin; + +import static org.junit.jupiter.api.Assertions.*; + +import bakhtin.Quest.Question; +import org.junit.jupiter.api.Test; + +class QuestTest { + + @Test + void testBuilderName() { + Quest quest = Quest.builder().name("Test").build(); + assertEquals("Test", quest.getName()); + } + + @Test + void testBuilderQuestionWithAnswer() { + Question.Answer answer = Question.Answer.builder().answer("TestAnswer").build(); + Question question = Question.builder().question("TestQuestion").answer(answer).build(); + assertTrue(question.getAnswers().containsValue(answer)); + } + + @Test + void testQuestTerminalQuestion() { + Question question = Question.builder().question("TestQuestion").win(true).build(); + assertTrue(question.isFinish()); + assertTrue(question.isWin()); + } + + @Test + void testQuestNextQuestion() { + Question question = Question.builder().question("TestQuestion").build(); + Question.Answer answer = Question.Answer.builder().answer("TestAnswer").nextQuestion(question).build(); + assertEquals(answer.getNextQuestion(), question); + } +} \ No newline at end of file
${requestScope[CURRENT_QUESTION_ATTR].question}