diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/.gitignore b/2021-02/spring-26-spring-batch/spring-batch-demo/.gitignore
new file mode 100644
index 00000000..04e0ae48
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/.gitignore
@@ -0,0 +1,6 @@
+.idea/
+*.iml
+
+target/
+
+output.csv
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/entries.csv b/2021-02/spring-26-spring-batch/spring-batch-demo/entries.csv
new file mode 100644
index 00000000..bde433ce
--- /dev/null
+++ b/2021-02/spring-26-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/2021-02/spring-26-spring-batch/spring-batch-demo/pom.xml b/2021-02/spring-26-spring-batch/spring-batch-demo/pom.xml
new file mode 100644
index 00000000..35220a75
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/pom.xml
@@ -0,0 +1,128 @@
+
+
+ 4.0.0
+
+ ru.otus.example
+ spring-batch-demo
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.5
+
+
+
+ 11
+ 11
+ 11
+
+
+ 4.1.19
+ 2.0.0
+
+
+
+
+
+ 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
+ ${flapdoodle.version}
+ test
+
+
+
+ com.github.cloudyrock.mongock
+ mongock-spring-v5
+ ${mongock.version}
+
+
+
+ com.github.cloudyrock.mongock
+ mongodb-springdata-v3-driver
+ ${mongock.version}
+
+
+
+
+
+ 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/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/Main.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/Main.java
new file mode 100644
index 00000000..00a0d2d1
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/Main.java
@@ -0,0 +1,19 @@
+package ru.otus.example.springbatch;
+
+import com.github.cloudyrock.spring.v5.EnableMongock;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.PropertySource;
+
+import java.io.IOException;
+
+@EnableMongock
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+}
+
+
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/chandgelogs/InitMongoDBDataChangeLog.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/chandgelogs/InitMongoDBDataChangeLog.java
new file mode 100644
index 00000000..feb52399
--- /dev/null
+++ b/2021-02/spring-26-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.github.cloudyrock.mongock.driver.mongodb.springdata.v3.decorator.impl.MongockTemplate;
+import com.mongodb.client.MongoDatabase;
+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(MongockTemplate template){
+ template.save(new Person("Джон", 21));
+ template.save(new Person("Игорь", 32));
+ template.save(new Person("Дмитрий", 52));
+ template.save(new Person("Михаил", 22));
+ template.save(new Person("Герман", 33));
+ template.save(new Person("Джон", 21));
+ template.save(new Person("Игорь", 32));
+ template.save(new Person("Дмитрий", 52));
+ template.save(new Person("Михаил", 22));
+ template.save(new Person("Герман", 33));
+ template.save(new Person("Джон", 21));
+ template.save(new Person("Игорь", 32));
+ template.save(new Person("Дмитрий", 52));
+ template.save(new Person("Михаил", 22));
+ template.save(new Person("Герман", 33));
+ template.save(new Person("Джон", 21));
+ template.save(new Person("Игорь", 32));
+ template.save(new Person("Дмитрий", 52));
+ template.save(new Person("Михаил", 22));
+ template.save(new Person("Герман", 33));
+ template.save(new Person("Джон", 21));
+ template.save(new Person("Игорь", 32));
+ template.save(new Person("Дмитрий", 52));
+ template.save(new Person("Михаил", 22));
+ template.save(new Person("Герман", 33));
+ }
+}
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/AppProps.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/AppProps.java
new file mode 100644
index 00000000..5d9deb61
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/AppProps.java
@@ -0,0 +1,14 @@
+package ru.otus.example.springbatch.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties("app")
+public class AppProps {
+ private String inputFile;
+ private String outputFile;
+
+}
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/BatchConfig.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/BatchConfig.java
new file mode 100644
index 00000000..54525aea
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/BatchConfig.java
@@ -0,0 +1,19 @@
+package ru.otus.example.springbatch.config;
+
+import org.springframework.batch.core.configuration.JobRegistry;
+import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
+import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@EnableBatchProcessing
+@Configuration
+public class BatchConfig {
+ @Bean
+ public JobRegistryBeanPostProcessor postProcessor(JobRegistry jobRegistry) {
+ var processor = new JobRegistryBeanPostProcessor();
+ processor.setJobRegistry(jobRegistry);
+ return processor;
+ }
+
+}
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/JobConfig.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/JobConfig.java
new file mode 100644
index 00000000..4692b6dc
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/config/JobConfig.java
@@ -0,0 +1,167 @@
+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.annotation.JobBuilderFactory;
+import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/model/Person.java b/2021-02/spring-26-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/2021-02/spring-26-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/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/service/HappyBirthdayService.java b/2021-02/spring-26-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/2021-02/spring-26-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/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/shell/BatchCommands.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/shell/BatchCommands.java
new file mode 100644
index 00000000..0e4296c4
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/java/ru/otus/example/springbatch/shell/BatchCommands.java
@@ -0,0 +1,60 @@
+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.JobParametersInvalidException;
+import org.springframework.batch.core.explore.JobExplorer;
+import org.springframework.batch.core.launch.JobInstanceAlreadyExistsException;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.launch.JobOperator;
+import org.springframework.batch.core.launch.NoSuchJobException;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRestartException;
+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/
+
+ @ShellMethod(value = "startMigrationJobWithJobLauncher", key = "sm-jl")
+ public void startMigrationJobWithJobLauncher() throws Exception {
+ JobExecution execution = jobLauncher.run(importUserJob, new JobParametersBuilder()
+ .addString(INPUT_FILE_NAME, appProps.getInputFile())
+ .addString(OUTPUT_FILE_NAME, appProps.getOutputFile())
+ .toJobParameters());
+ System.out.println(execution);
+ }
+
+ @ShellMethod(value = "startMigrationJobWithJobOperator", key = "sm-jo")
+ public void startMigrationJobWithJobOperator() throws Exception {
+ Long executionId = jobOperator.start(IMPORT_USER_JOB_NAME,
+ INPUT_FILE_NAME + "=" + appProps.getInputFile() + "\n" +
+ OUTPUT_FILE_NAME + "=" + appProps.getOutputFile()
+ );
+ 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/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/resources/application.yml b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/resources/application.yml
new file mode 100644
index 00000000..e602be05
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/main/resources/application.yml
@@ -0,0 +1,36 @@
+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:
+ host: localhost
+ port: 27017
+ database: SpringBatchExampleDB
+
+mongock:
+ runner-type: InitializingBean
+ change-logs-scan-package:
+ - ru.otus.example.springbatch.chandgelogs
+
+app:
+ ages-count-to-add: 1
+ input-file: entries.csv
+ output-file: output.dat
+
+#debug: true
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/java/ru/otus/example/springbatch/config/ImportUserJobTest.java b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/java/ru/otus/example/springbatch/config/ImportUserJobTest.java
new file mode 100644
index 00000000..97b1b035
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/java/ru/otus/example/springbatch/config/ImportUserJobTest.java
@@ -0,0 +1,73 @@
+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 java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+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 = "test-entries.csv";
+ private static final String EXPECTED_OUTPUT_FILE_NAME = "expected-test-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 {
+ var classLoader = ImportUserJobTest.class.getClassLoader();
+ var testInputFileName = URLDecoder.decode(
+ Objects.requireNonNull(classLoader.getResource(TEST_INPUT_FILE_NAME)).getFile(),
+ StandardCharsets.UTF_8
+ );
+ var expectedResultFileName = URLDecoder.decode(
+ Objects.requireNonNull(classLoader.getResource(EXPECTED_OUTPUT_FILE_NAME)).getFile(),
+ StandardCharsets.UTF_8
+ );
+
+ FileSystemResource expectedResult = new FileSystemResource(expectedResultFileName);
+ 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, testInputFileName)
+ .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/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/application.yml b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/application.yml
new file mode 100644
index 00000000..49c12c2e
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/application.yml
@@ -0,0 +1,24 @@
+spring:
+ batch:
+ job:
+ enabled: false
+ shell:
+ interactive:
+ enabled: false
+
+ datasource:
+ url: jdbc:h2:mem:testdb
+ driverClassName: org.h2.Driver
+ username: sa
+ password:
+
+ data:
+ mongodb:
+ host: localhost
+ port: 0
+ database: SpringBatchTestExampleDB
+
+mongock:
+ runner-type: InitializingBean
+ change-logs-scan-package:
+ - ru.otus.example.springbatch.chandgelogs
\ No newline at end of file
diff --git a/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/expected-test-output.dat b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/expected-test-output.dat
new file mode 100644
index 00000000..b4c926b1
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/expected-test-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/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/test-entries.csv b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/test-entries.csv
new file mode 100644
index 00000000..bde433ce
--- /dev/null
+++ b/2021-02/spring-26-spring-batch/spring-batch-demo/src/test/resources/test-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