mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
Compare commits
3 Commits
8bfdb2abb5
...
35304a1491
Author | SHA1 | Date | |
---|---|---|---|
![]() |
35304a1491 | ||
![]() |
cc938e1751 | ||
![]() |
b65624cf57 |
@ -37,8 +37,21 @@ public class EmailService {
|
|||||||
*/
|
*/
|
||||||
@Async
|
@Async
|
||||||
public void sendEmailWithAttachment(Email email) throws MessagingException {
|
public void sendEmailWithAttachment(Email email) throws MessagingException {
|
||||||
ApplicationProperties.Mail mailProperties = applicationProperties.getMail();
|
|
||||||
MultipartFile file = email.getFileInput();
|
MultipartFile file = email.getFileInput();
|
||||||
|
// 1) Validate recipient email address
|
||||||
|
if (email.getTo() == null || email.getTo().trim().isEmpty()) {
|
||||||
|
throw new MessagingException("Invalid Addresses");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Validate attachment
|
||||||
|
if (file == null
|
||||||
|
|| file.isEmpty()
|
||||||
|
|| file.getOriginalFilename() == null
|
||||||
|
|| file.getOriginalFilename().isEmpty()) {
|
||||||
|
throw new MessagingException("An attachment is required to send the email.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationProperties.Mail mailProperties = applicationProperties.getMail();
|
||||||
|
|
||||||
// Creates a MimeMessage to represent the email
|
// Creates a MimeMessage to represent the email
|
||||||
MimeMessage message = mailSender.createMimeMessage();
|
MimeMessage message = mailSender.createMimeMessage();
|
||||||
|
@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api;
|
|||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.mail.MailSendException;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@ -53,6 +54,11 @@ public class EmailController {
|
|||||||
// Calls the service to send the email with attachment
|
// Calls the service to send the email with attachment
|
||||||
emailService.sendEmailWithAttachment(email);
|
emailService.sendEmailWithAttachment(email);
|
||||||
return ResponseEntity.ok("Email sent successfully");
|
return ResponseEntity.ok("Email sent successfully");
|
||||||
|
} catch (MailSendException ex) {
|
||||||
|
// handles your "Invalid Addresses" case
|
||||||
|
String errorMsg = ex.getMessage();
|
||||||
|
log.error("MailSendException: {}", errorMsg, ex);
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMsg);
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
// Catches any messaging exception (e.g., invalid email address, SMTP server issues)
|
// Catches any messaging exception (e.g., invalid email address, SMTP server issues)
|
||||||
String errorMsg = "Failed to send email: " + e.getMessage();
|
String errorMsg = "Failed to send email: " + e.getMessage();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -52,6 +53,6 @@ public class UploadLimitService {
|
|||||||
if (bytes < 1024) return bytes + " B";
|
if (bytes < 1024) return bytes + " B";
|
||||||
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
||||||
String pre = "KMGTPE".charAt(exp - 1) + "B";
|
String pre = "KMGTPE".charAt(exp - 1) + "B";
|
||||||
return String.format("%.1f %s", bytes / Math.pow(1024, exp), pre);
|
return String.format(Locale.US, "%.1f %s", bytes / Math.pow(1024, exp), pre);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ multiPdfPrompt=Оберіть PDFи (2+)
|
|||||||
multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи
|
multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи
|
||||||
imgPrompt=Оберіть зображення(я)
|
imgPrompt=Оберіть зображення(я)
|
||||||
genericSubmit=Надіслати
|
genericSubmit=Надіслати
|
||||||
uploadLimit=Maximum file size:
|
uploadLimit=Максимальний розмір файлу:
|
||||||
uploadLimitExceededSingular=is too large. Maximum allowed size is
|
uploadLimitExceededSingular=занадто великий. Максимально дозволений розмір -
|
||||||
uploadLimitExceededPlural=are too large. Maximum allowed size is
|
uploadLimitExceededPlural=занадто великі. Максимально дозволений розмір -
|
||||||
processTimeWarning=Увага: Цей процес може тривати до хвилини в залежності від розміру файлу.
|
processTimeWarning=Увага: Цей процес може тривати до хвилини в залежності від розміру файлу.
|
||||||
pageOrderPrompt=Порядок сторінок (введіть список номерів сторінок через кому):
|
pageOrderPrompt=Порядок сторінок (введіть список номерів сторінок через кому):
|
||||||
pageSelectionPrompt=Користувацький вибір сторінки (введіть список номерів сторінок через кому 1,5,6 або функції типу 2n+1) :
|
pageSelectionPrompt=Користувацький вибір сторінки (введіть список номерів сторінок через кому 1,5,6 або функції типу 2n+1) :
|
||||||
@ -86,14 +86,14 @@ loading=Завантаження...
|
|||||||
addToDoc=Додати до документу
|
addToDoc=Додати до документу
|
||||||
reset=Скинути
|
reset=Скинути
|
||||||
apply=Застосувати
|
apply=Застосувати
|
||||||
noFileSelected=No file selected. Please upload one.
|
noFileSelected=Файл не вибрано. Будь ласка, завантажте один.
|
||||||
|
|
||||||
legal.privacy=Політика конфіденційності
|
legal.privacy=Політика конфіденційності
|
||||||
legal.terms=Правила та умови
|
legal.terms=Правила та умови
|
||||||
legal.accessibility=Доступність
|
legal.accessibility=Доступність
|
||||||
legal.cookie=Політика використання файлів cookie
|
legal.cookie=Політика використання файлів cookie
|
||||||
legal.impressum=Вихідні дані
|
legal.impressum=Вихідні дані
|
||||||
legal.showCookieBanner=Cookie Preferences
|
legal.showCookieBanner=Налаштування файлів cookie
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
@ -237,7 +237,7 @@ adminUserSettings.activeUsers=Активні користувачі:
|
|||||||
adminUserSettings.disabledUsers=Заблоковані користувачі:
|
adminUserSettings.disabledUsers=Заблоковані користувачі:
|
||||||
adminUserSettings.totalUsers=Всього користувачів:
|
adminUserSettings.totalUsers=Всього користувачів:
|
||||||
adminUserSettings.lastRequest=Останній запит
|
adminUserSettings.lastRequest=Останній запит
|
||||||
adminUserSettings.usage=View Usage
|
adminUserSettings.usage=Переглянути використання
|
||||||
|
|
||||||
endpointStatistics.title=Статистика кінцевих точок
|
endpointStatistics.title=Статистика кінцевих точок
|
||||||
endpointStatistics.header=Статистика кінцевих точок
|
endpointStatistics.header=Статистика кінцевих точок
|
||||||
@ -364,9 +364,9 @@ home.compressPdfs.title=Стиснути
|
|||||||
home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір.
|
home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір.
|
||||||
compressPdfs.tags=стиск,маленький,крихітний
|
compressPdfs.tags=стиск,маленький,крихітний
|
||||||
|
|
||||||
home.unlockPDFForms.title=Unlock PDF Forms
|
home.unlockPDFForms.title=Розблокувати PDF форми
|
||||||
home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document.
|
home.unlockPDFForms.desc=Видалити властивість "тільки для читання" з полів форми у PDF-документі.
|
||||||
unlockPDFForms.tags=remove,delete,form,field,readonly
|
unlockPDFForms.tags=видалити,розблокувати,форма,поле,тільки для читання
|
||||||
|
|
||||||
home.changeMetadata.title=Змінити метадані
|
home.changeMetadata.title=Змінити метадані
|
||||||
home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF
|
home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF
|
||||||
@ -609,7 +609,7 @@ login.userIsDisabled=Користувач деактивовано, вхід з
|
|||||||
login.alreadyLoggedIn=Ви вже увійшли до
|
login.alreadyLoggedIn=Ви вже увійшли до
|
||||||
login.alreadyLoggedIn2=пристроїв (а). Будь ласка, вийдіть із цих пристроїв і спробуйте знову.
|
login.alreadyLoggedIn2=пристроїв (а). Будь ласка, вийдіть із цих пристроїв і спробуйте знову.
|
||||||
login.toManySessions=У вас дуже багато активних сесій
|
login.toManySessions=У вас дуже багато активних сесій
|
||||||
login.logoutMessage=You have been logged out.
|
login.logoutMessage=Ви вийшли з системи.
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
autoRedact.title=Автоматичне редагування
|
autoRedact.title=Автоматичне редагування
|
||||||
@ -742,10 +742,10 @@ sanitizePDF.title=Дезінфекція PDF
|
|||||||
sanitizePDF.header=Дезінфекція PDF файлу
|
sanitizePDF.header=Дезінфекція PDF файлу
|
||||||
sanitizePDF.selectText.1=Видалити JavaScript
|
sanitizePDF.selectText.1=Видалити JavaScript
|
||||||
sanitizePDF.selectText.2=Видалити вбудовані файли
|
sanitizePDF.selectText.2=Видалити вбудовані файли
|
||||||
sanitizePDF.selectText.3=Remove XMP metadata
|
sanitizePDF.selectText.3=Видалити XMP метадані
|
||||||
sanitizePDF.selectText.4=Видалити посилання
|
sanitizePDF.selectText.4=Видалити посилання
|
||||||
sanitizePDF.selectText.5=Видалити шрифти
|
sanitizePDF.selectText.5=Видалити шрифти
|
||||||
sanitizePDF.selectText.6=Remove Document Info Metadata
|
sanitizePDF.selectText.6=Видалити метадані інформації про документ
|
||||||
sanitizePDF.submit=Дезінфекція
|
sanitizePDF.submit=Дезінфекція
|
||||||
|
|
||||||
|
|
||||||
@ -1071,7 +1071,7 @@ rotate.submit=Повернути
|
|||||||
split.title=Розділити PDF
|
split.title=Розділити PDF
|
||||||
split.header=Розділити PDF
|
split.header=Розділити PDF
|
||||||
split.desc.1=Числа, які ви вибрали, це номери сторінок, на яких ви хочете зробити розділ.
|
split.desc.1=Числа, які ви вибрали, це номери сторінок, на яких ви хочете зробити розділ.
|
||||||
split.desc.2=Таким чином, вибір 1,3,7-8 розділить 10-сторінковий документ на 6 окремих PDF-файлів з:
|
split.desc.2=Таким чином, вибір 1,3,7-8 розділіть 10-сторінковий документ на 6 окремих PDF-файлів з:
|
||||||
split.desc.3=Документ #1: Сторінка 1
|
split.desc.3=Документ #1: Сторінка 1
|
||||||
split.desc.4=Документ #2: Сторінки 2 і 3
|
split.desc.4=Документ #2: Сторінки 2 і 3
|
||||||
split.desc.5=Документ #3: Сторінки 4, 5 і 6
|
split.desc.5=Документ #3: Сторінки 4, 5 і 6
|
||||||
@ -1372,68 +1372,68 @@ fileChooser.extractPDF=Видобування...
|
|||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
releases.footer=Релізи
|
releases.footer=Релізи
|
||||||
releases.title=Примечания к релизу
|
releases.title=Примітки до релізу
|
||||||
releases.header=Примечания к релизу
|
releases.header=Примітки до релізу
|
||||||
releases.current.version=Текущий релиз
|
releases.current.version=Поточний реліз
|
||||||
releases.note=Примітка до релізу доступна тільки на англійській мові
|
releases.note=Примітки до релізу доступні лише англійською мовою
|
||||||
|
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Перевірка підписів PDF
|
validateSignature.title=Перевірка підписів PDF
|
||||||
validateSignature.header=Перевірка цифрових підписів
|
validateSignature.header=Перевірка цифрових підписів
|
||||||
validateSignature.selectPDF=Виберіть підписаний PDF-файл
|
validateSignature.selectPDF=Виберіть підписаний PDF-файл
|
||||||
validateSignature.submit=Перевірити підписи
|
validateSignature.submit=Перевірити підписи
|
||||||
validateSignature.results=Результаты проверки
|
validateSignature.results=Результати перевірки
|
||||||
validateSignature.status=Статус
|
validateSignature.status=Статус
|
||||||
validateSignature.signer=Підписант
|
validateSignature.signer=Підписант
|
||||||
validateSignature.date=Дата
|
validateSignature.date=Дата
|
||||||
validateSignature.reason=Причина
|
validateSignature.reason=Причина
|
||||||
validateSignature.location=Местоположение
|
validateSignature.location=Місцезнаходження
|
||||||
validateSignature.noSignatures=В цьому документі не знайдено цифрових підписів
|
validateSignature.noSignatures=У цьому документі не знайдено цифрових підписів
|
||||||
validateSignature.status.valid=Дійна
|
validateSignature.status.valid=Дійсний
|
||||||
validateSignature.status.invalid=Недійсна
|
validateSignature.status.invalid=Недійсний
|
||||||
validateSignature.chain.invalid=Перевірка цепочки сертифікатів не удалась - неможливо перевірити особистість підписанта
|
validateSignature.chain.invalid=Перевірка ланцюга сертифікатів не вдалася - неможливо перевірити особу підписанта
|
||||||
validateSignature.trust.invalid=Сертифікат відсутній у довіреному сховищі - джерело не може бути перевірено
|
validateSignature.trust.invalid=Сертифікат відсутній у довіреному сховищі - джерело не може бути перевірено
|
||||||
validateSignature.cert.expired=Срок дії сертифіката істеку
|
validateSignature.cert.expired=Термін дії сертифіката закінчився
|
||||||
validateSignature.cert.revoked=Сертифікат був отозван
|
validateSignature.cert.revoked=Сертифікат було відкликано
|
||||||
validateSignature.signature.info=Інформація про підписи
|
validateSignature.signature.info=Інформація про підпис
|
||||||
validateSignature.signature=Подпись
|
validateSignature.signature=Підпис
|
||||||
validateSignature.signature.mathValid=Подпись математически корректна, НО:
|
validateSignature.signature.mathValid=Підпис математично коректний, АЛЕ:
|
||||||
validateSignature.selectCustomCert=Користувачський файл сертифіката X.509 (Необов'язково)
|
validateSignature.selectCustomCert=Користувацький файл сертифіката X.509 (Необов'язково)
|
||||||
validateSignature.cert.info=Сведения про сертифікати
|
validateSignature.cert.info=Інформація про сертифікат
|
||||||
validateSignature.cert.issuer=Издатель
|
validateSignature.cert.issuer=Видавець
|
||||||
validateSignature.cert.subject=суб'єкт
|
validateSignature.cert.subject=Суб'єкт
|
||||||
validateSignature.cert.serialNumber=Серийний номер
|
validateSignature.cert.serialNumber=Серійний номер
|
||||||
validateSignature.cert.validFrom=Дійсний з
|
validateSignature.cert.validFrom=Дійсний з
|
||||||
validateSignature.cert.validUntil=Дійсний до
|
validateSignature.cert.validUntil=Дійсний до
|
||||||
validateSignature.cert.algorithm=Алгоритм
|
validateSignature.cert.algorithm=Алгоритм
|
||||||
validateSignature.cert.keySize=Розмір ключа
|
validateSignature.cert.keySize=Розмір ключа
|
||||||
validateSignature.cert.version=Версія
|
validateSignature.cert.version=Версія
|
||||||
validateSignature.cert.keyUsage=Використання ключа
|
validateSignature.cert.keyUsage=Використання ключа
|
||||||
validateSignature.cert.selfSigned=Самоподписанный
|
validateSignature.cert.selfSigned=Самопідписаний
|
||||||
validateSignature.cert.bits=біт
|
validateSignature.cert.bits=біт
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Cookie banner #
|
# Cookie banner #
|
||||||
####################
|
####################
|
||||||
cookieBanner.popUp.title=How we use Cookies
|
cookieBanner.popUp.title=Як ми використовуємо файли cookie
|
||||||
cookieBanner.popUp.description.1=We use cookies and other technologies to make Stirling PDF work better for you—helping us improve our tools and keep building features you'll love.
|
cookieBanner.popUp.description.1=Ми використовуємо файли cookie та інші технології, щоб Stirling PDF працював краще для вас — допомагаючи нам покращувати наші інструменти та створювати функції, які вам сподобаються.
|
||||||
cookieBanner.popUp.description.2=If you’d rather not, clicking 'No Thanks' will only enable the essential cookies needed to keep things running smoothly.
|
cookieBanner.popUp.description.2=Якщо ви не хочете, натискання «Ні, дякую» увімкне лише необхідні файли cookie, потрібні для безперебійної роботи.
|
||||||
cookieBanner.popUp.acceptAllBtn=Okay
|
cookieBanner.popUp.acceptAllBtn=Добре
|
||||||
cookieBanner.popUp.acceptNecessaryBtn=No Thanks
|
cookieBanner.popUp.acceptNecessaryBtn=Ні, дякую
|
||||||
cookieBanner.popUp.showPreferencesBtn=Manage preferences
|
cookieBanner.popUp.showPreferencesBtn=Керувати налаштуваннями
|
||||||
cookieBanner.preferencesModal.title=Consent Preferences Center
|
cookieBanner.preferencesModal.title=Центр налаштувань згоди
|
||||||
cookieBanner.preferencesModal.acceptAllBtn=Accept all
|
cookieBanner.preferencesModal.acceptAllBtn=Прийняти всі
|
||||||
cookieBanner.preferencesModal.acceptNecessaryBtn=Reject all
|
cookieBanner.preferencesModal.acceptNecessaryBtn=Відхилити всі
|
||||||
cookieBanner.preferencesModal.savePreferencesBtn=Save preferences
|
cookieBanner.preferencesModal.savePreferencesBtn=Зберегти налаштування
|
||||||
cookieBanner.preferencesModal.closeIconLabel=Close modal
|
cookieBanner.preferencesModal.closeIconLabel=Закрити модальне вікно
|
||||||
cookieBanner.preferencesModal.serviceCounterLabel=Service|Services
|
cookieBanner.preferencesModal.serviceCounterLabel=Сервіс|Сервіси
|
||||||
cookieBanner.preferencesModal.subtitle=Cookie Usage
|
cookieBanner.preferencesModal.subtitle=Використання файлів cookie
|
||||||
cookieBanner.preferencesModal.description.1=Stirling PDF uses cookies and similar technologies to enhance your experience and understand how our tools are used. This helps us improve performance, develop the features you care about, and provide ongoing support to our users.
|
cookieBanner.preferencesModal.description.1=Stirling PDF використовує файли cookie та подібні технології, щоб покращити ваш досвід і зрозуміти, як використовуються наші інструменти. Це допомагає нам покращувати продуктивність, розробляти функції, які вас цікавлять, і надавати постійну підтримку нашим користувачам.
|
||||||
cookieBanner.preferencesModal.description.2=Stirling PDF cannot—and will never—track or access the content of the documents you use.
|
cookieBanner.preferencesModal.description.2=Stirling PDF не може — і ніколи не буде — відстежувати або отримувати доступ до вмісту документів, які ви використовуєте.
|
||||||
cookieBanner.preferencesModal.description.3=Your privacy and trust are at the core of what we do.
|
cookieBanner.preferencesModal.description.3=Ваша конфіденційність і довіра є основою того, що ми робимо.
|
||||||
cookieBanner.preferencesModal.necessary.title.1=Strictly Necessary Cookies
|
cookieBanner.preferencesModal.necessary.title.1=Суворо необхідні файли cookie
|
||||||
cookieBanner.preferencesModal.necessary.title.2=Always Enabled
|
cookieBanner.preferencesModal.necessary.title.2=Завжди увімкнені
|
||||||
cookieBanner.preferencesModal.necessary.description=These cookies are essential for the website to function properly. They enable core features like setting your privacy preferences, logging in, and filling out forms—which is why they can’t be turned off.
|
cookieBanner.preferencesModal.necessary.description=Ці файли cookie є необхідними для правильного функціонування вебсайту. Вони забезпечують основні функції, такі як налаштування ваших уподобань конфіденційності, вхід у систему та заповнення форм — тому їх не можна вимкнути.
|
||||||
cookieBanner.preferencesModal.analytics.title=Analytics
|
cookieBanner.preferencesModal.analytics.title=Аналітика
|
||||||
cookieBanner.preferencesModal.analytics.description=These cookies help us understand how our tools are being used, so we can focus on building the features our community values most. Rest assured—Stirling PDF cannot and will never track the content of the documents you work with.
|
cookieBanner.preferencesModal.analytics.description=Ці файли cookie допомагають нам зрозуміти, як використовуються наші інструменти, щоб ми могли зосередитися на створенні функцій, які найбільше цінує наша спільнота. Будьте впевнені — Stirling PDF не може і ніколи не буде відстежувати вміст документів, з якими ви працюєте.
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.config.security.mail;
|
package stirling.software.SPDF.config.security.mail;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -57,4 +59,111 @@ public class EmailServiceTest {
|
|||||||
// Verify that the email was sent using mailSender
|
// Verify that the email was sent using mailSender
|
||||||
verify(mailSender).send(mimeMessage);
|
verify(mailSender).send(mimeMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailWithAttachmentThrowsExceptionForMissingFilename() throws MessagingException {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setTo("test@example.com");
|
||||||
|
email.setSubject("Test Email");
|
||||||
|
email.setBody("This is a test email.");
|
||||||
|
email.setFileInput(fileInput);
|
||||||
|
|
||||||
|
when(fileInput.isEmpty()).thenReturn(false);
|
||||||
|
when(fileInput.getOriginalFilename()).thenReturn("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailService.sendEmailWithAttachment(email);
|
||||||
|
fail("Expected MessagingException to be thrown");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
assertEquals("An attachment is required to send the email.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailWithAttachmentThrowsExceptionForMissingFilenameNull()
|
||||||
|
throws MessagingException {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setTo("test@example.com");
|
||||||
|
email.setSubject("Test Email");
|
||||||
|
email.setBody("This is a test email.");
|
||||||
|
email.setFileInput(fileInput);
|
||||||
|
|
||||||
|
when(fileInput.isEmpty()).thenReturn(false);
|
||||||
|
when(fileInput.getOriginalFilename()).thenReturn(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailService.sendEmailWithAttachment(email);
|
||||||
|
fail("Expected MessagingException to be thrown");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
assertEquals("An attachment is required to send the email.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailWithAttachmentThrowsExceptionForMissingFile() throws MessagingException {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setTo("test@example.com");
|
||||||
|
email.setSubject("Test Email");
|
||||||
|
email.setBody("This is a test email.");
|
||||||
|
email.setFileInput(fileInput);
|
||||||
|
|
||||||
|
when(fileInput.isEmpty()).thenReturn(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailService.sendEmailWithAttachment(email);
|
||||||
|
fail("Expected MessagingException to be thrown");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
assertEquals("An attachment is required to send the email.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailWithAttachmentThrowsExceptionForMissingFileNull() throws MessagingException {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setTo("test@example.com");
|
||||||
|
email.setSubject("Test Email");
|
||||||
|
email.setBody("This is a test email.");
|
||||||
|
email.setFileInput(null); // Missing file
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailService.sendEmailWithAttachment(email);
|
||||||
|
fail("Expected MessagingException to be thrown");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
assertEquals("An attachment is required to send the email.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailWithAttachmentThrowsExceptionForInvalidAddressNull()
|
||||||
|
throws MessagingException {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setTo(null); // Invalid address
|
||||||
|
email.setSubject("Test Email");
|
||||||
|
email.setBody("This is a test email.");
|
||||||
|
email.setFileInput(fileInput);
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailService.sendEmailWithAttachment(email);
|
||||||
|
fail("Expected MailSendException to be thrown");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
assertEquals("Invalid Addresses", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailWithAttachmentThrowsExceptionForInvalidAddressEmpty()
|
||||||
|
throws MessagingException {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setTo(""); // Invalid address
|
||||||
|
email.setSubject("Test Email");
|
||||||
|
email.setBody("This is a test email.");
|
||||||
|
email.setFileInput(fileInput);
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailService.sendEmailWithAttachment(email);
|
||||||
|
fail("Expected MailSendException to be thrown");
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
assertEquals("Invalid Addresses", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package stirling.software.SPDF.config.security.mail;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
|
class MailConfigTest {
|
||||||
|
|
||||||
|
private ApplicationProperties.Mail mailProps;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void initMailProperties() {
|
||||||
|
mailProps = mock(ApplicationProperties.Mail.class);
|
||||||
|
when(mailProps.getHost()).thenReturn("smtp.example.com");
|
||||||
|
when(mailProps.getPort()).thenReturn(587);
|
||||||
|
when(mailProps.getUsername()).thenReturn("user@example.com");
|
||||||
|
when(mailProps.getPassword()).thenReturn("password");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConfigureJavaMailSenderWithCorrectProperties() {
|
||||||
|
ApplicationProperties appProps = mock(ApplicationProperties.class);
|
||||||
|
when(appProps.getMail()).thenReturn(mailProps);
|
||||||
|
|
||||||
|
MailConfig config = new MailConfig(appProps);
|
||||||
|
JavaMailSender sender = config.javaMailSender();
|
||||||
|
|
||||||
|
assertInstanceOf(JavaMailSenderImpl.class, sender);
|
||||||
|
JavaMailSenderImpl impl = (JavaMailSenderImpl) sender;
|
||||||
|
|
||||||
|
Properties props = impl.getJavaMailProperties();
|
||||||
|
|
||||||
|
assertAll(
|
||||||
|
"SMTP configuration",
|
||||||
|
() -> assertEquals("smtp.example.com", impl.getHost()),
|
||||||
|
() -> assertEquals(587, impl.getPort()),
|
||||||
|
() -> assertEquals("user@example.com", impl.getUsername()),
|
||||||
|
() -> assertEquals("password", impl.getPassword()),
|
||||||
|
() -> assertEquals("UTF-8", impl.getDefaultEncoding()),
|
||||||
|
() -> assertEquals("true", props.getProperty("mail.smtp.auth")),
|
||||||
|
() -> assertEquals("true", props.getProperty("mail.smtp.starttls.enable")));
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,25 @@
|
|||||||
package stirling.software.SPDF.controller.api;
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.mail.MailSendException;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import jakarta.mail.MessagingException;
|
import jakarta.mail.MessagingException;
|
||||||
|
|
||||||
@ -20,7 +27,7 @@ import stirling.software.SPDF.config.security.mail.EmailService;
|
|||||||
import stirling.software.SPDF.model.api.Email;
|
import stirling.software.SPDF.model.api.Email;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
public class EmailControllerTest {
|
class EmailControllerTest {
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@ -28,59 +35,61 @@ public class EmailControllerTest {
|
|||||||
|
|
||||||
@InjectMocks private EmailController emailController;
|
@InjectMocks private EmailController emailController;
|
||||||
|
|
||||||
@Mock private MultipartFile fileInput;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
// Set up the MockMvc instance for testing
|
|
||||||
mockMvc = MockMvcBuilders.standaloneSetup(emailController).build();
|
mockMvc = MockMvcBuilders.standaloneSetup(emailController).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "Case {index}: exception={0}, includeTo={1}")
|
||||||
void testSendEmailWithAttachmentSuccess() throws Exception {
|
@MethodSource("emailParams")
|
||||||
// Create a mock Email object
|
void shouldHandleEmailRequests(
|
||||||
Email email = new Email();
|
Exception serviceException,
|
||||||
email.setTo("test@example.com");
|
boolean includeTo,
|
||||||
email.setSubject("Test Email");
|
int expectedStatus,
|
||||||
email.setBody("This is a test email.");
|
String expectedContent)
|
||||||
email.setFileInput(fileInput);
|
throws Exception {
|
||||||
|
if (serviceException == null) {
|
||||||
// Mock the service to not throw any exception
|
|
||||||
doNothing().when(emailService).sendEmailWithAttachment(any(Email.class));
|
doNothing().when(emailService).sendEmailWithAttachment(any(Email.class));
|
||||||
|
} else {
|
||||||
// Perform the request and verify the response
|
doThrow(serviceException).when(emailService).sendEmailWithAttachment(any(Email.class));
|
||||||
mockMvc.perform(
|
|
||||||
multipart("/api/v1/general/send-email")
|
|
||||||
.file("fileInput", "dummy-content".getBytes())
|
|
||||||
.param("to", email.getTo())
|
|
||||||
.param("subject", email.getSubject())
|
|
||||||
.param("body", email.getBody()))
|
|
||||||
.andExpect(status().isOk())
|
|
||||||
.andExpect(content().string("Email sent successfully"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
var request =
|
||||||
void testSendEmailWithAttachmentFailure() throws Exception {
|
|
||||||
// Create a mock Email object
|
|
||||||
Email email = new Email();
|
|
||||||
email.setTo("test@example.com");
|
|
||||||
email.setSubject("Test Email");
|
|
||||||
email.setBody("This is a test email.");
|
|
||||||
email.setFileInput(fileInput);
|
|
||||||
|
|
||||||
// Mock the service to throw a MessagingException
|
|
||||||
doThrow(new MessagingException("Failed to send email"))
|
|
||||||
.when(emailService)
|
|
||||||
.sendEmailWithAttachment(any(Email.class));
|
|
||||||
|
|
||||||
// Perform the request and verify the response
|
|
||||||
mockMvc.perform(
|
|
||||||
multipart("/api/v1/general/send-email")
|
multipart("/api/v1/general/send-email")
|
||||||
.file("fileInput", "dummy-content".getBytes())
|
.file("fileInput", "dummy-content".getBytes())
|
||||||
.param("to", email.getTo())
|
.param("subject", "Test Email")
|
||||||
.param("subject", email.getSubject())
|
.param("body", "This is a test email.");
|
||||||
.param("body", email.getBody()))
|
|
||||||
.andExpect(status().isInternalServerError())
|
if (includeTo) {
|
||||||
.andExpect(content().string("Failed to send email: Failed to send email"));
|
request = request.param("to", "test@example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
mockMvc.perform(request)
|
||||||
|
.andExpect(status().is(expectedStatus))
|
||||||
|
.andExpect(content().string(expectedContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> emailParams() {
|
||||||
|
return Stream.of(
|
||||||
|
// success case
|
||||||
|
Arguments.of(null, true, 200, "Email sent successfully"),
|
||||||
|
// generic messaging error
|
||||||
|
Arguments.of(
|
||||||
|
new MessagingException("Failed to send email"),
|
||||||
|
true,
|
||||||
|
500,
|
||||||
|
"Failed to send email: Failed to send email"),
|
||||||
|
// missing 'to' results in MailSendException
|
||||||
|
Arguments.of(
|
||||||
|
new MailSendException("Invalid Addresses"),
|
||||||
|
false,
|
||||||
|
500,
|
||||||
|
"Invalid Addresses"),
|
||||||
|
// invalid email address formatting
|
||||||
|
Arguments.of(
|
||||||
|
new MessagingException("Invalid Addresses"),
|
||||||
|
true,
|
||||||
|
500,
|
||||||
|
"Failed to send email: Invalid Addresses"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
|
class UploadLimitServiceTest {
|
||||||
|
|
||||||
|
private UploadLimitService uploadLimitService;
|
||||||
|
private ApplicationProperties applicationProperties;
|
||||||
|
private ApplicationProperties.System systemProps;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
applicationProperties = mock(ApplicationProperties.class);
|
||||||
|
systemProps = mock(ApplicationProperties.System.class);
|
||||||
|
when(applicationProperties.getSystem()).thenReturn(systemProps);
|
||||||
|
|
||||||
|
uploadLimitService = new UploadLimitService();
|
||||||
|
// inject mock
|
||||||
|
try {
|
||||||
|
var field = UploadLimitService.class.getDeclaredField("applicationProperties");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(uploadLimitService, applicationProperties);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "getUploadLimit case #{index}: input={0}, expected={1}")
|
||||||
|
@MethodSource("uploadLimitParams")
|
||||||
|
void shouldComputeUploadLimitCorrectly(String input, long expected) {
|
||||||
|
when(systemProps.getFileUploadLimit()).thenReturn(input);
|
||||||
|
|
||||||
|
long result = uploadLimitService.getUploadLimit();
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> uploadLimitParams() {
|
||||||
|
return Stream.of(
|
||||||
|
// empty or null input yields 0
|
||||||
|
Arguments.of(null, 0L),
|
||||||
|
Arguments.of("", 0L),
|
||||||
|
// invalid formats
|
||||||
|
Arguments.of("1234MB", 0L),
|
||||||
|
Arguments.of("5TB", 0L),
|
||||||
|
// valid formats
|
||||||
|
Arguments.of("10KB", 10 * 1024L),
|
||||||
|
Arguments.of("2MB", 2 * 1024 * 1024L),
|
||||||
|
Arguments.of("1GB", 1L * 1024 * 1024 * 1024),
|
||||||
|
Arguments.of("5mb", 5 * 1024 * 1024L),
|
||||||
|
Arguments.of("0MB", 0L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "getReadableUploadLimit case #{index}: rawValue={0}, expected={1}")
|
||||||
|
@MethodSource("readableLimitParams")
|
||||||
|
void shouldReturnReadableFormat(String rawValue, String expected) {
|
||||||
|
when(systemProps.getFileUploadLimit()).thenReturn(rawValue);
|
||||||
|
String result = uploadLimitService.getReadableUploadLimit();
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> readableLimitParams() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(null, "0 B"),
|
||||||
|
Arguments.of("", "0 B"),
|
||||||
|
Arguments.of("1KB", "1.0 KB"),
|
||||||
|
Arguments.of("2MB", "2.0 MB"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user