Compare commits

...

9 Commits

Author SHA1 Message Date
RM
b1e79e1e2b added integration test
use spring-retry instead of custom
add logging
2026-10-05 20:09:26 +03:00
RM
9813f2489e second tests 2026-10-05 14:58:33 +03:00
RM
db7bcd1d54 first tests 2026-10-03 02:47:02 +03:00
RM
f511e6ed77 change logging 2026-10-01 22:09:28 +03:00
RM
df09da42ef use jre base image 2026-10-01 21:55:24 +03:00
RM
f94cb5460d add business events logger 2026-10-01 21:45:00 +03:00
RM
c6183a88ef remove sneaky exceptions 2026-10-01 21:29:56 +03:00
RM
01ea006d95 remove magic numbers, dry code 2026-10-01 21:25:47 +03:00
RM
1de0988da9 clean file 2026-10-01 21:10:53 +03:00
19 changed files with 1670 additions and 209 deletions

View File

@@ -1,3 +1,3 @@
FROM amazoncorretto:26-alpine-jdk
FROM eclipse-temurin:26-jre-alpine
COPY torgi-0.0.1-SNAPSHOT.jar /home/torgi-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75", "-XX:InitialRAMPercentage=50", "-Xms512m", "-Xmx4096m", "-jar", "/home/torgi-0.0.1-SNAPSHOT.jar"]

View File

@@ -10,7 +10,6 @@ services:
depends_on:
- service-db
environment:
- SERVER_PORT=8181
- SERVER_PORT=8181
- SPRING_CLOUD_CONFIG_URI=http://192.168.2.22:10022
- SPRING_CLOUD_CONFIG_PROFILE=prod

1
lombok.config Normal file
View File

@@ -0,0 +1 @@
lombok.addLombokGeneratedAnnotation = true

110
pom.xml
View File

@@ -29,6 +29,7 @@
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
<testcontainers.version>1.21.4</testcontainers.version>
</properties>
<dependencies>
<dependency>
@@ -57,11 +58,31 @@
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Testcontainers -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
@@ -109,6 +130,67 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<argLine>"@{jacocoArgLine}" -Xshare:off -Dfile.encoding=UTF-8</argLine>
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>jacocoArgLine</propertyName>
<!-- Сохраняем файл в корень диска C в папку Temp, где нет кириллицы -->
<destFile>C:/Temp/jacoco.exec</destFile>
</configuration>
</execution>
<execution>
<id>default-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Читаем файл из безопасного места -->
<dataFile>C:/Temp/jacoco.exec</dataFile>
<outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
</configuration>
</execution>
<execution>
<id>default-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.70</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jsonschema2pojo</groupId>
<artifactId>jsonschema2pojo-maven-plugin</artifactId>
@@ -129,32 +211,4 @@
</plugins>
</build>
<!-- <build>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <artifactId>maven-assembly-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>package</phase>-->
<!-- <goals>-->
<!-- <goal>single</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- <configuration>-->
<!-- <archive>-->
<!-- <manifest>-->
<!-- <addClasspath>true</addClasspath>-->
<!-- <mainClass>com.pledge.torgi.MainApplication</mainClass>-->
<!-- </manifest>-->
<!-- </archive>-->
<!-- <descriptorRefs>-->
<!-- <descriptorRef>jar-with-dependencies</descriptorRef>-->
<!-- </descriptorRefs>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- -->
<!-- </plugins>-->
<!-- </build>-->
</project>

View File

@@ -5,11 +5,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@EnableJpaRepositories
@EnableJpaAuditing
@EnableConfigurationProperties
@EnableRetry
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);

View File

@@ -1,16 +1,13 @@
package com.pledge.torgi.controller;
import com.pledge.torgi.model.Configs;
import com.pledge.torgi.model.MissedData;
import com.pledge.torgi.model.TorgiEvent;
import com.pledge.torgi.repositories.ConfigsRepository;
import com.pledge.torgi.repositories.MissedDataRepository;
import com.pledge.torgi.repositories.TorgiEventRepository;
import com.pledge.torgi.service.DownloadHeadlessService;
import com.pledge.torgi.service.Lot;
import com.pledge.torgi.service.SendEmailService;
import com.pledge.torgi.utils.StartParams;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
@@ -21,13 +18,10 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static com.pledge.torgi.model.Configs.LAST_RUN_TIME;
import static com.pledge.torgi.utils.AppUtils.CUSTOM_EXCEL_LINK;
import static com.pledge.torgi.utils.AppUtils.EXCEL_LINK;
@@ -35,15 +29,17 @@ import static com.pledge.torgi.utils.AppUtils.EXCEL_LINK;
public class DownloadHeadlessController {
private final Logger logger = LoggerFactory.getLogger(DownloadHeadlessController.class);
private static final Logger businessLogger = LoggerFactory.getLogger("BUSINESS");
private final Set<String> content = new HashSet<>();
private final TorgiEventRepository eventRepository;
private final ConfigsRepository configsRepository;
private final MissedDataRepository missedDataRepository;
private final DownloadHeadlessService downloadHeadlessService;
public DownloadHeadlessController(TorgiEventRepository eventRepository, ConfigsRepository configsRepository, MissedDataRepository missedDataRepository) {
public DownloadHeadlessController(TorgiEventRepository eventRepository, MissedDataRepository missedDataRepository,
DownloadHeadlessService downloadHeadlessService) {
this.eventRepository = eventRepository;
this.configsRepository = configsRepository;
this.missedDataRepository = missedDataRepository;
this.downloadHeadlessService = downloadHeadlessService;
eventRepository.findAllByOrderByCreatedAtDesc(PageRequest.of(0, 10000)).forEach(event -> content.add(event.getKey()));
}
@@ -52,77 +48,28 @@ public class DownloadHeadlessController {
}
private void addNewToDiff() {
logger.error("startProcessing");
DownloadHeadlessService downloadHeadlessService = new DownloadHeadlessService();
Map<String, Lot> currentContent = downloadHeadlessService.downloadExcel(0, EXCEL_LINK);
businessLogger.info("startProcessing");
Map<String, Lot> currentContent = downloadHeadlessService.downloadExcel(EXCEL_LINK);
List<TorgiEvent> newEvents = new ArrayList<>();
for (String key : currentContent.keySet()) {
if (!content.contains(key)) {
newEvents.add(new TorgiEvent(key, currentContent.get(key)));
content.add(key);
logger.error("added new diff content");
businessLogger.info("added new diff content");
}
}
eventRepository.saveAll(newEvents);
logger.error("completeProcessing");
businessLogger.info("completeProcessing readed from torgi = {}, added to DB = {}", currentContent.size(), newEvents.size());
}
public void sendNewContent(StartParams params, List<String> cadastral, String email) {
DownloadHeadlessService downloadHeadlessService = new DownloadHeadlessService();
LocalDate now = LocalDate.now();
downloadHeadlessService.createNewDocument(eventRepository, cadastral, now);
createArchive(now);
sendArchive(params, now, email);
removeArchiveAndSource(now);
logger.error("sended archive");
}
public void sendNewContent(StartParams params) {
if (alreadyRunnedToday()) {
return;
}
DownloadHeadlessService downloadHeadlessService = new DownloadHeadlessService();
LocalDate now = LocalDate.now();
downloadHeadlessService.createNewDocument(eventRepository, now);
// int i = 0;
// while (true) {
// List<Lot> partition = eventRepository.findAllByOrderByCreatedAtDesc(PageRequest.of(i, 30000)).stream().map(Lot::new).toList();
// if (partition.isEmpty()) {
// break;
// }
// downloadHeadlessService.createNewDocument(eventRepository, now, i);
// i++;
// }
createArchive(now);
sendArchive(params, now);
removeArchiveAndSource(now);
logger.error("sended archive");
saveTime();
}
private void saveTime() {
List<Configs> configs = configsRepository.findAll().stream().filter(cfg -> cfg.getKey().equals(LAST_RUN_TIME)).toList();
if (configs.isEmpty()) {
Configs newValue = new Configs();
newValue.setId(1L);
newValue.setKey(LAST_RUN_TIME);
newValue.setValue(LocalDateTime.now().toString());
configsRepository.save(newValue);
} else {
Configs oldValue = configs.get(0);
oldValue.setValue(LocalDateTime.now().toString());
configsRepository.save(oldValue);
}
}
private boolean alreadyRunnedToday() {
List<Configs> configs = configsRepository.findAll().stream().filter(cfg -> cfg.getKey().equals(LAST_RUN_TIME)).toList();
if (configs.isEmpty()) {
return false;
}
LocalDateTime fromDB = LocalDateTime.parse(configs.get(0).getValue());
return fromDB.format(DateTimeFormatter.ISO_LOCAL_DATE).equals(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
businessLogger.info("sended archive");
}
private void removeArchiveAndSource(LocalDate now) {
@@ -140,19 +87,6 @@ public class DownloadHeadlessController {
return directoryToBeDeleted.delete();
}
private void sendArchive(StartParams params, LocalDate now) {
try {
new SendEmailService("smtp.gmail.com", 587, params.getFrom(), params.getToken())
.sendMail(params.getFrom(),
params.getTo(),//"m.ryabyh@gmail.com,MEvRyabykh@sberbank.ru",
"Новая Выгрузка по торгам от " + now,
"Новая Выгрузка по торгам от " + now,
new File(now + ".zip"));
} catch (Exception e) {
logger.error(e.getMessage());
}
}
private void sendArchive(StartParams params, LocalDate now, String to) {
try {
new SendEmailService("smtp.gmail.com", 587, params.getFrom(), params.getToken())
@@ -206,29 +140,31 @@ public class DownloadHeadlessController {
fis.close();
}
@SneakyThrows
public void richData() {
List<MissedData> all = missedDataRepository.findAll(PageRequest.of(0, 10)).toList();
DownloadHeadlessService downloadHeadlessService = new DownloadHeadlessService();
for (MissedData missedCadastral : all) {
Map<String, Lot> currentContent = downloadHeadlessService.downloadExcel(0, String.format(CUSTOM_EXCEL_LINK, missedCadastral.getCadastral()));
logger.error("get content by cadastral {} {}", missedCadastral.getCadastral(), currentContent.size());
if (!currentContent.isEmpty()) {
List<TorgiEvent> newEvents = new ArrayList<>();
Set<String> contentByCadastral = new HashSet<>();
eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc("%" + missedCadastral.getCadastral() + "%").forEach(event -> contentByCadastral.add(event.getKey()));
try {
List<MissedData> all = missedDataRepository.findAll(PageRequest.of(0, 10)).toList();
for (MissedData missedCadastral : all) {
Map<String, Lot> currentContent = downloadHeadlessService.downloadExcel(String.format(CUSTOM_EXCEL_LINK, missedCadastral.getCadastral()));
businessLogger.info("get content by cadastral {} {}", missedCadastral.getCadastral(), currentContent.size());
if (!currentContent.isEmpty()) {
List<TorgiEvent> newEvents = new ArrayList<>();
Set<String> contentByCadastral = new HashSet<>();
eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc("%" + missedCadastral.getCadastral() + "%").forEach(event -> contentByCadastral.add(event.getKey()));
for (String key : currentContent.keySet()) {
if (!contentByCadastral.contains(key)) {
newEvents.add(new TorgiEvent(key, currentContent.get(key)));
content.add(key);
logger.error("added new missed content");
for (String key : currentContent.keySet()) {
if (!contentByCadastral.contains(key)) {
newEvents.add(new TorgiEvent(key, currentContent.get(key)));
content.add(key);
businessLogger.info("added new missed content");
}
}
eventRepository.saveAll(newEvents);
}
eventRepository.saveAll(newEvents);
missedDataRepository.delete(missedCadastral);
Thread.sleep(5000);
}
missedDataRepository.delete(missedCadastral);
Thread.sleep(5000);
} catch (Exception ex) {
logger.error("Exception during rich data", ex);
}
}
}

View File

@@ -1,9 +1,7 @@
package com.pledge.torgi.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.pledge.torgi.model.TorgiEvent;
import com.pledge.torgi.repositories.TorgiEventRepository;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@@ -12,11 +10,15 @@ import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.FileSystems;
@@ -27,70 +29,95 @@ import java.util.*;
import static com.pledge.torgi.utils.AppUtils.EMPTY;
import static java.util.Objects.nonNull;
@Service
public class DownloadHeadlessService {
private final Logger logger = LoggerFactory.getLogger(DownloadHeadlessService.class);
private static final Logger businessLogger = LoggerFactory.getLogger("BUSINESS");
private static final int COL_TYPE = 1;
private static final int COL_NOTICE = 3;
private static final int COL_BID_START = 6;
private static final int COL_BID_END = 7;
private static final int COL_TRADES_DATE = 9;
private static final int COL_LOT_NUMBER = 16;
private static final int COL_STATUS = 17;
private static final int COL_INIT_MONEY = 24;
private static final int COL_STEP = 25;
private static final int COL_START_PRICE = 26;
private static final int COL_FINAL_PRICE = 27;
private static final int COL_FULL_INFO = 29;
@SneakyThrows
public Map<String, Lot> downloadExcel(int retryCount, String link) {
@Retryable(retryFor = UncheckedIOException.class, maxAttempts = 11,
backoff = @Backoff(delay = 1000))
public Map<String, Lot> downloadExcel(String link) {
HttpURLConnection connection;
int responseCode;
try {
HttpURLConnection connection = (HttpURLConnection) new URL(link).openConnection();
connection = (HttpURLConnection) new URL(link).openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
connection.setRequestProperty("Accept", "application/json, text/plain, */*");
connection.setRequestProperty("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7");
connection.setConnectTimeout(5000);
connection.setReadTimeout(10000);
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
Map<String, Lot> data = new HashMap<>();
Workbook wb = new XSSFWorkbook(connection.getInputStream());
Sheet sheetWithData = wb.getSheetAt(0);
int currentRow = 2;
while (sheetWithData.getRow(currentRow) != null) {
List<String> cadastrals = extractCadastral(sheetWithData.getRow(currentRow).getCell(29).getStringCellValue());
if (cadastrals.isEmpty()) {
Lot extractedContent = extractDataFromRow(sheetWithData.getRow(currentRow), "");
data.put(extractedContent.getNotice() + "_" + extractedContent.getLotNumber() + "_" + extractedContent.getStatus(), extractedContent);
} else {
for (String cadastral : cadastrals) {
Lot extractedContent = extractDataFromRow(sheetWithData.getRow(currentRow), cadastral);
data.put(extractedContent.getNotice() + "_" + extractedContent.getLotNumber() + "_" + extractedContent.getStatus() + "_" + cadastral, extractedContent);
}
}
currentRow++;
}
return data;
} else {
logger.error("Error in line 99 ");
Thread.sleep(1000);
return downloadExcel(retryCount + 1, link);
}
} catch (Exception e) {
logger.error("Error in line 103 " + e);
if (retryCount > 10) {
return new HashMap<>();
}
responseCode = connection.getResponseCode();
} catch (IOException e) {
logger.error("Error during processing link {} ", link, e);
throw new UncheckedIOException(e);
}
Thread.sleep(1000);
return downloadExcel(retryCount + 1, link);
if (responseCode != HttpURLConnection.HTTP_OK) {
logger.error("Wrong response code {} during call link {} ", responseCode, link);
throw new UncheckedIOException(new IOException("Wrong response code " + responseCode + " during call link " + link));
}
try (Workbook wb = new XSSFWorkbook(connection.getInputStream())) {
Map<String, Lot> data = new HashMap<>();
Sheet sheetWithData = wb.getSheetAt(0);
int currentRow = 2;
while (sheetWithData.getRow(currentRow) != null) {
List<String> cadastrals = extractCadastral(sheetWithData.getRow(currentRow).getCell(29).getStringCellValue());
if (cadastrals.isEmpty()) {
Lot extractedContent = extractDataFromRow(sheetWithData.getRow(currentRow), "");
data.put(extractedContent.getNotice() + "_" + extractedContent.getLotNumber() + "_" + extractedContent.getStatus(), extractedContent);
} else {
for (String cadastral : cadastrals) {
Lot extractedContent = extractDataFromRow(sheetWithData.getRow(currentRow), cadastral);
data.put(extractedContent.getNotice() + "_" + extractedContent.getLotNumber() + "_" + extractedContent.getStatus() + "_" + cadastral, extractedContent);
}
}
currentRow++;
}
return data;
} catch (IOException e) {
logger.error("Error during processing link {} ", link, e);
throw new UncheckedIOException(e);
} catch (Exception e) {
logger.error("Error during parsing content by link {} ", link, e);
throw new UncheckedIOException(new IOException("Failed to parse content by link " + link, e));
}
}
@Recover
public Map<String, Lot> downloadExcelFallback(Exception e, String link) {
logger.error("Failed to download excel by link {} after retries", link, e);
return new HashMap<>();
}
private Lot extractDataFromRow(Row row, String cadastral) {
Lot lot = new Lot();
lot.setBidStart(row.getCell(6).getStringCellValue());
lot.setBidEnd(row.getCell(7).getStringCellValue());
lot.setNotice(row.getCell(COL_NOTICE).getStringCellValue());
lot.setBidStart(row.getCell(COL_BID_START).getStringCellValue());
lot.setBidEnd(row.getCell(COL_BID_END).getStringCellValue());
lot.setTradesDate(row.getCell(COL_TRADES_DATE).getStringCellValue());
lot.setCadastral(cadastral);
lot.setLotNumber((int) row.getCell(16).getNumericCellValue());
lot.setFinalPrice(row.getCell(27).getStringCellValue());
lot.setNotice(row.getCell(3).getStringCellValue());
lot.setInitMoney(row.getCell(24).getStringCellValue());
lot.setStep(row.getCell(25).getStringCellValue());
lot.setStartPrice(row.getCell(26).getStringCellValue());
lot.setStatus(row.getCell(17).getStringCellValue());
lot.setTradesDate(row.getCell(9).getStringCellValue());
lot.setFullInfo(row.getCell(29).getStringCellValue());
lot.setType(row.getCell(1).getStringCellValue());
lot.setLotNumber((int) row.getCell(COL_LOT_NUMBER).getNumericCellValue());
lot.setStatus(row.getCell(COL_STATUS).getStringCellValue());
lot.setInitMoney(row.getCell(COL_INIT_MONEY).getStringCellValue());
lot.setStep(row.getCell(COL_STEP).getStringCellValue());
lot.setStartPrice(row.getCell(COL_START_PRICE).getStringCellValue());
lot.setFinalPrice(row.getCell(COL_FINAL_PRICE).getStringCellValue());
lot.setFullInfo(row.getCell(COL_FULL_INFO).getStringCellValue());
lot.setType(row.getCell(COL_TYPE).getStringCellValue());
return lot;
}
@@ -126,31 +153,6 @@ public class DownloadHeadlessService {
return cadastrals;
}
public void createNewDocument(TorgiEventRepository eventRepository, LocalDate now) {
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
XSSFSheet sheet = workbook.createSheet("sheet");
setRowHead(sheet);
logger.error("before write body");
int i = 0;
int currentRow = 1;
while (true) {
List<Lot> partition = eventRepository.findAllByOrderByCreatedAtDesc(PageRequest.of(i, 1000)).stream().map(Lot::new).toList();
if (partition.isEmpty()) {
break;
}
currentRow = setRowBodyNew(sheet, currentRow, partition);
i++;
}
//setRowWith(sheet);
logger.error("before write to file");
try (FileOutputStream fileOut = new FileOutputStream(getFileToSave(now))) {
workbook.write(fileOut);
}
} catch (Exception ex) {
logger.error("in createNewDocument " + ex.getMessage());
}
}
public void createNewDocument(TorgiEventRepository eventRepository, List<String> cadastrals, LocalDate now) {
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
XSSFSheet sheet = workbook.createSheet("sheet");
@@ -160,14 +162,12 @@ public class DownloadHeadlessService {
int currentRow = 1;
setRowBodyNew(sheet, currentRow, data);
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
logger.error("before write to file");
businessLogger.info("before write to file");
try (FileOutputStream fileOut = new FileOutputStream(getFileToSave(now))) {
workbook.write(fileOut);
}
} catch (Exception ex) {
logger.error("in createNewDocument " + ex.getMessage());
logger.error("in createNewDocument", ex);
}
}
@@ -179,10 +179,10 @@ public class DownloadHeadlessService {
try {
events.addAll(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc("%" + cadastral + "%"));
} catch (Exception e) {
logger.error("Error during process cadastral = " + cadastral + " " + e);
logger.error("Error during process cadastral = {}", cadastral, e);
}
if (i % 100 == 0) {
logger.error("executed = " + i + " added = " + events.size());
businessLogger.info("executed = {} added = {}", i ,events.size());
}
}
return events.stream().map(Lot::new).toList();
@@ -206,7 +206,7 @@ public class DownloadHeadlessService {
rowHead.createCell(13).setCellValue("Время получения события");
}
private int setRowBodyNew(XSSFSheet sheet, int currentRow, Collection<Lot> contents) throws JsonProcessingException {
private int setRowBodyNew(XSSFSheet sheet, int currentRow, Collection<Lot> contents) {
for (Lot lot : contents) {
currentRow = createRow(sheet, currentRow, lot);
}

View File

@@ -26,8 +26,7 @@ public class EmailService {
return;
}
List<Emails> alreadyProcessed = emailsRepository.findTop10ByOrderByIdDesc();
List<EmailData> emailData = new ReceiveEmailService("imap.gmail.com", 993, params.getFrom(), params.getToken(), params.getSpecialEmail(), log)
.downloadEmails(alreadyProcessed);
List<EmailData> 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);
}

View File

@@ -0,0 +1,34 @@
package com.pledge.torgi;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@Testcontainers
class DatabaseIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine")
.withDatabaseName("torgi_test")
.withUsername("postgres")
.withPassword("postgres");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Test
void connectionEstablished() {
assertThat(postgres.isRunning()).isTrue();
}
}

View File

@@ -0,0 +1,468 @@
package com.pledge.torgi.controller;
import com.pledge.torgi.model.MissedData;
import com.pledge.torgi.model.TorgiEvent;
import com.pledge.torgi.repositories.MissedDataRepository;
import com.pledge.torgi.repositories.TorgiEventRepository;
import com.pledge.torgi.service.DownloadHeadlessService;
import com.pledge.torgi.service.Lot;
import com.pledge.torgi.service.SendEmailService;
import com.pledge.torgi.utils.StartParams;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
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.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class DownloadHeadlessControllerTest {
private static final String CADASTRAL = "77:01:0001001:1234";
private static final String EVENT_KEY = "Notice1_1_PUBLISHED_" + CADASTRAL;
@Mock
private TorgiEventRepository eventRepository;
@Mock
private MissedDataRepository missedDataRepository;
@Mock
private DownloadHeadlessService downloadHeadlessService;
@BeforeEach
void setUp() {
lenient().when(eventRepository.findAllByOrderByCreatedAtDesc(any(PageRequest.class)))
.thenReturn(new PageImpl<>(List.of()));
}
@Test
void constructor_shouldLoadExistingKeys() {
// given
TorgiEvent event = new TorgiEvent();
event.setKey("test-key");
Page<TorgiEvent> page = new PageImpl<>(List.of(event));
when(eventRepository.findAllByOrderByCreatedAtDesc(any(PageRequest.class)))
.thenReturn(page);
// when
new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// then
verify(eventRepository).findAllByOrderByCreatedAtDesc(any(PageRequest.class));
}
@Test
void constructor_shouldLoadAllKeysIntoContentSet() {
// given
TorgiEvent event1 = new TorgiEvent();
event1.setKey("key-1");
TorgiEvent event2 = new TorgiEvent();
event2.setKey("key-2");
when(eventRepository.findAllByOrderByCreatedAtDesc(any(PageRequest.class)))
.thenReturn(new PageImpl<>(List.of(event1, event2)));
// when
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// then
assertThat(getContent(controller)).containsExactlyInAnyOrder("key-1", "key-2");
}
@Test
void process_shouldSaveNewEvents() throws Exception {
// given
Lot lot = new Lot();
lot.setStatus("PUBLISHED");
lot.setNotice("Notice1");
lot.setLotNumber(1);
lot.setCadastral(CADASTRAL);
lot.setFullInfo("Full info");
Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot);
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.process();
// then
ArgumentCaptor<List<TorgiEvent>> captor = ArgumentCaptor.forClass(List.class);
verify(eventRepository).saveAll(captor.capture());
List<TorgiEvent> saved = captor.getValue();
assertThat(saved).hasSize(1);
assertThat(saved.get(0).getKey()).isEqualTo(EVENT_KEY);
assertThat(saved.get(0).getStatus()).isEqualTo("PUBLISHED");
assertThat(saved.get(0).getNotice()).isEqualTo("Notice1");
assertThat(saved.get(0).getLotNumber()).isEqualTo(1);
assertThat(saved.get(0).getCadastral()).isEqualTo(CADASTRAL);
assertThat(saved.get(0).getFullInfo()).isEqualTo("Full info");
assertThat(getContent(controller)).contains(EVENT_KEY);
}
@Test
void process_shouldSkipAlreadyKnownKeys() throws Exception {
// given
TorgiEvent existing = new TorgiEvent();
existing.setKey(EVENT_KEY);
when(eventRepository.findAllByOrderByCreatedAtDesc(any(PageRequest.class)))
.thenReturn(new PageImpl<>(List.of(existing)));
Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot());
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.process();
// then
ArgumentCaptor<List<TorgiEvent>> captor = ArgumentCaptor.forClass(List.class);
verify(eventRepository).saveAll(captor.capture());
assertThat(captor.getValue()).isEmpty();
assertThat(getContent(controller)).containsExactly(EVENT_KEY);
}
@Test
void sendNewContent_shouldCreateDocumentSendArchiveAndCleanUp() throws Exception {
// given
StartParams params = new StartParams();
params.setFrom("from@example.com");
params.setToken("token");
List<String> cadastral = List.of(CADASTRAL);
try (MockedConstruction<SendEmailService> emailConstruction = mockConstruction(SendEmailService.class)) {
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.sendNewContent(params, cadastral, "to@example.com");
// then
ArgumentCaptor<LocalDate> dateCaptor = ArgumentCaptor.forClass(LocalDate.class);
verify(downloadHeadlessService).createNewDocument(eq(eventRepository), eq(cadastral), dateCaptor.capture());
LocalDate now = dateCaptor.getValue();
SendEmailService emailService = emailConstruction.constructed().get(0);
verify(emailService).sendMail(eq("from@example.com"), eq("to@example.com"),
eq("Новая Выгрузка по торгам от " + now),
eq("Новая Выгрузка по торгам от " + now),
argThat((File file) -> file != null && file.getName().equals(now + ".zip")));
assertThat(new File(now + ".zip")).doesNotExist();
assertThat(new File(now.toString())).doesNotExist();
}
}
@Test
void sendArchive_shouldSwallowEmailException() throws Exception {
// given
StartParams params = new StartParams();
params.setFrom("from@example.com");
params.setToken("token");
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
try (MockedConstruction<SendEmailService> ignored = mockConstruction(SendEmailService.class,
(mock, context) -> doThrow(new RuntimeException("smtp failure"))
.when(mock).sendMail(anyString(), anyString(), anyString(), anyString(), any(File.class)))) {
// when / then
assertThatCode(() -> invokeSendArchive(controller, params, LocalDate.now(), "to@example.com"))
.doesNotThrowAnyException();
SendEmailService emailService = ignored.constructed().get(0);
verify(emailService).sendMail(anyString(), anyString(), anyString(), anyString(), any(File.class));
}
}
@Test
void richData_shouldSaveNewEventsForMissedCadastral() throws Exception {
// given
MissedData missed = new MissedData();
missed.setCadastral(CADASTRAL);
when(missedDataRepository.findAll(any(PageRequest.class))).thenReturn(new PageImpl<>(List.of(missed)));
when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc("%" + CADASTRAL + "%")).thenReturn(Collections.emptyList());
Lot lot = new Lot();
lot.setStatus("PUBLISHED");
lot.setNotice("Notice1");
lot.setLotNumber(1);
lot.setCadastral(CADASTRAL);
Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot);
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.richData();
// then
ArgumentCaptor<List<TorgiEvent>> captor = ArgumentCaptor.forClass(List.class);
verify(eventRepository).saveAll(captor.capture());
assertThat(captor.getValue()).hasSize(1);
assertThat(captor.getValue().get(0).getKey()).isEqualTo(EVENT_KEY);
verify(missedDataRepository).delete(missed);
}
@Test
void richData_shouldSkipKnownEventsForCadastral() throws Exception {
// given
MissedData missed = new MissedData();
missed.setCadastral(CADASTRAL);
when(missedDataRepository.findAll(any(PageRequest.class))).thenReturn(new PageImpl<>(List.of(missed)));
TorgiEvent existing = new TorgiEvent();
existing.setKey(EVENT_KEY);
when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc("%" + CADASTRAL + "%")).thenReturn(List.of(existing));
Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot());
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.richData();
// then
ArgumentCaptor<List<TorgiEvent>> captor = ArgumentCaptor.forClass(List.class);
verify(eventRepository).saveAll(captor.capture());
assertThat(captor.getValue()).isEmpty();
verify(missedDataRepository).delete(missed);
}
@Test
void richData_shouldDeleteMissedDataWhenDownloadedContentEmpty() throws Exception {
// given
MissedData missed = new MissedData();
missed.setCadastral(CADASTRAL);
when(missedDataRepository.findAll(any(PageRequest.class))).thenReturn(new PageImpl<>(List.of(missed)));
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(Collections.emptyMap());
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.richData();
// then
verify(eventRepository, never()).saveAll(anyList());
verify(missedDataRepository).delete(missed);
}
@Test
void richData_shouldAbortProcessingOnRepositoryError() throws Exception {
// given
MissedData missed = new MissedData();
missed.setCadastral(CADASTRAL);
when(missedDataRepository.findAll(any(PageRequest.class))).thenReturn(new PageImpl<>(List.of(missed)));
when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc(anyString()))
.thenThrow(new RuntimeException("Database error"));
Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot());
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
controller.richData();
// then
verify(eventRepository, never()).saveAll(anyList());
verify(missedDataRepository, never()).delete(any());
}
@Test
void richData_shouldHandleExceptionGracefully() {
// given
when(missedDataRepository.findAll(any(PageRequest.class))).thenThrow(new RuntimeException("Database error"));
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when / then
assertThatCode(controller::richData).doesNotThrowAnyException();
}
@Test
void deleteDirectory_shouldDeleteNestedStructure(@TempDir File tempDir) throws Exception {
// given
File nested = new File(tempDir, "nested");
File deeper = new File(nested, "deeper");
assertThat(deeper.mkdirs()).isTrue();
writeFile(new File(deeper, "file.txt"), "content");
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
// when
boolean result = invokeDeleteDirectory(controller, nested);
// then
assertThat(result).isTrue();
assertThat(nested).doesNotExist();
assertThat(tempDir).exists();
}
@Test
void zipFile_shouldAddFileEntry(@TempDir File tempDir) throws Exception {
// given
File file = new File(tempDir, "data.txt");
writeFile(file, "hello");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// when
try (ZipOutputStream zipOut = new ZipOutputStream(baos)) {
invokeZipFile(file, "dir/data.txt", zipOut);
}
// then
try (ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
ZipEntry entry = zipIn.getNextEntry();
assertThat(entry).isNotNull();
assertThat(entry.getName()).isEqualTo("dir/data.txt");
assertThat(new String(zipIn.readAllBytes(), StandardCharsets.UTF_8)).isEqualTo("hello");
assertThat(zipIn.getNextEntry()).isNull();
}
}
@Test
void zipFile_shouldAddDirectoryEntry(@TempDir File tempDir) throws Exception {
// given
File dir = new File(tempDir, "mydir");
assertThat(dir.mkdirs()).isTrue();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// when
try (ZipOutputStream zipOut = new ZipOutputStream(baos)) {
invokeZipFile(dir, "mydir", zipOut);
}
// then
try (ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
ZipEntry entry = zipIn.getNextEntry();
assertThat(entry).isNotNull();
assertThat(entry.getName()).isEqualTo("mydir/");
assertThat(entry.isDirectory()).isTrue();
assertThat(zipIn.getNextEntry()).isNull();
}
}
@Test
void createArchive_shouldCreateZipFileForDirectory() throws Exception {
// given
LocalDate now = LocalDate.of(1999, 1, 1);
File sourceDir = new File(now.toString());
File nested = new File(sourceDir, "nested");
assertThat(nested.mkdirs()).isTrue();
writeFile(new File(nested, "data.txt"), "archive-content");
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService);
try {
// when
invokeCreateArchive(controller, now);
// then
File zip = new File(now + ".zip");
assertThat(zip).exists();
assertThat(readEntryNames(zip)).containsExactlyInAnyOrder(
"1999-01-01/", "1999-01-01/nested/", "1999-01-01/nested/data.txt");
} finally {
new File(now + ".zip").delete();
deleteRecursively(sourceDir);
}
}
// Helpers
@SuppressWarnings("unchecked")
private Set<String> getContent(DownloadHeadlessController controller) {
try {
Field field = DownloadHeadlessController.class.getDeclaredField("content");
field.setAccessible(true);
return (Set<String>) field.get(controller);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void invokeCreateArchive(DownloadHeadlessController controller, LocalDate now) {
try {
Method method = DownloadHeadlessController.class.getDeclaredMethod("createArchive", LocalDate.class);
method.setAccessible(true);
method.invoke(controller, now);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private boolean invokeDeleteDirectory(DownloadHeadlessController controller, File directory) {
try {
Method method = DownloadHeadlessController.class.getDeclaredMethod("deleteDirectory", File.class);
method.setAccessible(true);
return (boolean) method.invoke(controller, directory);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void invokeSendArchive(DownloadHeadlessController controller, StartParams params, LocalDate now, String to) {
try {
Method method = DownloadHeadlessController.class.getDeclaredMethod("sendArchive", StartParams.class, LocalDate.class, String.class);
method.setAccessible(true);
method.invoke(controller, params, now, to);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void invokeZipFile(File fileToZip, String fileName, ZipOutputStream zipOut) {
try {
Method method = DownloadHeadlessController.class.getDeclaredMethod("zipFile", File.class, String.class, ZipOutputStream.class);
method.setAccessible(true);
method.invoke(null, fileToZip, fileName, zipOut);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void writeFile(File file, String content) throws IOException {
Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8));
}
private List<String> readEntryNames(File zipFile) throws IOException {
List<String> names = new ArrayList<>();
try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
names.add(entry.getName());
}
}
return names;
}
private void deleteRecursively(File file) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursively(child);
}
}
file.delete();
}
}

View File

@@ -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<String> response = controller.getPerson();
// then
assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
assertThat(response.getBody()).isEqualTo("OK");
}
}

View File

@@ -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<String> 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<String> 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<String> result = invokeExtractCadastral(workbook);
// then
assertThat(result).isEmpty();
}
@Test
void extractCadastral_shouldReturnEmptyForWorkbookWithNoRows() throws IOException {
// given
Workbook workbook = new XSSFWorkbook();
workbook.createSheet("Sheet1");
// when
List<String> 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<String> 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<String> 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<Iterable<MissedData>> 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<String> invokeExtractCadastral(Workbook workbook) {
try {
var method = FileServiceImpl.class.getDeclaredMethod("extractCadastral", Workbook.class);
method.setAccessible(true);
return (List<String>) method.invoke(fileService, workbook);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
private void invokeSaveCadastralToGetMissedData(List<String> cadastrals) {
try {
var method = FileServiceImpl.class.getDeclaredMethod("saveCadastralToGetMissedData", List.class);
method.setAccessible(true);
method.invoke(fileService, cadastrals);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> result = service.extractCadastral(rawValue);
// then
assertThat(result).containsExactly("77:01:0001001:1234");
}
}

View File

@@ -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<String> cadastrals = List.of();
// when
List<Lot> result = invokeExtractData(cadastrals);
// then
assertThat(result).isEmpty();
}
@Test
void extractData_shouldReturnLotsForCadastral() {
// given
List<String> 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<Lot> 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<String> 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<Lot> result = invokeExtractData(cadastrals);
// then
assertThat(result).hasSize(2);
}
@Test
void extractData_shouldHandleRepositoryException() {
// given
List<String> cadastrals = List.of("77:01:0001001:1234");
when(eventRepository.findAllByFullInfoLikeOrderByCreatedAtDesc(anyString()))
.thenThrow(new RuntimeException("Database error"));
// when
List<Lot> 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<Lot> 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<Lot> invokeExtractData(List<String> cadastrals) {
try {
var method = DownloadHeadlessService.class.getDeclaredMethod("extractData", TorgiEventRepository.class, List.class);
method.setAccessible(true);
return (List<Lot>) 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<Lot> 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);
}
}
}

View File

@@ -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<String> 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<String> 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<String> result = service.extractCadastral(rawValue);
// then
assertThat(result).containsExactly("X9FKXXEEBMD123456");
}
@Test
void extractCadastral_shouldReturnEmptyForUnknownType() {
// given
String rawValue = "Неизвестный тип: значение";
// when
List<String> result = service.extractCadastral(rawValue);
// then
assertThat(result).isEmpty();
}
@Test
void extractCadastral_shouldReturnEmptyForEmptyInput() {
// given
String rawValue = "";
// when
List<String> result = service.extractCadastral(rawValue);
// then
assertThat(result).isEmpty();
}
}

View File

@@ -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")
));
}
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}