diff --git a/.gitignore b/.gitignore
index ced985d..6860851 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,3 @@
target
*.iml
*.log
-*.txt
\ No newline at end of file
diff --git a/README.md b/README.md
index 67d4ca0..becb362 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,10 @@
-# OtusSpringHW
-Домашние задания по Spring для Otus.
+# OtusSpringHW
+
+Домашнее задание #4:
+- Перевести приложение для проведения опросов на Spring Shell.
+- Подключить Spring Shell, используя starter.
+- Написать юнит-тесты и использовать spring-boot-test-starter.
+
+### 19-05-26 Доработки:
+- Функционал по печати результатов вынесен в отдельный метод.
+- Разделение функционала метода startTest и использование @ShellMethodAvailability.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..ef2ed40
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,147 @@
+
+
+ 4.0.0
+
+ home-work
+
+ ru.otus.mkulikov
+ home-work
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.3.RELEASE
+
+
+
+
+ 11
+ UTF-8
+
+
+
+
+ net.sf.opencsv
+ opencsv
+ 2.3
+
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.4
+ provided
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.3.2
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.3.2
+ test
+
+
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.4.1
+ test
+
+
+
+
+ org.mockito
+ mockito-all
+ 1.10.19
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.shell
+ spring-shell-starter
+ 2.0.0.RELEASE
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${encoding}
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+ ${encoding}
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.0
+
+
+ false
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+ src/main/resources
+
+ **/*.csv
+ **/*.properties
+ **/*.txt
+ **/*.yml
+
+ false
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/Application.java b/src/main/java/ru/otus/mkulikov/Application.java
new file mode 100644
index 0000000..134eb85
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/Application.java
@@ -0,0 +1,21 @@
+package ru.otus.mkulikov;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:27
+ */
+
+@SpringBootApplication
+@EnableConfigurationProperties
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/config/LocaleProperties.java b/src/main/java/ru/otus/mkulikov/app/config/LocaleProperties.java
new file mode 100644
index 0000000..ff2f51e
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/config/LocaleProperties.java
@@ -0,0 +1,22 @@
+package ru.otus.mkulikov.app.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 19.04.2019
+ * Time: 9:57
+ */
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "locale")
+public class LocaleProperties {
+
+ private String basename;
+ private String defaultLocale;
+ private String encoding;
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/config/QuetionsProperties.java b/src/main/java/ru/otus/mkulikov/app/config/QuetionsProperties.java
new file mode 100644
index 0000000..0231bc7
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/config/QuetionsProperties.java
@@ -0,0 +1,20 @@
+package ru.otus.mkulikov.app.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 19.04.2019
+ * Time: 10:25
+ */
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "questions.default")
+public class QuetionsProperties {
+
+ private String dir;
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/exceptions/QuestionsFileLoadingException.java b/src/main/java/ru/otus/mkulikov/app/exceptions/QuestionsFileLoadingException.java
new file mode 100644
index 0000000..8f5e560
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/exceptions/QuestionsFileLoadingException.java
@@ -0,0 +1,19 @@
+package ru.otus.mkulikov.app.exceptions;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-17
+ * Time: 01:40
+ */
+
+public class QuestionsFileLoadingException extends RuntimeException {
+
+ public QuestionsFileLoadingException(String message) {
+ super(message);
+ }
+
+ public QuestionsFileLoadingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/models/Question.java b/src/main/java/ru/otus/mkulikov/app/models/Question.java
new file mode 100644
index 0000000..ef74502
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/models/Question.java
@@ -0,0 +1,39 @@
+package ru.otus.mkulikov.app.models;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:54
+ */
+
+@Data
+public class Question implements Serializable {
+
+ private String id;
+ private String question;
+ private String answer1;
+ private String answer2;
+ private String answer3;
+ private String answer4;
+ private String trueAnswer;
+ private String userAnswer;
+
+ public Question(String id, String question, String answer1, String answer2, String answer3, String answer4, String trueAnswer, String userAnswer) {
+ this.id = id;
+ this.question = question;
+ this.answer1 = answer1;
+ this.answer2 = answer2;
+ this.answer3 = answer3;
+ this.answer4 = answer4;
+ this.trueAnswer = trueAnswer;
+ this.userAnswer = userAnswer;
+ }
+
+ public Question() {
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/models/User.java b/src/main/java/ru/otus/mkulikov/app/models/User.java
new file mode 100644
index 0000000..0abc4dd
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/models/User.java
@@ -0,0 +1,22 @@
+package ru.otus.mkulikov.app.models;
+
+import lombok.Data;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:29
+ */
+
+@Data
+public class User {
+
+ private final String name;
+ private final String surname;
+
+ public User(String name, String surname) {
+ this.name = name;
+ this.surname = surname;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/console/ConsoleIOServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/services/console/ConsoleIOServiceImpl.java
new file mode 100644
index 0000000..d990321
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/console/ConsoleIOServiceImpl.java
@@ -0,0 +1,38 @@
+package ru.otus.mkulikov.app.services.console;
+
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PreDestroy;
+import java.util.Scanner;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 17.03.2019
+ * Time: 23:21
+ */
+
+@Service
+public class ConsoleIOServiceImpl implements IOService {
+
+ private final Scanner in;
+
+ public ConsoleIOServiceImpl() {
+ in = new Scanner(System.in);
+ }
+
+ @Override
+ public void write(String text) {
+ System.out.println(text);
+ }
+
+ @Override
+ public String read() {
+ return in.nextLine();
+ }
+
+ @PreDestroy
+ public void destroy() {
+ in.close();
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/console/IOService.java b/src/main/java/ru/otus/mkulikov/app/services/console/IOService.java
new file mode 100644
index 0000000..f8ed658
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/console/IOService.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.services.console;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 17.03.2019
+ * Time: 23:21
+ */
+
+public interface IOService {
+
+ void write(String text);
+
+ String read();
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/localisation/LocalisationService.java b/src/main/java/ru/otus/mkulikov/app/services/localisation/LocalisationService.java
new file mode 100644
index 0000000..b1c0eb9
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/localisation/LocalisationService.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.services.localisation;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-21
+ * Time: 14:02
+ */
+
+public interface LocalisationService {
+
+ String getValue(String key);
+
+ String getValueWithParams(String key, String[] params);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImpl.java
new file mode 100644
index 0000000..ab8a7bc
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImpl.java
@@ -0,0 +1,39 @@
+package ru.otus.mkulikov.app.services.localisation;
+
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.config.LocaleProperties;
+
+import java.util.Locale;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-21
+ * Time: 14:02
+ */
+
+@Service
+public class LocalisationServiceImpl extends ReloadableResourceBundleMessageSource implements LocalisationService {
+
+ private final Locale locale;
+ private final Locale russian = new Locale("ru");
+
+ public LocalisationServiceImpl(LocaleProperties localeProperties) {
+ super();
+ setBasename(localeProperties.getBasename());
+ setDefaultEncoding(localeProperties.getEncoding());
+
+ locale = (localeProperties.getDefaultLocale() != null)
+ ? new Locale(localeProperties.getDefaultLocale())
+ : russian;
+ }
+
+ public String getValue(String key) {
+ return getMessage(key, null, locale);
+ }
+
+ public String getValueWithParams(String key, String[] params) {
+ return getMessage(key, params, locale);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/processor/ProcessorService.java b/src/main/java/ru/otus/mkulikov/app/services/processor/ProcessorService.java
new file mode 100644
index 0000000..c599db8
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/processor/ProcessorService.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.services.processor;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:54
+ */
+
+public interface ProcessorService {
+
+ boolean registry(String userName, String userSurname);
+
+ void startTest();
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/processor/ProcessorServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/services/processor/ProcessorServiceImpl.java
new file mode 100644
index 0000000..4776767
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/processor/ProcessorServiceImpl.java
@@ -0,0 +1,34 @@
+package ru.otus.mkulikov.app.services.processor;
+
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.services.questions.QuestionsService;
+import ru.otus.mkulikov.app.services.registration.RegistrationService;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:54
+ */
+
+@Service
+public class ProcessorServiceImpl implements ProcessorService {
+
+ private final QuestionsService questionsService;
+ private final RegistrationService registration;
+
+ public ProcessorServiceImpl(QuestionsService questionsService, RegistrationService registration) {
+ this.questionsService = questionsService;
+ this.registration = registration;
+ }
+
+ @Override
+ public boolean registry(String userName, String userSurname) {
+ return registration.addNewUser(userName, userSurname);
+ }
+
+ @Override
+ public void startTest() {
+ questionsService.showQuestions();
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/questions/QuestionsService.java b/src/main/java/ru/otus/mkulikov/app/services/questions/QuestionsService.java
new file mode 100644
index 0000000..8499fc8
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/questions/QuestionsService.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.services.questions;
+
+import ru.otus.mkulikov.app.exceptions.QuestionsFileLoadingException;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 16:41
+ */
+
+public interface QuestionsService {
+
+ void showQuestions() throws QuestionsFileLoadingException;
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/questions/QuestionsServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/services/questions/QuestionsServiceImpl.java
new file mode 100644
index 0000000..3efa3b6
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/questions/QuestionsServiceImpl.java
@@ -0,0 +1,78 @@
+package ru.otus.mkulikov.app.services.questions;
+
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.exceptions.QuestionsFileLoadingException;
+import ru.otus.mkulikov.app.models.Question;
+import ru.otus.mkulikov.app.services.console.IOService;
+import ru.otus.mkulikov.app.services.localisation.LocalisationService;
+import ru.otus.mkulikov.app.services.questions.dao.QuestionsDAO;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 16:41
+ */
+
+@Service
+public class QuestionsServiceImpl implements QuestionsService {
+
+ private final String c_delimeter = "---------------------------------------------------";
+ private final List answerNumbers = Arrays.asList(new String[]{"1", "2", "3", "4"});
+
+ private final QuestionsDAO questionsDAO;
+ private final IOService consoleService;
+ private final LocalisationService localisationService;
+
+ public QuestionsServiceImpl(QuestionsDAO questionsDAO, IOService consoleService, LocalisationService localisationService) {
+ this.questionsDAO = questionsDAO;
+ this.consoleService = consoleService;
+ this.localisationService = localisationService;
+ }
+
+ @Override
+ public void showQuestions() throws QuestionsFileLoadingException {
+ consoleService.write(c_delimeter);
+ List questions = questionsDAO.getQuestions();
+
+ for (Question question : questions) {
+ printQuestion(question);
+ }
+
+ long count = questions.stream()
+ .filter(obj -> obj.getTrueAnswer().equals(obj.getUserAnswer()))
+ .count();
+
+ consoleService.write(
+ localisationService.getValueWithParams(
+ "count.true.answers",
+ new String[]{String.valueOf(count), String.valueOf(questions.size())}
+ )
+ );
+ consoleService.write(c_delimeter);
+ }
+
+ private void printQuestion(Question question) {
+ consoleService.write(question.getQuestion());
+ consoleService.write(question.getAnswer1());
+ consoleService.write(question.getAnswer2());
+ consoleService.write(question.getAnswer3());
+ consoleService.write(question.getAnswer4());
+
+ String answer = null;
+ boolean isOkAnswer = false;
+ int i = 0;
+ while (!isOkAnswer && i < 4) {
+ consoleService.write(localisationService.getValue("enter.answer"));
+ answer = consoleService.read();
+ isOkAnswer = answerNumbers.contains(answer) && !answer.isEmpty();
+ i++;
+ }
+
+ question.setUserAnswer((!isOkAnswer && i == 4) ? "0" : answer);
+ consoleService.write(c_delimeter);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/questions/dao/QuestionsDAO.java b/src/main/java/ru/otus/mkulikov/app/services/questions/dao/QuestionsDAO.java
new file mode 100644
index 0000000..c5a4a14
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/questions/dao/QuestionsDAO.java
@@ -0,0 +1,18 @@
+package ru.otus.mkulikov.app.services.questions.dao;
+
+import ru.otus.mkulikov.app.exceptions.QuestionsFileLoadingException;
+import ru.otus.mkulikov.app.models.Question;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 16:41
+ */
+
+public interface QuestionsDAO {
+
+ List getQuestions() throws QuestionsFileLoadingException;
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/questions/dao/QuestionsDAOImpl.java b/src/main/java/ru/otus/mkulikov/app/services/questions/dao/QuestionsDAOImpl.java
new file mode 100644
index 0000000..74375c1
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/questions/dao/QuestionsDAOImpl.java
@@ -0,0 +1,63 @@
+package ru.otus.mkulikov.app.services.questions.dao;
+
+import au.com.bytecode.opencsv.CSVReader;
+import au.com.bytecode.opencsv.bean.ColumnPositionMappingStrategy;
+import au.com.bytecode.opencsv.bean.CsvToBean;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Repository;
+import ru.otus.mkulikov.app.config.QuetionsProperties;
+import ru.otus.mkulikov.app.exceptions.QuestionsFileLoadingException;
+import ru.otus.mkulikov.app.models.Question;
+import ru.otus.mkulikov.app.services.localisation.LocalisationService;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 16:42
+ */
+
+@Repository
+public class QuestionsDAOImpl implements QuestionsDAO {
+
+ private final LocalisationService localisationService;
+ private final String defaultFileDir;
+
+ public QuestionsDAOImpl(LocalisationService localisationService, QuetionsProperties quetionsProperties) {
+ this.localisationService = localisationService;
+ this.defaultFileDir = quetionsProperties.getDir();
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Override
+ public List getQuestions() throws QuestionsFileLoadingException {
+ String csvFilename = defaultFileDir + "/" + localisationService.getValue("csv.file.name");
+ List questions;
+ try {
+ ClassPathResource resource = new ClassPathResource(csvFilename);
+ CSVReader csvReader = new CSVReader(new InputStreamReader(resource.getInputStream(), "UTF-8"));
+ questions = new CsvToBean().parse(setColumMapping(), csvReader);
+ } catch (FileNotFoundException e) {
+ throw new QuestionsFileLoadingException(localisationService.getValueWithParams("questions.find.error.filename", new String[] {csvFilename}), e);
+ } catch (UnsupportedEncodingException e) {
+ throw new QuestionsFileLoadingException(localisationService.getValue("questions.encoding.error"), e);
+ } catch (IOException e) {
+ throw new QuestionsFileLoadingException(localisationService.getValue("questions.io.error"), e);
+ }
+ return questions;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private ColumnPositionMappingStrategy setColumMapping() {
+ ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();
+ strategy.setType(Question.class);
+ strategy.setColumnMapping(new String[]{"id", "question", "answer1", "answer2", "answer3", "answer4", "trueAnswer"});
+ return strategy;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/registration/RegistrationService.java b/src/main/java/ru/otus/mkulikov/app/services/registration/RegistrationService.java
new file mode 100644
index 0000000..a755341
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/registration/RegistrationService.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.services.registration;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 16:34
+ */
+
+public interface RegistrationService {
+
+ void addNewUser();
+
+ boolean addNewUser(String userName, String userSurname);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/services/registration/RegistrationServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/services/registration/RegistrationServiceImpl.java
new file mode 100644
index 0000000..ab5ff3e
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/services/registration/RegistrationServiceImpl.java
@@ -0,0 +1,67 @@
+package ru.otus.mkulikov.app.services.registration;
+
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.models.User;
+import ru.otus.mkulikov.app.services.console.IOService;
+import ru.otus.mkulikov.app.services.localisation.LocalisationService;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 16:34
+ */
+
+@Service
+public class RegistrationServiceImpl implements RegistrationService {
+
+ private final IOService consoleService;
+ private final LocalisationService localisationService;
+
+ public RegistrationServiceImpl(IOService consoleService, LocalisationService localisationService) {
+ this.consoleService = consoleService;
+ this.localisationService = localisationService;
+ }
+
+ @Override
+ public void addNewUser() {
+ consoleService.write(localisationService.getValue("enter.your.data"));
+ consoleService.write(localisationService.getValue("enter.your.surname"));
+ String surname = consoleService.read();
+
+ consoleService.write(localisationService.getValue("enter.your.name"));
+ String name = consoleService.read();
+
+ checkUser(new User(name, surname));
+ }
+
+ @Override
+ public boolean addNewUser(String userName, String userSurname) {
+ return checkUser(new User(userName, userSurname));
+ }
+
+ private boolean checkUser(User user) {
+ String name = user.getName().replace(" ", "");
+ String surname = user.getSurname().replace(" ", "");
+
+ if (name != null && !name.isEmpty() && surname != null && !surname.isEmpty()) {
+ greetingUser(new User(name, surname));
+ return true;
+ } else {
+ wrongUser();
+ return false;
+ }
+ }
+
+ private void wrongUser() {
+ consoleService.write(
+ localisationService.getValue("hello.user.fail")
+ );
+ }
+
+ private void greetingUser(User user) {
+ consoleService.write(
+ localisationService.getValueWithParams("hello.user", new String[] {user.getName(), user.getSurname()})
+ );
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/Commands.java b/src/main/java/ru/otus/mkulikov/shell/Commands.java
new file mode 100644
index 0000000..0bc4179
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/Commands.java
@@ -0,0 +1,50 @@
+package ru.otus.mkulikov.shell;
+
+import org.springframework.shell.Availability;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellMethodAvailability;
+import ru.otus.mkulikov.app.exceptions.QuestionsFileLoadingException;
+import ru.otus.mkulikov.app.services.processor.ProcessorService;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 25.05.2019
+ * Time: 14:20
+ */
+
+@ShellComponent
+public class Commands {
+
+ private boolean isCheckIn = false;
+ private ProcessorService processor;
+
+ public Commands(ProcessorService processor) {
+ this.processor = processor;
+ }
+
+ @ShellMethod(key = { "registry" }, value = "User registration")
+ public void registry(String userName, String userSurname) {
+ try {
+ isCheckIn = processor.registry(userName, userSurname);
+ } catch (QuestionsFileLoadingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @ShellMethod(key = { "startTest" }, value = "Start test")
+ @ShellMethodAvailability({"checkRegistry"})
+ public void startTest() {
+ try {
+ processor.startTest();
+ } catch (QuestionsFileLoadingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Availability checkRegistry() {
+ String message = "you are not registered";
+ return isCheckIn ? Availability.available() : Availability.unavailable(message);
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..fc12ff7
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,12 @@
+locale:
+ basename: "/i18n/bundle"
+ defaultLocale: "ru"
+ encoding: "UTF-8"
+
+questions:
+ default:
+ dir: "/questions"
+
+spring:
+ banner:
+ location: "classpath:banner.txt"
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 0000000..1687640
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,11 @@
+ ___ ___ ___ ___ ___
+ /\__\ /\ \ /\__\ /\ \ /\ \
+ /:/ / /::\ \ /:/ / /::\ \ /::\ \
+ /:/ / /:/\:\ \ /:/ / /:/\:\ \ /:/\ \ \
+ /:/ / ___ /:/ \:\ \ /:/ / /::\~\:\ \ _\:\~\ \ \
+ /:/__/ /\__\ /:/__/ \:\__\ /:/__/ /:/\:\ \:\__\ /\ \:\ \ \__\
+ \:\ \ /:/ / \:\ \ /:/ / \:\ \ \:\~\:\ \/__/ \:\ \:\ \/__/
+ \:\ /:/ / \:\ /:/ / \:\ \ \:\ \:\__\ \:\ \:\__\
+ \:\/:/ / \:\/:/ / \:\ \ \:\ \/__/ \:\/:/ /
+ \::/ / \::/ / \:\__\ \:\__\ \::/ /
+ \/__/ \/__/ \/__/ \/__/ \/__/
\ No newline at end of file
diff --git a/src/main/resources/i18n/bundle_en.properties b/src/main/resources/i18n/bundle_en.properties
new file mode 100644
index 0000000..8e8b77a
--- /dev/null
+++ b/src/main/resources/i18n/bundle_en.properties
@@ -0,0 +1,12 @@
+count.true.answers=Number of correct answers: {0} of {1}
+enter.answer=Enter answer number:
+enter.your.data=Enter your data
+enter.your.name=Name:
+enter.your.surname=Surname:
+hello.user=Hello, {0} {1}!
+hello.user.fail=Registration failed!
+questions.encoding.error=File encoding error!
+questions.find.error.filename=File named {0} not found!
+questions.io.error=Error I/O when working with a file!
+
+csv.file.name=en.csv
diff --git a/src/main/resources/i18n/bundle_ru.properties b/src/main/resources/i18n/bundle_ru.properties
new file mode 100644
index 0000000..a48e2bc
--- /dev/null
+++ b/src/main/resources/i18n/bundle_ru.properties
@@ -0,0 +1,12 @@
+count.true.answers=Количество правильных ответов: {0} из {1}
+enter.answer=Введите номер ответа:
+enter.your.data=Введите свои данные
+enter.your.name=Имя:
+enter.your.surname=Фамилия:
+hello.user=Здравствуйте, {0} {1}!
+hello.user.fail=Ошибка регистрации!
+questions.encoding.error=Ошибка кодировки файла!
+questions.find.error.filename=Файл с именем {0} не найден!
+questions.io.error=Ошибка ввода/вывода при работе с файлом!
+
+csv.file.name=ru.csv
diff --git a/src/main/resources/questions/en.csv b/src/main/resources/questions/en.csv
new file mode 100644
index 0000000..4d91327
--- /dev/null
+++ b/src/main/resources/questions/en.csv
@@ -0,0 +1,5 @@
+1,Which list is not a design pattern?,1. Facade,2. Object,3. Factory,4. Singleton,2
+2,Which type is not primitive?,1. String,2. int,3. char,4. boolean,1
+3,How many classes can be inherited in Java?,1. One,2. Two,3. Three,4. Four,1
+4,Which collection contains unique objects?,1. List,2. Map,3. Set,4. Queue,3
+5,What value cannot be put in the primitive type - int?,1. -1,2. 0,3. 10,4. null,4
\ No newline at end of file
diff --git a/src/main/resources/questions/ru.csv b/src/main/resources/questions/ru.csv
new file mode 100644
index 0000000..6114d64
--- /dev/null
+++ b/src/main/resources/questions/ru.csv
@@ -0,0 +1,5 @@
+1,Что из списка не является паттерном проектирования?,1. Фасад,2. Объект,3. Фабрика,4. Синглтон,2
+2,Какой тип не является примитивным?,1. String,2. int,3. char,4. boolean,1
+3,Сколько классов можно наследовать в Java?,1. Один,2. Два,3. Три,4. Четыре,1
+4,Какая коллекция содержит уникальне объекты?,1. List,2. Map,3. Set,4. Queue,3
+5,Какое значение нельзя положить в примитивный тип - int?,1. -1,2. 0,3. 10,4. null,4
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/services/AppTestConfig.java b/src/test/java/ru/otus/mkulikov/app/services/AppTestConfig.java
new file mode 100644
index 0000000..651d3aa
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/services/AppTestConfig.java
@@ -0,0 +1,20 @@
+package ru.otus.mkulikov.app.services;
+
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 25.05.2019
+ * Time: 16:51
+ */
+
+@SpringBootConfiguration
+@ComponentScan(basePackages = "ru.otus.mkulikov.app")
+@EnableConfigurationProperties
+public class AppTestConfig {
+
+
+}
diff --git a/src/test/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImplEnTest.java b/src/test/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImplEnTest.java
new file mode 100644
index 0000000..6266814
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImplEnTest.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.app.services.localisation;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.services.AppTestConfig;
+import ru.otus.mkulikov.app.config.LocaleProperties;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-22
+ * Time: 13:58
+ */
+
+@DisplayName("Класс LocalisationServiceImpl")
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AppTestConfig.class)
+@ActiveProfiles("en")
+class LocalisationServiceImplEnTest {
+
+ @Autowired
+ private LocaleProperties localeProperties;
+
+ @Test
+ @DisplayName("Проверка получения значения en локали")
+ void getValueEnTest() {
+ LocalisationService enLocale = new LocalisationServiceImpl(localeProperties);
+ String value = enLocale.getValue("test.key");
+
+ assertAll("value",
+ () -> assertNotNull(value),
+ () -> assertEquals("test123", value)
+ );
+ }
+
+ @Test
+ @DisplayName("Проверка получения значения en локали с параметром")
+ void getValueEnWithParamsTest() {
+ LocalisationService enLocale = new LocalisationServiceImpl(localeProperties);
+ String value = enLocale.getValueWithParams("test.key.params", new String[]{"Hel", "lo"});
+
+ assertAll("value",
+ () -> assertNotNull(value),
+ () -> assertEquals("Hel & lo - Hello", value)
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImplRuTest.java b/src/test/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImplRuTest.java
new file mode 100644
index 0000000..67d51ce
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/services/localisation/LocalisationServiceImplRuTest.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.app.services.localisation;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.services.AppTestConfig;
+import ru.otus.mkulikov.app.config.LocaleProperties;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-22
+ * Time: 13:58
+ */
+
+@DisplayName("Класс LocalisationServiceImpl")
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AppTestConfig.class)
+@ActiveProfiles("ru")
+class LocalisationServiceImplRuTest {
+
+ @Autowired
+ private LocaleProperties localeProperties;
+
+ @Test
+ @DisplayName("Проверка получения значения ru локали")
+ void getValueRuTest() {
+ LocalisationService ruLocale = new LocalisationServiceImpl(localeProperties);
+ String value = ruLocale.getValue("test.key");
+
+ assertAll("value",
+ () -> assertNotNull(value),
+ () -> assertEquals("тест123", value)
+ );
+ }
+
+ @Test
+ @DisplayName("Проверка получения значения ru локали с параметром")
+ void getValueRuWithParamsTest() {
+ LocalisationService ruLocale = new LocalisationServiceImpl(localeProperties);
+ String value = ruLocale.getValueWithParams("test.key.params", new String[]{"При", "вет"});
+
+ assertAll("value",
+ () -> assertNotNull(value),
+ () -> assertEquals("При и вет - Привет", value)
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/services/questions/QuestionsDAOTest.java b/src/test/java/ru/otus/mkulikov/app/services/questions/QuestionsDAOTest.java
new file mode 100644
index 0000000..58d5f16
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/services/questions/QuestionsDAOTest.java
@@ -0,0 +1,61 @@
+package ru.otus.mkulikov.app.services.questions;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.services.AppTestConfig;
+import ru.otus.mkulikov.app.config.LocaleProperties;
+import ru.otus.mkulikov.app.config.QuetionsProperties;
+import ru.otus.mkulikov.app.exceptions.QuestionsFileLoadingException;
+import ru.otus.mkulikov.app.models.Question;
+import ru.otus.mkulikov.app.services.localisation.LocalisationServiceImpl;
+import ru.otus.mkulikov.app.services.questions.dao.QuestionsDAO;
+import ru.otus.mkulikov.app.services.questions.dao.QuestionsDAOImpl;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-22
+ * Time: 13:58
+ */
+
+@DisplayName("Класс QuestionsDAO")
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AppTestConfig.class)
+@ActiveProfiles("ru")
+class QuestionsDAOTest {
+
+ @Autowired
+ private LocaleProperties localeProperties;
+ @Autowired
+ private QuetionsProperties quetionsProperties;
+
+ private final String c_test1 = "Тест1";
+ private final String c_test2 = "Тест2";
+ private final String c_test3 = "Тест3";
+
+ @Test
+ @DisplayName("Корректная загрузка вопросов")
+ public void loadQuestionsTest() throws QuestionsFileLoadingException {
+ LocalisationServiceImpl localisationService = new LocalisationServiceImpl(localeProperties);
+ QuestionsDAO questionsDAO = new QuestionsDAOImpl(localisationService, quetionsProperties);
+
+ List questions = questionsDAO.getQuestions();
+
+ assertAll("questions",
+ () -> assertNotNull(questions),
+ () -> assertEquals(3, questions.size()),
+ () -> assertEquals(c_test1, questions.get(0).getQuestion()),
+ () -> assertEquals(c_test2, questions.get(1).getQuestion()),
+ () -> assertEquals(c_test3, questions.get(2).getQuestion())
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/application-en.yml b/src/test/resources/application-en.yml
new file mode 100644
index 0000000..9d2007a
--- /dev/null
+++ b/src/test/resources/application-en.yml
@@ -0,0 +1,8 @@
+locale:
+ basename: "/locale/bundle"
+ defaultLocale: "en"
+ encoding: "UTF-8"
+
+questions:
+ default:
+ dir: "/questions"
diff --git a/src/test/resources/application-ru.yml b/src/test/resources/application-ru.yml
new file mode 100644
index 0000000..4bd742b
--- /dev/null
+++ b/src/test/resources/application-ru.yml
@@ -0,0 +1,8 @@
+locale:
+ basename: "/locale/bundle"
+ defaultLocale: "ru"
+ encoding: "UTF-8"
+
+questions:
+ default:
+ dir: "/questions"
diff --git a/src/test/resources/locale/bundle_en.properties b/src/test/resources/locale/bundle_en.properties
new file mode 100644
index 0000000..4c04204
--- /dev/null
+++ b/src/test/resources/locale/bundle_en.properties
@@ -0,0 +1,14 @@
+count.true.answers=Number of correct answers: {0} of {1}
+enter.answer=Enter answer number:
+enter.your.data=Enter your data
+enter.your.name=Name:
+enter.your.surname=Surname:
+hello.user=Hello, {0} {1}!
+questions.encoding.error=File encoding error!
+questions.find.error.filename=File named {0} not found!
+questions.io.error=Error I/O when working with a file!
+
+csv.file.name=en.csv
+
+test.key=test123
+test.key.params={0} & {1} - Hello
\ No newline at end of file
diff --git a/src/test/resources/locale/bundle_ru.properties b/src/test/resources/locale/bundle_ru.properties
new file mode 100644
index 0000000..2fc5bc9
--- /dev/null
+++ b/src/test/resources/locale/bundle_ru.properties
@@ -0,0 +1,14 @@
+count.true.answers=Количество правильных ответов: {0} из {1}
+enter.answer=Введите номер ответа:
+enter.your.data=Введите свои данные
+enter.your.name=Имя:
+enter.your.surname=Фамилия:
+hello.user=Здравствуйте, {0} {1}!
+questions.encoding.error=Ошибка кодировки файла!
+questions.find.error.filename=Файл с именем {0} не найден!
+questions.io.error=Ошибка ввода/вывода при работе с файлом!
+
+csv.file.name=ru.csv
+
+test.key=тест123
+test.key.params={0} и {1} - Привет
\ No newline at end of file
diff --git a/src/test/resources/questions/ru.csv b/src/test/resources/questions/ru.csv
new file mode 100644
index 0000000..47cc49f
--- /dev/null
+++ b/src/test/resources/questions/ru.csv
@@ -0,0 +1,3 @@
+1,Тест1,Ответ1,Ответ2,Ответ3,Ответ4,Ответ1
+2,Тест2,Ответ1,Ответ2,Ответ3,Ответ4,Ответ1
+3,Тест3,Ответ1,Ответ2,Ответ3,Ответ4,Ответ1