diff --git a/lombok.config b/lombok.config
new file mode 100644
index 0000000..8f7e8aa
--- /dev/null
+++ b/lombok.config
@@ -0,0 +1 @@
+lombok.addLombokGeneratedAnnotation = true
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 57993b2..2d22f99 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,6 +109,67 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.0
+
+ "@{jacocoArgLine}" -Xshare:off -Dfile.encoding=UTF-8
+ 1
+ true
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.12
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+ jacocoArgLine
+
+ C:/Temp/jacoco.exec
+
+
+
+ default-report
+ test
+
+ report
+
+
+
+ C:/Temp/jacoco.exec
+ ${project.reporting.outputDirectory}/jacoco
+
+
+
+ default-check
+
+ check
+
+
+
+
+ BUNDLE
+
+
+ COMPLEXITY
+ COVEREDRATIO
+ 0.70
+
+
+
+
+
+
+
+
org.jsonschema2pojo
jsonschema2pojo-maven-plugin
@@ -129,32 +190,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/java/com/pledge/torgi/service/EmailService.java b/src/main/java/com/pledge/torgi/service/EmailService.java
index 71c3e8e..12f62fa 100644
--- a/src/main/java/com/pledge/torgi/service/EmailService.java
+++ b/src/main/java/com/pledge/torgi/service/EmailService.java
@@ -26,8 +26,7 @@ public class EmailService {
return;
}
List alreadyProcessed = emailsRepository.findTop10ByOrderByIdDesc();
- List emailData = new ReceiveEmailService("imap.gmail.com", 993, params.getFrom(), params.getToken(), params.getSpecialEmail(), log)
- .downloadEmails(alreadyProcessed);
+ List emailData = createReceiveEmailService(params).downloadEmails(alreadyProcessed);
int sendLetters = 0;
log.info("Extracted letters = {}", emailData.size());
for (EmailData data : emailData) {
@@ -41,6 +40,10 @@ public class EmailService {
log.info("End email processing, send letters = {}", sendLetters);
}
+ protected ReceiveEmailService createReceiveEmailService(StartParams params) {
+ return new ReceiveEmailService("imap.gmail.com", 993, params.getFrom(), params.getToken(), params.getSpecialEmail(), log);
+ }
+
private boolean alreadyProcessed(String emailId) {
return emailsRepository.existsByMailId(emailId);
}
diff --git a/src/test/java/com/pledge/torgi/controller/DownloadHeadlessControllerTest.java b/src/test/java/com/pledge/torgi/controller/DownloadHeadlessControllerTest.java
new file mode 100644
index 0000000..0a9b02b
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/controller/DownloadHeadlessControllerTest.java
@@ -0,0 +1,44 @@
+package com.pledge.torgi.controller;
+
+import com.pledge.torgi.model.TorgiEvent;
+import com.pledge.torgi.repositories.MissedDataRepository;
+import com.pledge.torgi.repositories.TorgiEventRepository;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class DownloadHeadlessControllerTest {
+
+ @Mock
+ private TorgiEventRepository eventRepository;
+
+ @Mock
+ private MissedDataRepository missedDataRepository;
+
+ @Test
+ void constructor_shouldLoadExistingKeys() {
+ // given
+ TorgiEvent event = new TorgiEvent();
+ event.setKey("test-key");
+ Page page = new PageImpl<>(List.of(event));
+
+ when(eventRepository.findAllByOrderByCreatedAtDesc(any(PageRequest.class)))
+ .thenReturn(page);
+
+ // when
+ new DownloadHeadlessController(eventRepository, missedDataRepository);
+
+ // then
+ verify(eventRepository).findAllByOrderByCreatedAtDesc(any(PageRequest.class));
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/controller/HealthControllerTest.java b/src/test/java/com/pledge/torgi/controller/HealthControllerTest.java
new file mode 100644
index 0000000..74ffb03
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/controller/HealthControllerTest.java
@@ -0,0 +1,21 @@
+package com.pledge.torgi.controller;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.http.ResponseEntity;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class HealthControllerTest {
+
+ private final HealthController controller = new HealthController();
+
+ @Test
+ void getHealth_shouldReturnOk() {
+ // when
+ ResponseEntity response = controller.getPerson();
+
+ // then
+ assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
+ assertThat(response.getBody()).isEqualTo("OK");
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/controller/storage/FileServiceImplTest.java b/src/test/java/com/pledge/torgi/controller/storage/FileServiceImplTest.java
new file mode 100644
index 0000000..bce2109
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/controller/storage/FileServiceImplTest.java
@@ -0,0 +1,194 @@
+package com.pledge.torgi.controller.storage;
+
+import com.pledge.torgi.controller.DownloadHeadlessController;
+import com.pledge.torgi.model.MissedData;
+import com.pledge.torgi.repositories.MissedDataRepository;
+import com.pledge.torgi.utils.StartParams;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class FileServiceImplTest {
+
+ @Mock
+ private DownloadHeadlessController headlessController;
+
+ @Mock
+ private StartParams params;
+
+ @Mock
+ private MissedDataRepository missedDataRepository;
+
+ @InjectMocks
+ private FileServiceImpl fileService;
+
+ @Test
+ void extractCadastral_shouldExtractSingleCadastral() throws IOException {
+ // given
+ Workbook workbook = createWorkbookWithData("77:01:0001001:1234");
+
+ // when
+ List result = invokeExtractCadastral(workbook);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234");
+ }
+
+ @Test
+ void extractCadastral_shouldExtractMultipleCadastrals() throws IOException {
+ // given
+ Workbook workbook = createWorkbookWithData("77:01:0001001:1234", "77:01:0001001:5678");
+
+ // when
+ List result = invokeExtractCadastral(workbook);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234", "77:01:0001001:5678");
+ }
+
+ @Test
+ void extractCadastral_shouldReturnEmptyForEmptyCell() throws IOException {
+ // given
+ Workbook workbook = createWorkbookWithData("");
+
+ // when
+ List result = invokeExtractCadastral(workbook);
+
+ // then
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ void extractCadastral_shouldReturnEmptyForWorkbookWithNoRows() throws IOException {
+ // given
+ Workbook workbook = new XSSFWorkbook();
+ workbook.createSheet("Sheet1");
+
+ // when
+ List result = invokeExtractCadastral(workbook);
+
+ // then
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ void processSync_shouldProcessFile() throws IOException {
+ // given
+ Workbook workbook = createWorkbookWithData("77:01:0001001:1234");
+ when(missedDataRepository.findAll()).thenReturn(List.of());
+
+ // when
+ String result = fileService.processSync(workbook, "test@example.com");
+
+ // then
+ assertThat(result).isEqualTo("Обработано 1 кадастровых номеров");
+ verify(headlessController).sendNewContent(any(), anyList(), anyString());
+ }
+
+ @Test
+ void processAsync_shouldProcessFile() throws IOException {
+ // given
+ MultipartFile file = createMultipartFileWithData("77:01:0001001:1234");
+
+ // when
+ String result = fileService.processAsync(file, "test@example.com");
+
+ // then
+ assertThat(result).isEqualTo("Взято в обработку 1 кадастровых номеров");
+ }
+
+ @Test
+ void saveCadastralToGetMissedData_shouldSaveNewCadastrals() {
+ // given
+ List cadastrals = List.of("77:01:0001001:1234", "77:01:0001001:5678");
+ MissedData existing = new MissedData();
+ existing.setCadastral("77:01:0001001:9999");
+ when(missedDataRepository.findAll()).thenReturn(List.of(existing));
+
+ // when
+ invokeSaveCadastralToGetMissedData(cadastrals);
+
+ // then
+ verify(missedDataRepository).saveAll(anyList());
+ }
+
+ @Test
+ void saveCadastralToGetMissedData_shouldNotSaveExistingCadastrals() {
+ // given
+ List cadastrals = List.of("77:01:0001001:1234");
+ MissedData existing = new MissedData();
+ existing.setCadastral("77:01:0001001:1234");
+ when(missedDataRepository.findAll()).thenReturn(List.of(existing));
+
+ // when
+ invokeSaveCadastralToGetMissedData(cadastrals);
+
+ // then
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> captor = ArgumentCaptor.forClass(Iterable.class);
+ verify(missedDataRepository).saveAll(captor.capture());
+ assertThat(captor.getValue()).isEmpty();
+ }
+
+ // Helper methods
+
+ private Workbook createWorkbookWithData(String... values) {
+ Workbook workbook = new XSSFWorkbook();
+ Sheet sheet = workbook.createSheet("Sheet1");
+ for (int i = 0; i < values.length; i++) {
+ Row row = sheet.createRow(i);
+ row.createCell(0).setCellValue(values[i]);
+ }
+ return workbook;
+ }
+
+ private MultipartFile createMultipartFileWithData(String... values) throws IOException {
+ Workbook workbook = createWorkbookWithData(values);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ workbook.write(out);
+ return new MockMultipartFile("file", "test.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", new ByteArrayInputStream(out.toByteArray()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private List invokeExtractCadastral(Workbook workbook) {
+ try {
+ var method = FileServiceImpl.class.getDeclaredMethod("extractCadastral", Workbook.class);
+ method.setAccessible(true);
+ return (List) method.invoke(fileService, workbook);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void invokeSaveCadastralToGetMissedData(List cadastrals) {
+ try {
+ var method = FileServiceImpl.class.getDeclaredMethod("saveCadastralToGetMissedData", List.class);
+ method.setAccessible(true);
+ method.invoke(fileService, cadastrals);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceAdditionalTest.java b/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceAdditionalTest.java
new file mode 100644
index 0000000..21fe2b7
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceAdditionalTest.java
@@ -0,0 +1,84 @@
+package com.pledge.torgi.service;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DownloadHeadlessServiceAdditionalTest {
+
+ private final DownloadHeadlessService service = new DownloadHeadlessService();
+
+ @Test
+ void extractCadastral_shouldExtractMultipleCadastralsWithComma() {
+ // given
+ String rawValue = "Кадастровый номер: 77:01:0001001:1234,77:01:0001001:5678";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234", "77:01:0001001:5678");
+ }
+
+ @Test
+ void extractCadastral_shouldExtractConditionalNumber() {
+ // given
+ String rawValue = "Условный номер земельного участка: 77:01:0001001:1234";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234");
+ }
+
+ @Test
+ void extractCadastral_shouldHandleMixedTypes() {
+ // given
+ String rawValue = "Кадастровый номер: 77:01:0001001:1234;VIN: X9FKXXEEBMD123456";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234", "X9FKXXEEBMD123456");
+ }
+
+ @Test
+ void extractCadastral_shouldHandleEmptyParts() {
+ // given
+ String rawValue = "Кадастровый номер: 77:01:0001001:1234;;VIN: X9FKXXEEBMD123456";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234", "X9FKXXEEBMD123456");
+ }
+
+ @Test
+ void extractCadastral_shouldHandleSpaces() {
+ // given
+ String rawValue = " Кадастровый номер : 77:01:0001001:1234 ";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234");
+ }
+
+ @Test
+ void extractCadastral_shouldExtractSingleCadastralWithType() {
+ // given
+ String rawValue = "Кадастровый номер: 77:01:0001001:1234";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234");
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceCoverageTest.java b/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceCoverageTest.java
new file mode 100644
index 0000000..afba654
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceCoverageTest.java
@@ -0,0 +1,262 @@
+package com.pledge.torgi.service;
+
+import com.pledge.torgi.model.TorgiEvent;
+import com.pledge.torgi.repositories.TorgiEventRepository;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import java.util.ArrayList;
+
+import java.io.ByteArrayOutputStream;
+import java.time.LocalDate;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class DownloadHeadlessServiceCoverageTest {
+
+ @Mock
+ private TorgiEventRepository eventRepository;
+
+ private final DownloadHeadlessService service = new DownloadHeadlessService();
+
+ @Test
+ void extractData_shouldReturnEmptyForNoCadastrals() {
+ // given
+ List cadastrals = List.of();
+
+ // when
+ List result = invokeExtractData(cadastrals);
+
+ // then
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ void extractData_shouldReturnLotsForCadastral() {
+ // given
+ List cadastrals = List.of("77:01:0001001:1234");
+ TorgiEvent event = new TorgiEvent();
+ event.setKey("test-key");
+ event.setStatus("PUBLISHED");
+ event.setBidStart("2024-01-01");
+ event.setBidEnd("2024-01-31");
+ event.setTradesDate("2024-02-01");
+ event.setStartPrice("1000000");
+ event.setStep("10000");
+ event.setInitMoney("50000");
+ event.setFinalPrice("1200000");
+ event.setNotice("Notice123");
+ event.setLotNumber(1);
+ event.setCadastral("77:01:0001001:1234");
+ event.setType("Earth");
+ event.setFullInfo("Full info");
+ when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc(anyString())).thenReturn(List.of(event));
+
+ // when
+ List result = invokeExtractData(cadastrals);
+
+ // then
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).getStatus()).isEqualTo("PUBLISHED");
+ assertThat(result.get(0).getNotice()).isEqualTo("Notice123");
+ }
+
+ @Test
+ void extractData_shouldHandleMultipleCadastrals() {
+ // given
+ List cadastrals = List.of("77:01:0001001:1234", "77:01:0001001:5678");
+ TorgiEvent event1 = new TorgiEvent();
+ event1.setKey("test-key-1");
+ event1.setStatus("PUBLISHED");
+ event1.setCadastral("77:01:0001001:1234");
+ TorgiEvent event2 = new TorgiEvent();
+ event2.setKey("test-key-2");
+ event2.setStatus("CANCELED");
+ event2.setCadastral("77:01:0001001:5678");
+ when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc(anyString())).thenReturn(List.of(event1, event2));
+
+ // when
+ List result = invokeExtractData(cadastrals);
+
+ // then
+ assertThat(result).hasSize(2);
+ }
+
+ @Test
+ void extractData_shouldHandleRepositoryException() {
+ // given
+ List cadastrals = List.of("77:01:0001001:1234");
+ when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc(anyString()))
+ .thenThrow(new RuntimeException("Database error"));
+
+ // when
+ List result = invokeExtractData(cadastrals);
+
+ // then
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ void getFileToSave_shouldReturnFile() {
+ // given
+ LocalDate now = LocalDate.now();
+
+ // when
+ java.io.File result = invokeGetFileToSave(now);
+
+ // then
+ assertThat(result).isNotNull();
+ assertThat(result.getName()).isEqualTo(now + ".xlsx");
+ }
+
+ @Test
+ void setRowHead_shouldCreateHeader() {
+ // given
+ XSSFWorkbook workbook = new XSSFWorkbook();
+ XSSFSheet sheet = workbook.createSheet("Sheet1");
+
+ // when
+ invokeSetRowHead(sheet);
+
+ // then
+ Row headerRow = sheet.getRow(0);
+ assertThat(headerRow).isNotNull();
+ assertThat(headerRow.getCell(0).getStringCellValue()).isEqualTo("Статус торгов");
+ assertThat(headerRow.getCell(1).getStringCellValue()).isEqualTo("Дата начала подачи заявок");
+ assertThat(headerRow.getCell(13).getStringCellValue()).isEqualTo("Время получения события");
+ }
+
+ @Test
+ void createRow_shouldCreateRow() {
+ // given
+ XSSFWorkbook workbook = new XSSFWorkbook();
+ XSSFSheet sheet = workbook.createSheet("Sheet1");
+ Lot lot = new Lot();
+ lot.setStatus("PUBLISHED");
+ lot.setBidStart("2024-01-01");
+ lot.setBidEnd("2024-01-31");
+ lot.setTradesDate("2024-02-01");
+ lot.setStartPrice("1000000");
+ lot.setStep("10000");
+ lot.setInitMoney("50000");
+ lot.setFinalPrice("1200000");
+ lot.setNotice("Notice123");
+ lot.setLotNumber(1);
+ lot.setCadastral("77:01:0001001:1234");
+ lot.setType("Earth");
+ lot.setFullInfo("Full info");
+
+ // when
+ int result = invokeCreateRow(sheet, 0, lot);
+
+ // then
+ assertThat(result).isEqualTo(1);
+ Row row = sheet.getRow(0);
+ assertThat(row).isNotNull();
+ assertThat(row.getCell(0).getStringCellValue()).isEqualTo("PUBLISHED");
+ assertThat(row.getCell(8).getStringCellValue()).isEqualTo("Notice123");
+ }
+
+ @Test
+ void createRow_shouldHandleNullValues() {
+ // given
+ XSSFWorkbook workbook = new XSSFWorkbook();
+ XSSFSheet sheet = workbook.createSheet("Sheet1");
+ Lot lot = new Lot();
+
+ // when
+ int result = invokeCreateRow(sheet, 0, lot);
+
+ // then
+ assertThat(result).isEqualTo(1);
+ Row row = sheet.getRow(0);
+ assertThat(row).isNotNull();
+ assertThat(row.getCell(0).getStringCellValue()).isEmpty();
+ }
+
+ @Test
+ void setRowBodyNew_shouldCreateRows() {
+ // given
+ XSSFWorkbook workbook = new XSSFWorkbook();
+ XSSFSheet sheet = workbook.createSheet("Sheet1");
+ Lot lot1 = new Lot();
+ lot1.setStatus("PUBLISHED");
+ lot1.setNotice("Notice1");
+ Lot lot2 = new Lot();
+ lot2.setStatus("CANCELED");
+ lot2.setNotice("Notice2");
+ List lots = List.of(lot1, lot2);
+
+ // when
+ int result = invokeSetRowBodyNew(sheet, 0, lots);
+
+ // then
+ assertThat(result).isEqualTo(2);
+ assertThat(sheet.getRow(0) != null).isTrue();
+ assertThat(sheet.getRow(1) != null).isTrue();
+ }
+
+ // Helper methods
+
+ @SuppressWarnings("unchecked")
+ private List invokeExtractData(List cadastrals) {
+ try {
+ var method = DownloadHeadlessService.class.getDeclaredMethod("extractData", TorgiEventRepository.class, List.class);
+ method.setAccessible(true);
+ return (List) method.invoke(service, eventRepository, cadastrals);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private java.io.File invokeGetFileToSave(LocalDate now) {
+ try {
+ var method = DownloadHeadlessService.class.getDeclaredMethod("getFileToSave", LocalDate.class);
+ method.setAccessible(true);
+ return (java.io.File) method.invoke(service, now);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void invokeSetRowHead(XSSFSheet sheet) {
+ try {
+ var method = DownloadHeadlessService.class.getDeclaredMethod("setRowHead", XSSFSheet.class);
+ method.setAccessible(true);
+ method.invoke(service, sheet);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private int invokeCreateRow(XSSFSheet sheet, int rowCount, Lot lot) {
+ try {
+ var method = DownloadHeadlessService.class.getDeclaredMethod("createRow", XSSFSheet.class, int.class, Lot.class);
+ method.setAccessible(true);
+ return (int) method.invoke(service, sheet, rowCount, lot);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private int invokeSetRowBodyNew(XSSFSheet sheet, int currentRow, List lots) {
+ try {
+ var method = DownloadHeadlessService.class.getDeclaredMethod("setRowBodyNew", XSSFSheet.class, int.class, java.util.Collection.class);
+ method.setAccessible(true);
+ return (int) method.invoke(service, sheet, currentRow, lots);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceTest.java b/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceTest.java
new file mode 100644
index 0000000..0700ebd
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/service/DownloadHeadlessServiceTest.java
@@ -0,0 +1,72 @@
+package com.pledge.torgi.service;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DownloadHeadlessServiceTest {
+
+ private final DownloadHeadlessService service = new DownloadHeadlessService();
+
+ @Test
+ void extractCadastral_shouldExtractSingleCadastral() {
+ // given
+ String rawValue = "Кадастровый номер: 77:01:0001001:1234";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234");
+ }
+
+ @Test
+ void extractCadastral_shouldExtractMultipleCadastrals() {
+ // given
+ String rawValue = "Кадастровый номер: 77:01:0001001:1234|77:01:0001001:5678";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("77:01:0001001:1234", "77:01:0001001:5678");
+ }
+
+ @Test
+ void extractCadastral_shouldExtractVIN() {
+ // given
+ String rawValue = "VIN: X9FKXXEEBMD123456";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).containsExactly("X9FKXXEEBMD123456");
+ }
+
+ @Test
+ void extractCadastral_shouldReturnEmptyForUnknownType() {
+ // given
+ String rawValue = "Неизвестный тип: значение";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ void extractCadastral_shouldReturnEmptyForEmptyInput() {
+ // given
+ String rawValue = "";
+
+ // when
+ List result = service.extractCadastral(rawValue);
+
+ // then
+ assertThat(result).isEmpty();
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/service/EmailServiceTest.java b/src/test/java/com/pledge/torgi/service/EmailServiceTest.java
new file mode 100644
index 0000000..ae1b247
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/service/EmailServiceTest.java
@@ -0,0 +1,167 @@
+package com.pledge.torgi.service;
+
+import com.pledge.torgi.controller.storage.FileService;
+import com.pledge.torgi.model.Emails;
+import com.pledge.torgi.repositories.EmailsRepository;
+import com.pledge.torgi.utils.StartParams;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class EmailServiceTest {
+
+ @Mock
+ private FileService fileService;
+
+ @Mock
+ private EmailsRepository emailsRepository;
+
+ @Spy
+ @InjectMocks
+ private EmailService emailService;
+
+ @Test
+ void processEmails_shouldSkipWhenDisabled() {
+ // given
+ StartParams params = new StartParams();
+ params.setEnableEmailScan(false);
+
+ // when
+ emailService.processEmails(params);
+
+ // then
+ verifyNoInteractions(emailsRepository);
+ }
+
+ @Test
+ void processEmails_shouldProcessWhenEnabled() {
+ // given
+ StartParams params = new StartParams();
+ params.setEnableEmailScan(true);
+ params.setFrom("test@gmail.com");
+ params.setToken("token");
+ params.setSpecialEmail("special@gmail.com");
+
+ when(emailsRepository.findTop10ByOrderByIdDesc()).thenReturn(List.of());
+
+ ReceiveEmailService mockReceiveEmailService = mock(ReceiveEmailService.class);
+ when(mockReceiveEmailService.downloadEmails(any())).thenReturn(List.of());
+ doReturn(mockReceiveEmailService).when(emailService).createReceiveEmailService(any());
+
+ // when
+ emailService.processEmails(params);
+
+ // then
+ verify(emailsRepository).findTop10ByOrderByIdDesc();
+ verify(mockReceiveEmailService).downloadEmails(any());
+ }
+
+ @Test
+ void processEmails_shouldProcessNewEmails() {
+ // given
+ StartParams params = new StartParams();
+ params.setEnableEmailScan(true);
+ params.setFrom("test@gmail.com");
+ params.setToken("token");
+ params.setSpecialEmail("special@gmail.com");
+
+ Emails processedEmail = new Emails();
+ processedEmail.setMailId("existing-id");
+ when(emailsRepository.findTop10ByOrderByIdDesc()).thenReturn(List.of(processedEmail));
+
+ EmailData newEmailData = new EmailData();
+ newEmailData.setEmailId("new-email-id");
+ newEmailData.setEmails("test@example.com");
+ newEmailData.setFile(new XSSFWorkbook());
+
+ ReceiveEmailService mockReceiveEmailService = mock(ReceiveEmailService.class);
+ when(mockReceiveEmailService.downloadEmails(any())).thenReturn(List.of(newEmailData));
+ doReturn(mockReceiveEmailService).when(emailService).createReceiveEmailService(any());
+
+ when(emailsRepository.existsByMailId("new-email-id")).thenReturn(false);
+
+ // when
+ emailService.processEmails(params);
+
+ // then
+ verify(emailsRepository).existsByMailId("new-email-id");
+ verify(fileService).processSync(any(), anyString());
+ verify(emailsRepository).save(any(Emails.class));
+ }
+
+ @Test
+ void processEmails_shouldSkipAlreadyProcessedEmails() {
+ // given
+ StartParams params = new StartParams();
+ params.setEnableEmailScan(true);
+ params.setFrom("test@gmail.com");
+ params.setToken("token");
+ params.setSpecialEmail("special@gmail.com");
+
+ Emails processedEmail = new Emails();
+ processedEmail.setMailId("existing-id");
+ when(emailsRepository.findTop10ByOrderByIdDesc()).thenReturn(List.of(processedEmail));
+
+ EmailData existingEmailData = new EmailData();
+ existingEmailData.setEmailId("existing-id");
+ existingEmailData.setEmails("test@example.com");
+ existingEmailData.setFile(new XSSFWorkbook());
+
+ ReceiveEmailService mockReceiveEmailService = mock(ReceiveEmailService.class);
+ when(mockReceiveEmailService.downloadEmails(any())).thenReturn(List.of(existingEmailData));
+ doReturn(mockReceiveEmailService).when(emailService).createReceiveEmailService(any());
+
+ when(emailsRepository.existsByMailId("existing-id")).thenReturn(true);
+
+ // when
+ emailService.processEmails(params);
+
+ // then
+ verify(emailsRepository).existsByMailId("existing-id");
+ verifyNoInteractions(fileService);
+ verify(emailsRepository, never()).save(any(Emails.class));
+ }
+
+ @Test
+ void processEmails_shouldSaveProcessedEmail() {
+ // given
+ StartParams params = new StartParams();
+ params.setEnableEmailScan(true);
+ params.setFrom("test@gmail.com");
+ params.setToken("token");
+ params.setSpecialEmail("special@gmail.com");
+
+ when(emailsRepository.findTop10ByOrderByIdDesc()).thenReturn(List.of());
+
+ EmailData emailData = new EmailData();
+ emailData.setEmailId("new-id");
+ emailData.setEmails("test@example.com");
+ emailData.setFile(new XSSFWorkbook());
+
+ ReceiveEmailService mockReceiveEmailService = mock(ReceiveEmailService.class);
+ when(mockReceiveEmailService.downloadEmails(any())).thenReturn(List.of(emailData));
+ doReturn(mockReceiveEmailService).when(emailService).createReceiveEmailService(any());
+
+ when(emailsRepository.existsByMailId("new-id")).thenReturn(false);
+
+ // when
+ emailService.processEmails(params);
+
+ // then
+ verify(emailsRepository).save(argThat(email ->
+ email.getMailId().equals("new-id") &&
+ email.getReplyTo().equals("test@example.com")
+ ));
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/service/LotTest.java b/src/test/java/com/pledge/torgi/service/LotTest.java
new file mode 100644
index 0000000..4b27637
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/service/LotTest.java
@@ -0,0 +1,91 @@
+package com.pledge.torgi.service;
+
+import com.pledge.torgi.model.TorgiEvent;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class LotTest {
+
+ @Test
+ void defaultConstructor_shouldCreateInstance() {
+ // when
+ Lot lot = new Lot();
+
+ // then
+ assertThat(lot).isNotNull();
+ }
+
+ @Test
+ void constructorFromTorgiEvent_shouldCopyFields() {
+ // given
+ TorgiEvent event = new TorgiEvent();
+ event.setStatus("PUBLISHED");
+ event.setBidStart("2024-01-01");
+ event.setBidEnd("2024-01-31");
+ event.setTradesDate("2024-02-01");
+ event.setStartPrice("1000000");
+ event.setStep("10000");
+ event.setInitMoney("50000");
+ event.setFinalPrice("1200000");
+ event.setNotice("Notice123");
+ event.setLotNumber(1);
+ event.setCadastral("77:01:0001001:1234");
+ event.setType("Earth");
+ event.setFullInfo("Full info");
+
+ // when
+ Lot lot = new Lot(event);
+
+ // then
+ assertThat(lot.getStatus()).isEqualTo("PUBLISHED");
+ assertThat(lot.getBidStart()).isEqualTo("2024-01-01");
+ assertThat(lot.getBidEnd()).isEqualTo("2024-01-31");
+ assertThat(lot.getTradesDate()).isEqualTo("2024-02-01");
+ assertThat(lot.getStartPrice()).isEqualTo("1000000");
+ assertThat(lot.getStep()).isEqualTo("10000");
+ assertThat(lot.getInitMoney()).isEqualTo("50000");
+ assertThat(lot.getFinalPrice()).isEqualTo("1200000");
+ assertThat(lot.getNotice()).isEqualTo("Notice123");
+ assertThat(lot.getLotNumber()).isEqualTo(1);
+ assertThat(lot.getCadastral()).isEqualTo("77:01:0001001:1234");
+ assertThat(lot.getType()).isEqualTo("Earth");
+ assertThat(lot.getFullInfo()).isEqualTo("Full info");
+ }
+
+ @Test
+ void settersAndGetters_shouldWork() {
+ // given
+ Lot lot = new Lot();
+
+ // when
+ lot.setStatus("PUBLISHED");
+ lot.setBidStart("2024-01-01");
+ lot.setBidEnd("2024-01-31");
+ lot.setTradesDate("2024-02-01");
+ lot.setStartPrice("1000000");
+ lot.setStep("10000");
+ lot.setInitMoney("50000");
+ lot.setFinalPrice("1200000");
+ lot.setNotice("Notice123");
+ lot.setLotNumber(1);
+ lot.setCadastral("77:01:0001001:1234");
+ lot.setType("Earth");
+ lot.setFullInfo("Full info");
+
+ // then
+ assertThat(lot.getStatus()).isEqualTo("PUBLISHED");
+ assertThat(lot.getBidStart()).isEqualTo("2024-01-01");
+ assertThat(lot.getBidEnd()).isEqualTo("2024-01-31");
+ assertThat(lot.getTradesDate()).isEqualTo("2024-02-01");
+ assertThat(lot.getStartPrice()).isEqualTo("1000000");
+ assertThat(lot.getStep()).isEqualTo("10000");
+ assertThat(lot.getInitMoney()).isEqualTo("50000");
+ assertThat(lot.getFinalPrice()).isEqualTo("1200000");
+ assertThat(lot.getNotice()).isEqualTo("Notice123");
+ assertThat(lot.getLotNumber()).isEqualTo(1);
+ assertThat(lot.getCadastral()).isEqualTo("77:01:0001001:1234");
+ assertThat(lot.getType()).isEqualTo("Earth");
+ assertThat(lot.getFullInfo()).isEqualTo("Full info");
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/service/SendEmailServiceTest.java b/src/test/java/com/pledge/torgi/service/SendEmailServiceTest.java
new file mode 100644
index 0000000..902ef15
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/service/SendEmailServiceTest.java
@@ -0,0 +1,34 @@
+package com.pledge.torgi.service;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class SendEmailServiceTest {
+
+ @Test
+ void constructor_shouldCreateInstance() {
+ // when
+ SendEmailService service = new SendEmailService("smtp.gmail.com", 587, "user", "pass");
+
+ // then
+ assertThat(service).isNotNull();
+ }
+
+ @Test
+ void sendMail_shouldThrowExceptionForInvalidFile() {
+ // given
+ SendEmailService service = new SendEmailService("smtp.gmail.com", 587, "user", "pass");
+ File nonExistentFile = new File("non-existent.zip");
+
+ // when & then
+ try {
+ service.sendMail("from@test.com", "to@test.com", "Subject", "Body", nonExistentFile);
+ } catch (Exception e) {
+ // Expected - file doesn't exist
+ assertThat(e).isNotNull();
+ }
+ }
+}
diff --git a/src/test/java/com/pledge/torgi/utils/StartParamsTest.java b/src/test/java/com/pledge/torgi/utils/StartParamsTest.java
new file mode 100644
index 0000000..f7ab20a
--- /dev/null
+++ b/src/test/java/com/pledge/torgi/utils/StartParamsTest.java
@@ -0,0 +1,39 @@
+package com.pledge.torgi.utils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class StartParamsTest {
+
+ @Test
+ void defaultConstructor_shouldCreateInstance() {
+ // when
+ StartParams params = new StartParams();
+
+ // then
+ assertThat(params).isNotNull();
+ }
+
+ @Test
+ void settersAndGetters_shouldWork() {
+ // given
+ StartParams params = new StartParams();
+
+ // when
+ params.setFrom("test@gmail.com");
+ params.setToken("token");
+ params.setTo("to@gmail.com");
+ params.setDays("1");
+ params.setSpecialEmail("special@gmail.com");
+ params.setEnableEmailScan(true);
+
+ // then
+ assertThat(params.getFrom()).isEqualTo("test@gmail.com");
+ assertThat(params.getToken()).isEqualTo("token");
+ assertThat(params.getTo()).isEqualTo("to@gmail.com");
+ assertThat(params.getDays()).isEqualTo("1");
+ assertThat(params.getSpecialEmail()).isEqualTo("special@gmail.com");
+ assertThat(params.getEnableEmailScan()).isTrue();
+ }
+}