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