diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/.gitignore b/2020-02/spring-25-spring-batch/spring-batch-demo/.gitignore
new file mode 100644
index 00000000..04e0ae48
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/.gitignore
@@ -0,0 +1,6 @@
+.idea/
+*.iml
+
+target/
+
+output.csv
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/entries.csv b/2020-02/spring-25-spring-batch/spring-batch-demo/entries.csv
new file mode 100644
index 00000000..bde433ce
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/entries.csv
@@ -0,0 +1,16 @@
+Ivan,23
+John,24
+Ivan,23
+John,24
+Ivan,23
+Mary,24
+Ivan,23
+John,24
+Sunny,23
+John,24
+Ivan,23
+John,24
+Ivan,23
+John,24
+Ivan,23
+John,24
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/expected_output.dat b/2020-02/spring-25-spring-batch/spring-batch-demo/expected_output.dat
new file mode 100644
index 00000000..b4c926b1
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/expected_output.dat
@@ -0,0 +1,16 @@
+Person(name=Ivan, age=24)
+Person(name=John, age=25)
+Person(name=Ivan, age=24)
+Person(name=John, age=25)
+Person(name=Ivan, age=24)
+Person(name=Mary, age=25)
+Person(name=Ivan, age=24)
+Person(name=John, age=25)
+Person(name=Sunny, age=24)
+Person(name=John, age=25)
+Person(name=Ivan, age=24)
+Person(name=John, age=25)
+Person(name=Ivan, age=24)
+Person(name=John, age=25)
+Person(name=Ivan, age=24)
+Person(name=John, age=25)
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/pom.xml b/2020-02/spring-25-spring-batch/spring-batch-demo/pom.xml
new file mode 100644
index 00000000..bda68754
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/pom.xml
@@ -0,0 +1,113 @@
+
+
+ 4.0.0
+
+ ru.otus.example
+ spring-batch-demo
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.4.RELEASE
+
+
+
+ 13
+ 13
+ 13
+ 2.0.2
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-batch
+
+
+
+ com.h2database
+ h2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
+
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+ LATEST
+
+
+
+ com.github.cloudyrock.mongock
+ mongock-spring
+ ${mongock.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.1.8.RELEASE
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.springframework.shell
+ spring-shell-starter
+ 2.0.1.RELEASE
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.batch
+ spring-batch-test
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-jupiter.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit-jupiter.version}
+ test
+
+
+
+
+ spring-batch-demo
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/Main.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/Main.java
new file mode 100644
index 00000000..d2d0768d
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/Main.java
@@ -0,0 +1,17 @@
+package ru.otus.example.springbatch;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.PropertySource;
+
+import java.io.IOException;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) throws IOException {
+ SpringApplication.run(Main.class, args);
+ }
+}
+
+
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/chandgelogs/InitMongoDBDataChangeLog.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/chandgelogs/InitMongoDBDataChangeLog.java
new file mode 100644
index 00000000..274a0a86
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/chandgelogs/InitMongoDBDataChangeLog.java
@@ -0,0 +1,45 @@
+package ru.otus.example.springbatch.chandgelogs;
+
+import com.github.cloudyrock.mongock.ChangeLog;
+import com.github.cloudyrock.mongock.ChangeSet;
+import com.mongodb.client.MongoDatabase;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import ru.otus.example.springbatch.model.Person;
+
+@ChangeLog(order = "001")
+public class InitMongoDBDataChangeLog {
+
+ @ChangeSet(order = "000", id = "dropDB", author = "stvort", runAlways = true)
+ public void dropDB(MongoDatabase database){
+ database.drop();
+ }
+
+ @ChangeSet(order = "001", id = "initPersons", author = "stvort", runAlways = true)
+ public void initPersons(MongoTemplate template){
+ template.save(new Person("John", 21));
+ template.save(new Person("Ivan", 32));
+ template.save(new Person("Dima", 52));
+ template.save(new Person("Mike", 22));
+ template.save(new Person("Duke", 33));
+ template.save(new Person("John", 21));
+ template.save(new Person("Ivan", 32));
+ template.save(new Person("Dima", 52));
+ template.save(new Person("Mike", 22));
+ template.save(new Person("Duke", 33));
+ template.save(new Person("John", 21));
+ template.save(new Person("Ivan", 32));
+ template.save(new Person("Dima", 52));
+ template.save(new Person("Mike", 22));
+ template.save(new Person("Duke", 33));
+ template.save(new Person("John", 21));
+ template.save(new Person("Ivan", 32));
+ template.save(new Person("Dima", 52));
+ template.save(new Person("Mike", 22));
+ template.save(new Person("Duke", 33));
+ template.save(new Person("John", 21));
+ template.save(new Person("Ivan", 32));
+ template.save(new Person("Dima", 52));
+ template.save(new Person("Mike", 22));
+ template.save(new Person("Duke", 33));
+ }
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/AppProps.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/AppProps.java
new file mode 100644
index 00000000..1e704a2d
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/AppProps.java
@@ -0,0 +1,21 @@
+package ru.otus.example.springbatch.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+public class AppProps {
+
+ @Value("${spring.data.mongodb.database}")
+ private String mongoDBName;
+
+ @Value("${app.input-file:''}")
+ private String inputFileName;
+
+ @Value("${app.output-file:''}")
+ private String outputFileName;
+
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/BatchConfig.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/BatchConfig.java
new file mode 100644
index 00000000..c2649770
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/BatchConfig.java
@@ -0,0 +1,43 @@
+package ru.otus.example.springbatch.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.batch.core.*;
+import org.springframework.batch.core.configuration.JobRegistry;
+import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
+import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
+import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
+import org.springframework.batch.core.job.flow.FlowJob;
+import org.springframework.batch.core.launch.support.RunIdIncrementer;
+import org.springframework.batch.core.scope.context.ChunkContext;
+import org.springframework.batch.item.ItemProcessor;
+import org.springframework.batch.item.ItemReader;
+import org.springframework.batch.item.file.FlatFileItemReader;
+import org.springframework.batch.item.file.FlatFileItemWriter;
+import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
+import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
+import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.transaction.PlatformTransactionManager;
+import ru.otus.example.springbatch.model.Person;
+import ru.otus.example.springbatch.service.HappyBirthdayService;
+
+import java.util.List;
+
+@EnableBatchProcessing
+@Configuration
+public class BatchConfig {
+ @Bean
+ public JobRegistryBeanPostProcessor postProcessor(JobRegistry jobRegistry) {
+ var processor = new JobRegistryBeanPostProcessor();
+ processor.setJobRegistry(jobRegistry);
+ return processor;
+ }
+
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/JobConfig.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/JobConfig.java
new file mode 100644
index 00000000..edaf8645
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/JobConfig.java
@@ -0,0 +1,138 @@
+package ru.otus.example.springbatch.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.batch.core.*;
+import org.springframework.batch.core.configuration.JobRegistry;
+import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
+import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
+import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
+import org.springframework.batch.core.launch.support.RunIdIncrementer;
+import org.springframework.batch.core.scope.context.ChunkContext;
+import org.springframework.batch.item.ItemProcessor;
+import org.springframework.batch.item.ItemReader;
+import org.springframework.batch.item.file.FlatFileItemReader;
+import org.springframework.batch.item.file.FlatFileItemWriter;
+import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
+import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
+import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.FileSystemResource;
+import ru.otus.example.springbatch.model.Person;
+import ru.otus.example.springbatch.service.HappyBirthdayService;
+
+import java.util.List;
+
+@SuppressWarnings("all")
+@Configuration
+public class JobConfig {
+ private static final int CHUNK_SIZE = 5;
+ private final Logger logger = LoggerFactory.getLogger("Batch");
+
+ public static final String OUTPUT_FILE_NAME = "outputFileName";
+ public static final String INPUT_FILE_NAME = "inputFileName";
+ public static final String IMPORT_USER_JOB_NAME = "importUserJob";
+
+ @Autowired
+ private JobBuilderFactory jobBuilderFactory;
+
+ @Autowired
+ private StepBuilderFactory stepBuilderFactory;
+
+ @StepScope
+ @Bean
+ public FlatFileItemReader reader(@Value("#{jobParameters['" + INPUT_FILE_NAME + "']}") String inputFileName) {
+ return new FlatFileItemReaderBuilder()
+ .name("personItemReader")
+ .resource(new FileSystemResource(inputFileName))
+
+ // Работа через lineMapper
+ .lineMapper((s, i) -> {
+ String[] fieldsValues = s.split(",");
+ return new Person(fieldsValues[0], Integer.parseInt(fieldsValues[1]));
+ })
+/*
+ // Работа через fieldSetMapper
+ .delimited()
+ .names("name", "age")
+ .fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
+ setTargetType(Person.class);
+ }})
+*/
+ .build();
+ }
+
+
+
+ @StepScope
+ @Bean
+ public ItemProcessor processor(HappyBirthdayService happyBirthdayService) {
+ return (ItemProcessor) happyBirthdayService::doHappyBirthday;
+ }
+
+ @StepScope
+ @Bean
+ public FlatFileItemWriter writer(@Value("#{jobParameters['" + OUTPUT_FILE_NAME + "']}") String outputFileName) {
+ return new FlatFileItemWriterBuilder<>()
+ .name("personItemWriter")
+ .resource(new FileSystemResource(outputFileName))
+ .lineAggregator(new DelimitedLineAggregator<>())
+ .build();
+ }
+
+ @Bean
+ public Job importUserJob(Step step1) {
+ return jobBuilderFactory.get(IMPORT_USER_JOB_NAME)
+ .incrementer(new RunIdIncrementer())
+ .flow(step1)
+ .end()
+ .listener(new JobExecutionListener() {
+ @Override
+ public void beforeJob(JobExecution jobExecution) {
+ logger.info("Начало job");
+ }
+
+ @Override
+ public void afterJob(JobExecution jobExecution) {
+ logger.info("Конец job");
+ }
+ })
+ .build();
+ }
+
+ @Bean
+ public Step step1(FlatFileItemWriter writer, ItemReader reader, ItemProcessor itemProcessor) {
+ return stepBuilderFactory.get("step1")
+ .chunk(CHUNK_SIZE)
+ .reader(reader)
+ .processor(itemProcessor)
+ .writer(writer)
+ .listener(new ItemReadListener() {
+ public void beforeRead() { logger.info("Начало чтения"); }
+ public void afterRead(Object o) { logger.info("Конец чтения"); }
+ public void onReadError(Exception e) { logger.info("Ошибка чтения"); }
+ })
+ .listener(new ItemWriteListener() {
+ public void beforeWrite(List list) { logger.info("Начало записи"); }
+ public void afterWrite(List list) { logger.info("Конец записи"); }
+ public void onWriteError(Exception e, List list) { logger.info("Ошибка записи"); }
+ })
+ .listener(new ItemProcessListener() {
+ public void beforeProcess(Object o) {logger.info("Начало обработки");}
+ public void afterProcess(Object o, Object o2) {logger.info("Конец обработки");}
+ public void onProcessError(Object o, Exception e) {logger.info("Ошбка обработки");}
+ })
+ .listener(new ChunkListener() {
+ public void beforeChunk(ChunkContext chunkContext) {logger.info("Начало пачки");}
+ public void afterChunk(ChunkContext chunkContext) {logger.info("Конец пачки");}
+ public void afterChunkError(ChunkContext chunkContext) {logger.info("Ошибка пачки");}
+ })
+// .taskExecutor(new SimpleAsyncTaskExecutor())
+ .build();
+ }
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/MongoConfig.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/MongoConfig.java
new file mode 100644
index 00000000..9d7eb407
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/MongoConfig.java
@@ -0,0 +1,24 @@
+package ru.otus.example.springbatch.config;
+
+import com.github.cloudyrock.mongock.Mongock;
+import com.github.cloudyrock.mongock.SpringMongockBuilder;
+import com.mongodb.MongoClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import ru.otus.example.springbatch.chandgelogs.InitMongoDBDataChangeLog;
+
+@Configuration
+public class MongoConfig {
+
+ @Autowired
+ private AppProps appProps;
+
+ @Bean
+ public Mongock mongock(MongoClient mongoClient) {
+ return new SpringMongockBuilder(mongoClient, appProps.getMongoDBName(),
+ InitMongoDBDataChangeLog.class.getPackageName())
+ .build();
+ }
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/model/Person.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/model/Person.java
new file mode 100644
index 00000000..b95cb1ff
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/model/Person.java
@@ -0,0 +1,13 @@
+package ru.otus.example.springbatch.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class Person {
+ private String name;
+ private int age;
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/service/HappyBirthdayService.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/service/HappyBirthdayService.java
new file mode 100644
index 00000000..00ec7287
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/service/HappyBirthdayService.java
@@ -0,0 +1,13 @@
+package ru.otus.example.springbatch.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.example.springbatch.model.Person;
+
+@Service
+public class HappyBirthdayService {
+
+ public Person doHappyBirthday(Person person){
+ person.setAge(person.getAge() + 1);
+ return person;
+ }
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/shell/BatchCommands.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/shell/BatchCommands.java
new file mode 100644
index 00000000..0e0cfea1
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/shell/BatchCommands.java
@@ -0,0 +1,56 @@
+package ru.otus.example.springbatch.shell;
+
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.explore.JobExplorer;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.launch.JobOperator;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import ru.otus.example.springbatch.config.AppProps;
+
+
+import static ru.otus.example.springbatch.config.JobConfig.*;
+
+@RequiredArgsConstructor
+@ShellComponent
+public class BatchCommands {
+
+ private final AppProps appProps;
+ private final Job importUserJob;
+
+ private final JobLauncher jobLauncher;
+ private final JobOperator jobOperator;
+ private final JobExplorer jobExplorer;
+
+ //http://localhost:8080/h2-console/
+
+ @SneakyThrows
+ @ShellMethod(value = "startMigrationJobWithJobLauncher", key = "sm-jl")
+ public void startMigrationJobWithJobLauncher() {
+ JobExecution execution = jobLauncher.run(importUserJob, new JobParametersBuilder()
+ .addString(INPUT_FILE_NAME, appProps.getInputFileName())
+ .addString(OUTPUT_FILE_NAME, appProps.getOutputFileName())
+ .toJobParameters());
+ System.out.println(execution);
+ }
+
+ @SneakyThrows
+ @ShellMethod(value = "startMigrationJobWithJobOperator", key = "sm-jo")
+ public void startMigrationJobWithJobOperator() {
+ Long executionId = jobOperator.start(IMPORT_USER_JOB_NAME,
+ INPUT_FILE_NAME + "=" + appProps.getInputFileName() + "\n" +
+ OUTPUT_FILE_NAME + "=" + appProps.getOutputFileName()
+ );
+ System.out.println(jobOperator.getSummary(executionId));
+ }
+
+ @ShellMethod(value = "showInfo", key = "i")
+ public void showInfo() {
+ System.out.println(jobExplorer.getJobNames());
+ System.out.println(jobExplorer.getLastJobInstance(IMPORT_USER_JOB_NAME));
+ }
+}
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/resources/application.yml b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/resources/application.yml
new file mode 100644
index 00000000..a9bbf414
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/main/resources/application.yml
@@ -0,0 +1,31 @@
+spring:
+ batch:
+ job:
+ enabled: false
+ shell:
+ interactive:
+ enabled: true
+
+ datasource:
+ url: jdbc:h2:mem:testdb
+ driverClassName: org.h2.Driver
+ username: sa
+ password:
+
+ h2:
+ console:
+ enabled: true
+ path: /h2-console
+
+ data:
+ mongodb:
+ uri: mongodb://localhost
+ port: 0 #27017
+ database: SpringBatchExampleDB
+
+app:
+ ages-count-to-add: 1
+ input-file: entries.csv
+ output-file: output.dat
+
+#debug: true
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/test/java/ru/otus/example/springbatch/config/ImportUserJobTest.java b/2020-02/spring-25-spring-batch/spring-batch-demo/src/test/java/ru/otus/example/springbatch/config/ImportUserJobTest.java
new file mode 100644
index 00000000..5a9fa468
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/test/java/ru/otus/example/springbatch/config/ImportUserJobTest.java
@@ -0,0 +1,59 @@
+package ru.otus.example.springbatch.config;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.test.AssertFile;
+import org.springframework.batch.test.JobLauncherTestUtils;
+import org.springframework.batch.test.JobRepositoryTestUtils;
+import org.springframework.batch.test.context.SpringBatchTest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.core.io.FileSystemResource;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static ru.otus.example.springbatch.config.JobConfig.*;
+
+@SpringBootTest
+@SpringBatchTest
+class ImportUserJobTest {
+
+ private static final String TEST_INPUT_FILE_NAME = "entries.csv";
+ private static final String EXPECTED_OUTPUT_FILE_NAME = "expected_output.dat";
+ private static final String TEST_OUTPUT_FILE_NAME = "test_output.dat";
+
+ @Autowired
+ private JobLauncherTestUtils jobLauncherTestUtils;
+
+ @Autowired
+ private JobRepositoryTestUtils jobRepositoryTestUtils;
+
+ @BeforeEach
+ void clearMetaData() {
+ jobRepositoryTestUtils.removeJobExecutions();
+ }
+
+ @Test
+ void testJob() throws Exception {
+ FileSystemResource expectedResult = new FileSystemResource(EXPECTED_OUTPUT_FILE_NAME);
+ FileSystemResource actualResult = new FileSystemResource(TEST_OUTPUT_FILE_NAME);
+
+ Job job = jobLauncherTestUtils.getJob();
+ assertThat(job).isNotNull()
+ .extracting(Job::getName)
+ .isEqualTo(IMPORT_USER_JOB_NAME);
+
+ JobParameters parameters = new JobParametersBuilder()
+ .addString(INPUT_FILE_NAME, TEST_INPUT_FILE_NAME)
+ .addString(OUTPUT_FILE_NAME, TEST_OUTPUT_FILE_NAME)
+ .toJobParameters();
+ JobExecution jobExecution = jobLauncherTestUtils.launchJob(parameters);
+
+ assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo("COMPLETED");
+ AssertFile.assertFileEquals(expectedResult, actualResult);
+ }
+}
\ No newline at end of file
diff --git a/2020-02/spring-25-spring-batch/spring-batch-demo/src/test/resources/application.yml b/2020-02/spring-25-spring-batch/spring-batch-demo/src/test/resources/application.yml
new file mode 100644
index 00000000..61fa4c4b
--- /dev/null
+++ b/2020-02/spring-25-spring-batch/spring-batch-demo/src/test/resources/application.yml
@@ -0,0 +1,19 @@
+spring:
+ batch:
+ job:
+ enabled: false
+ shell:
+ interactive:
+ enabled: false
+
+ datasource:
+ url: jdbc:h2:mem:testdb
+ driverClassName: org.h2.Driver
+ username: sa
+ password:
+
+ data:
+ mongodb:
+ uri: mongodb://localhost
+ port: 0
+ database: SpringBatchTestExampleDB
\ No newline at end of file