UNITTEST ERA
Some checks failed
Build and Push Quarkus Image / build-and-push (push) Has been cancelled
Some checks failed
Build and Push Quarkus Image / build-and-push (push) Has been cancelled
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -24,7 +24,7 @@
|
|||||||
<sonar.qualitygate.wait>true</sonar.qualitygate.wait>
|
<sonar.qualitygate.wait>true</sonar.qualitygate.wait>
|
||||||
|
|
||||||
<sonar.coverage.jacoco.xmlReportPaths>target/jacoco-report/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
|
<sonar.coverage.jacoco.xmlReportPaths>target/jacoco-report/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
|
||||||
<sonar.coverage.exclusions>**/domain/**, **/config/**, **/*Exception.java, **/entity/**</sonar.coverage.exclusions>
|
<sonar.coverage.exclusions>**/domain/**, **/config/**, **/*Exception.java, **/entity/**, **/test/java/**</sonar.coverage.exclusions>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -105,6 +105,11 @@
|
|||||||
<artifactId>quarkus-jacoco</artifactId>
|
<artifactId>quarkus-jacoco</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-junit5-mockito</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ package nl.veenm.jobfindr.repository;
|
|||||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import nl.veenm.jobfindr.domain.Event;
|
import nl.veenm.jobfindr.domain.Event;
|
||||||
import nl.veenm.jobfindr.domain.VacatureDetail;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class EventRepository implements PanacheRepository<Event> {
|
public class EventRepository implements PanacheRepository<Event> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,5 @@ import nl.veenm.jobfindr.domain.VacatureDetail;
|
|||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class VacatureDetailRepository implements PanacheRepository<VacatureDetail> {
|
public class VacatureDetailRepository implements PanacheRepository<VacatureDetail> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,8 @@ import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
|||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import nl.veenm.jobfindr.domain.Vacature;
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class VacatureRepository implements PanacheRepository<Vacature> {
|
public class VacatureRepository implements PanacheRepository<Vacature> {
|
||||||
|
|
||||||
public List<Vacature> findByDate(LocalDate date) {
|
|
||||||
return list("datum", date);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ import java.util.List;
|
|||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public class VacatureResource {
|
public class VacatureResource {
|
||||||
private static final org.jboss.logging.Logger logger = Logger.getLogger(VacatureService.class);
|
private static final org.jboss.logging.Logger logger = Logger.getLogger(VacatureResource.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
public VacatureResource(VacatureService vacatureService) {
|
||||||
|
this.vacatureService = vacatureService;
|
||||||
|
}
|
||||||
|
|
||||||
VacatureService vacatureService;
|
VacatureService vacatureService;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -25,7 +29,7 @@ public class VacatureResource {
|
|||||||
return vacatureService.getNewVacatures();
|
return vacatureService.getNewVacatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@PATCH
|
||||||
@Path("/test")
|
@Path("/test")
|
||||||
public void testEmails(){
|
public void testEmails(){
|
||||||
logger.info("testEmails");
|
logger.info("testEmails");
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ import java.util.List;
|
|||||||
public class EmailService {
|
public class EmailService {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
public EmailService(Mailer mailer, EventService eventService) {
|
||||||
|
this.mailer = mailer;
|
||||||
|
this.eventService = eventService;
|
||||||
|
}
|
||||||
|
|
||||||
EventService eventService;
|
EventService eventService;
|
||||||
|
|
||||||
private final String className = this.getClass().getSimpleName();
|
private final String className = this.getClass().getSimpleName();
|
||||||
@@ -23,11 +28,8 @@ public class EmailService {
|
|||||||
|
|
||||||
private final Mailer mailer;
|
private final Mailer mailer;
|
||||||
|
|
||||||
public EmailService(Mailer mailer) {
|
|
||||||
this.mailer = mailer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stuurVacatureEmail(String recipient, List<Vacature> vacatures) {
|
public void stuurVacatureEmail(String recipient, List<Vacature> vacatures) {
|
||||||
|
String methodName = "stuurVacatureEmail";
|
||||||
// Bouw de HTML-inhoud van de e-mail
|
// Bouw de HTML-inhoud van de e-mail
|
||||||
StringBuilder emailBody = new StringBuilder();
|
StringBuilder emailBody = new StringBuilder();
|
||||||
emailBody.append("<html>")
|
emailBody.append("<html>")
|
||||||
@@ -76,12 +78,12 @@ public class EmailService {
|
|||||||
|
|
||||||
// Verstuur de e-mail met HTML-inhoud
|
// Verstuur de e-mail met HTML-inhoud
|
||||||
log.info("Sending email to " + recipient);
|
log.info("Sending email to " + recipient);
|
||||||
eventService.logInfo(className, "stuurVacatureEmail", "Sending email to " + recipient);
|
eventService.logInfo(className, methodName, "Sending email to " + recipient);
|
||||||
try{
|
try{
|
||||||
mailer.send(Mail.withHtml(recipient, subject, emailBody.toString()));
|
mailer.send(Mail.withHtml(recipient, subject, emailBody.toString()));
|
||||||
eventService.logSucces(className, "stuurVacatureEmail", "Email sent");
|
eventService.logSucces(className, methodName, "Email sent");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
eventService.logError(className, "stuurVacatureEmail", "Sending email failed", e);
|
eventService.logError(className, methodName, "Sending email failed", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,21 +18,25 @@ import java.util.List;
|
|||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class VacatureService {
|
public class VacatureService {
|
||||||
@Inject
|
|
||||||
ScraperService scraperService;
|
ScraperService scraperService;
|
||||||
|
|
||||||
@Inject
|
|
||||||
EventService eventService;
|
EventService eventService;
|
||||||
|
|
||||||
@Inject
|
|
||||||
EmailService emailService;
|
EmailService emailService;
|
||||||
|
|
||||||
@Inject
|
|
||||||
VacatureRepository vacatureRepository;
|
VacatureRepository vacatureRepository;
|
||||||
|
|
||||||
@Inject
|
|
||||||
VacatureDetailRepository vacatureDetailRepository;
|
VacatureDetailRepository vacatureDetailRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public VacatureService(ScraperService scraperService, EventService eventService, EmailService emailService, VacatureRepository vacatureRepository, VacatureDetailRepository vacatureDetailRepository) {
|
||||||
|
this.scraperService = scraperService;
|
||||||
|
this.eventService = eventService;
|
||||||
|
this.emailService = emailService;
|
||||||
|
this.vacatureRepository = vacatureRepository;
|
||||||
|
this.vacatureDetailRepository = vacatureDetailRepository;
|
||||||
|
}
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(VacatureService.class);
|
private static final Logger logger = Logger.getLogger(VacatureService.class);
|
||||||
|
|
||||||
private final String className = this.getClass().getSimpleName();
|
private final String className = this.getClass().getSimpleName();
|
||||||
@@ -46,8 +50,9 @@ public class VacatureService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void checkAndSendNewVacatures() {
|
public void checkAndSendNewVacatures() {
|
||||||
|
String methodName = "checkAndSendNewVacatures";
|
||||||
logger.info("Checking for new vacatures");
|
logger.info("Checking for new vacatures");
|
||||||
eventService.logInfo(className, "checkAndSendNewVacatures", "Checking for new vacatures");
|
eventService.logInfo(className, methodName, "Checking for new vacatures");
|
||||||
LocalDate today = LocalDate.now();
|
LocalDate today = LocalDate.now();
|
||||||
List<Vacature> todayVacatures = getVacatures();
|
List<Vacature> todayVacatures = getVacatures();
|
||||||
|
|
||||||
@@ -55,7 +60,7 @@ public class VacatureService {
|
|||||||
try {
|
try {
|
||||||
scraperService.getVacatureDetails(todayVacature);
|
scraperService.getVacatureDetails(todayVacature);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
eventService.logError(className, "checkAndSendNewVacatures", "checkAndSendNewVacatures failed", e);
|
eventService.logError(className, methodName, "checkAndSendNewVacatures failed", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,10 +81,10 @@ public class VacatureService {
|
|||||||
emailService.stuurVacatureEmail("danthefranken@gmail.com", todayVacatures);
|
emailService.stuurVacatureEmail("danthefranken@gmail.com", todayVacatures);
|
||||||
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", todayVacatures);
|
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", todayVacatures);
|
||||||
logger.info("Nieuwe vacatures verstuurd en opgeslagen.");
|
logger.info("Nieuwe vacatures verstuurd en opgeslagen.");
|
||||||
eventService.logSucces(className, "checkAndSendNewVacatures", "Nieuwe vacatures verstuurd en opgeslagen.");
|
eventService.logSucces(className, methodName, "Nieuwe vacatures verstuurd en opgeslagen.");
|
||||||
} else {
|
} else {
|
||||||
logger.info("Geen nieuwe vacatures gevonden.");
|
logger.info("Geen nieuwe vacatures gevonden.");
|
||||||
eventService.logInfo(className, "checkAndSendNewVacatures", "Geen nieuwe vacatures gevonden.");
|
eventService.logInfo(className, methodName, "Geen nieuwe vacatures gevonden.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +100,7 @@ public class VacatureService {
|
|||||||
try {
|
try {
|
||||||
todayVacatures.addAll(scraperService.scrapeVacatures(url));
|
todayVacatures.addAll(scraperService.scrapeVacatures(url));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
eventService.logError(className, "getVacatures", "getVacatures failed", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return todayVacatures;
|
return todayVacatures;
|
||||||
@@ -116,21 +121,22 @@ public class VacatureService {
|
|||||||
@Scheduled(cron = "0 0 0 * * ?")
|
@Scheduled(cron = "0 0 0 * * ?")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void cleanVacatures() {
|
public void cleanVacatures() {
|
||||||
|
String methodName = "cleanVacatures";
|
||||||
logger.info("Cleaning vacatures");
|
logger.info("Cleaning vacatures");
|
||||||
eventService.logInfo(className, "cleanVacatures", "Cleaning vacatures");
|
eventService.logInfo(className, methodName, "Cleaning vacatures");
|
||||||
getAllVacatures().forEach(vacature -> {
|
getAllVacatures().forEach(vacature -> {
|
||||||
var date = vacature.getClosingDate();
|
var date = vacature.getClosingDate();
|
||||||
var today = LocalDate.now();
|
var today = LocalDate.now();
|
||||||
|
|
||||||
if (date.isBefore(today)) {
|
if (date.isBefore(today)) {
|
||||||
logger.info("Deleting vacature " + vacature.getTitel());
|
logger.info("Deleting vacature " + vacature.getTitel());
|
||||||
eventService.logInfo(className, "cleanVacatures", "Deleting vacature " + vacature.getTitel());
|
eventService.logInfo(className, methodName, "Deleting vacature " + vacature.getTitel());
|
||||||
vacatureDetailRepository.delete(vacature.getDetail());
|
vacatureDetailRepository.delete(vacature.getDetail());
|
||||||
vacatureRepository.delete(vacature);
|
vacatureRepository.delete(vacature);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
logger.info("Cleaning vacatures done");
|
logger.info("Cleaning vacatures done");
|
||||||
eventService.logSucces(className, "cleanVacatures", "Cleaning vacatures done");
|
eventService.logSucces(className, methodName, "Cleaning vacatures done");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import java.time.Instant;
|
|||||||
public class EventService {
|
public class EventService {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
public EventService(EventRepository eventRepository) {
|
||||||
|
this.eventRepository = eventRepository;
|
||||||
|
}
|
||||||
|
|
||||||
EventRepository eventRepository;
|
EventRepository eventRepository;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package nl.veenm.jobfindr.resources;
|
||||||
|
|
||||||
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
|
import nl.veenm.jobfindr.services.VacatureService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class VacatureResourceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VacatureService vacatureService;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
VacatureResource vacatureResource;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getNewVacatures_ReturnsListFromService() {
|
||||||
|
List<Vacature> expectedVacatures = List.of(new Vacature(), new Vacature());
|
||||||
|
when(vacatureService.getNewVacatures()).thenReturn(expectedVacatures);
|
||||||
|
|
||||||
|
List<Vacature> result = vacatureResource.getNewVacatures();
|
||||||
|
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals(expectedVacatures, result);
|
||||||
|
verify(vacatureService).getNewVacatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmails_CallsCheckAndSendNewVacatures() {
|
||||||
|
vacatureResource.testEmails();
|
||||||
|
|
||||||
|
verify(vacatureService).checkAndSendNewVacatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAllVacatures_ReturnsListFromService() {
|
||||||
|
List<Vacature> expectedVacatures = List.of(new Vacature());
|
||||||
|
when(vacatureService.getAllVacatures()).thenReturn(expectedVacatures);
|
||||||
|
|
||||||
|
List<Vacature> result = vacatureResource.getAllVacatures();
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
assertEquals(expectedVacatures, result);
|
||||||
|
verify(vacatureService).getAllVacatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cleanVacatures_CallsCleanVacaturesOnService() {
|
||||||
|
vacatureResource.cleanVacatures();
|
||||||
|
|
||||||
|
verify(vacatureService).cleanVacatures();
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/test/java/nl/veenm/jobfindr/scrapers/ScraperServiceTest.java
Normal file
103
src/test/java/nl/veenm/jobfindr/scrapers/ScraperServiceTest.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package nl.veenm.jobfindr.scrapers;
|
||||||
|
|
||||||
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
|
import org.jsoup.Connection;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ScraperServiceTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
ScraperService scraperService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getVacatureDetails_HaaltBeschrijvingEnDatumOp() throws IOException {
|
||||||
|
String detailHtml = "<html><body>" +
|
||||||
|
"<div class=\"rounded gray-tag-md px-2 py-2\">0,8 FTE</div>" +
|
||||||
|
"<div class=\"rounded gray-tag-md px-2 py-2\">Sluitingsdatum: 15-04-2026</div>" +
|
||||||
|
"</body></html>";
|
||||||
|
Document mockDocument = Jsoup.parse(detailHtml);
|
||||||
|
|
||||||
|
Connection mockConnection = mock(Connection.class);
|
||||||
|
when(mockConnection.get()).thenReturn(mockDocument);
|
||||||
|
|
||||||
|
Vacature vacature = new Vacature("Test", "https://fake-url.com", "Apeldoorn");
|
||||||
|
|
||||||
|
try (MockedStatic<Jsoup> mockedJsoup = mockStatic(Jsoup.class)) {
|
||||||
|
mockedJsoup.when(() -> Jsoup.connect("https://fake-url.com")).thenReturn(mockConnection);
|
||||||
|
|
||||||
|
scraperService.getVacatureDetails(vacature);
|
||||||
|
|
||||||
|
assertEquals("0,8 FTE<br>Sluitingsdatum: 15-04-2026<br>", vacature.getDescription());
|
||||||
|
assertEquals(LocalDate.of(2026, 4, 15), vacature.getClosingDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void scrapeVacatures_HaaltLijstOpEnFiltertOnbekendeLocaties() throws IOException {
|
||||||
|
String listHtml = "<html><body>" +
|
||||||
|
" <div>" +
|
||||||
|
" <div class='mt-2 text-overflow-ellipsis'>" +
|
||||||
|
" <div class='heading-6'>Docent Geschiedenis</div>" +
|
||||||
|
" <a class='vacature-target' data-target='/split/vacature/123'>Link</a>" +
|
||||||
|
" </div>" +
|
||||||
|
" <div class='location mt-1'>Apeldoorn</div>" +
|
||||||
|
" </div>" +
|
||||||
|
" <div>" +
|
||||||
|
" <div class='mt-2 text-overflow-ellipsis'>" +
|
||||||
|
" <div class='heading-6'>Docent Wiskunde</div>" +
|
||||||
|
" <a class='vacature-target' data-target='/vacature/456'>Link</a>" +
|
||||||
|
" </div>" +
|
||||||
|
" </div>" +
|
||||||
|
"</body></html>";
|
||||||
|
|
||||||
|
String detailHtml = "<html><body><div class='rounded gray-tag-md px-2 py-2'>Sluitingsdatum: 01-05-2026</div></body></html>";
|
||||||
|
String mainUrl = "https://www.meesterbaan.nl/zoeken";
|
||||||
|
|
||||||
|
// FIX: We parsen de HTML nu hier, buiten het bereik van de mock!
|
||||||
|
Document listDocument = Jsoup.parse(listHtml);
|
||||||
|
Document detailDocument = Jsoup.parse(detailHtml);
|
||||||
|
|
||||||
|
try (MockedStatic<Jsoup> mockedJsoup = mockStatic(Jsoup.class)) {
|
||||||
|
mockedJsoup.when(() -> Jsoup.connect(anyString())).thenAnswer(invocation -> {
|
||||||
|
String url = invocation.getArgument(0);
|
||||||
|
Connection connection = mock(Connection.class);
|
||||||
|
|
||||||
|
// We geven nu direct de kant-en-klare Document objecten terug
|
||||||
|
if (url.equals(mainUrl)) {
|
||||||
|
when(connection.get()).thenReturn(listDocument);
|
||||||
|
} else {
|
||||||
|
when(connection.get()).thenReturn(detailDocument);
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
List<Vacature> results = scraperService.scrapeVacatures(mainUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(1, results.size());
|
||||||
|
|
||||||
|
Vacature v = results.getFirst();
|
||||||
|
assertEquals("Docent Geschiedenis", v.getTitel());
|
||||||
|
assertEquals("https://www.meesterbaan.nl/vacature/123", v.getUrl());
|
||||||
|
assertEquals("Apeldoorn", v.getLocatie());
|
||||||
|
assertEquals("Sluitingsdatum: 01-05-2026<br>", v.getDescription());
|
||||||
|
assertEquals(LocalDate.of(2026, 5, 1), v.getClosingDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package nl.veenm.jobfindr.services;
|
||||||
|
|
||||||
|
import io.quarkus.mailer.Mail;
|
||||||
|
import io.quarkus.mailer.Mailer;
|
||||||
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
|
import nl.veenm.jobfindr.util.EventService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class EmailServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
EventService eventService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
Mailer mailer;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
EmailService emailService;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<Mail> mailCaptor;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void stuurVacatureEmail_VerstuurtEmailEnLogtSucces() {
|
||||||
|
Vacature vacature = mock(Vacature.class);
|
||||||
|
when(vacature.getTitel()).thenReturn("Java Developer");
|
||||||
|
when(vacature.getDescription()).thenReturn("Mooie Quarkus rol");
|
||||||
|
when(vacature.getUrl()).thenReturn("https://example.com/vacature/1");
|
||||||
|
|
||||||
|
emailService.stuurVacatureEmail("henk@example.com", List.of(vacature));
|
||||||
|
|
||||||
|
verify(eventService).logInfo("EmailService", "stuurVacatureEmail", "Sending email to henk@example.com");
|
||||||
|
|
||||||
|
verify(mailer).send(mailCaptor.capture());
|
||||||
|
Mail capturedMail = mailCaptor.getValue();
|
||||||
|
|
||||||
|
assertEquals(1, capturedMail.getTo().size());
|
||||||
|
assertEquals("henk@example.com", capturedMail.getTo().getFirst());
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
String expectedDate = now.getDayOfMonth() + "-" + now.getMonthValue() + "-" + now.getYear();
|
||||||
|
assertEquals("[" + expectedDate + "] Nieuwe vacatures gevonden", capturedMail.getSubject());
|
||||||
|
|
||||||
|
String htmlBody = capturedMail.getHtml();
|
||||||
|
assertTrue(htmlBody.contains("Lieve Danthe"));
|
||||||
|
assertTrue(htmlBody.contains("Java Developer"));
|
||||||
|
assertTrue(htmlBody.contains("Mooie Quarkus rol"));
|
||||||
|
assertTrue(htmlBody.contains("https://example.com/vacature/1"));
|
||||||
|
|
||||||
|
verify(eventService).logSucces("EmailService", "stuurVacatureEmail", "Email sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void stuurVacatureEmail_LogtErrorBijException() {
|
||||||
|
RuntimeException exception = new RuntimeException("Mail server down");
|
||||||
|
doThrow(exception).when(mailer).send(any(Mail.class));
|
||||||
|
|
||||||
|
emailService.stuurVacatureEmail("henk@example.com", List.of());
|
||||||
|
|
||||||
|
verify(eventService).logInfo("EmailService", "stuurVacatureEmail", "Sending email to henk@example.com");
|
||||||
|
verify(eventService).logError("EmailService", "stuurVacatureEmail", "Sending email failed", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package nl.veenm.jobfindr.services;
|
||||||
|
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheQuery;
|
||||||
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
|
import nl.veenm.jobfindr.domain.VacatureDetail;
|
||||||
|
import nl.veenm.jobfindr.repository.VacatureDetailRepository;
|
||||||
|
import nl.veenm.jobfindr.repository.VacatureRepository;
|
||||||
|
import nl.veenm.jobfindr.scrapers.ScraperService;
|
||||||
|
import nl.veenm.jobfindr.util.EventService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class VacatureServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ScraperService scraperService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
EventService eventService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
EmailService emailService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VacatureRepository vacatureRepository;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VacatureDetailRepository vacatureDetailRepository;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
VacatureService vacatureService;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<String> emailCaptor;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<List<Vacature>> listCaptor;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getNewVacatures() throws IOException {
|
||||||
|
Vacature v1 = new Vacature();
|
||||||
|
when(scraperService.scrapeVacatures(anyString())).thenReturn(List.of(v1));
|
||||||
|
|
||||||
|
List<Vacature> result = vacatureService.getNewVacatures();
|
||||||
|
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
|
||||||
|
// Geen eq() nodig omdat we exacte waarden doorgeven zonder matchers
|
||||||
|
verify(eventService).logInfo(VacatureService.class.getName(), "getServices", "getServices aangeroepen");
|
||||||
|
|
||||||
|
// Afvangen in plaats van eq()
|
||||||
|
verify(emailService).stuurVacatureEmail(emailCaptor.capture(), listCaptor.capture());
|
||||||
|
assertEquals("vanveenmel11@gmail.com", emailCaptor.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void checkAndSendNewVacatures_WithNewVacatures() throws IOException {
|
||||||
|
Vacature newVacature = new Vacature();
|
||||||
|
newVacature.setUrl("url1");
|
||||||
|
|
||||||
|
Vacature existingVacature = new Vacature();
|
||||||
|
existingVacature.setUrl("url2");
|
||||||
|
|
||||||
|
when(scraperService.scrapeVacatures(anyString())).thenReturn(List.of(newVacature, existingVacature));
|
||||||
|
|
||||||
|
PanacheQuery<Vacature> query = mock(PanacheQuery.class);
|
||||||
|
when(vacatureRepository.findAll()).thenReturn(query);
|
||||||
|
when(query.stream()).thenReturn(Stream.of(existingVacature));
|
||||||
|
|
||||||
|
vacatureService.checkAndSendNewVacatures();
|
||||||
|
|
||||||
|
verify(scraperService, times(4)).getVacatureDetails(any(Vacature.class));
|
||||||
|
verify(vacatureRepository, times(2)).persist(any(Vacature.class));
|
||||||
|
|
||||||
|
verify(emailService, times(2)).stuurVacatureEmail(emailCaptor.capture(), listCaptor.capture());
|
||||||
|
assertEquals("vanveenmel11@gmail.com", emailCaptor.getValue());
|
||||||
|
|
||||||
|
verify(eventService).logSucces("VacatureService", "checkAndSendNewVacatures", "Nieuwe vacatures verstuurd en opgeslagen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void checkAndSendNewVacatures_NoNewVacatures() throws IOException {
|
||||||
|
Vacature existingVacature = new Vacature();
|
||||||
|
existingVacature.setUrl("url1");
|
||||||
|
|
||||||
|
when(scraperService.scrapeVacatures(anyString())).thenReturn(List.of(existingVacature));
|
||||||
|
|
||||||
|
PanacheQuery<Vacature> query = mock(PanacheQuery.class);
|
||||||
|
when(vacatureRepository.findAll()).thenReturn(query);
|
||||||
|
when(query.stream()).thenReturn(Stream.of(existingVacature));
|
||||||
|
|
||||||
|
vacatureService.checkAndSendNewVacatures();
|
||||||
|
|
||||||
|
verify(vacatureRepository, never()).persist(any(Vacature.class));
|
||||||
|
// Omdat het nooit wordt aangeroepen, kunnen we hier wel any() en anyString() gebruiken
|
||||||
|
verify(emailService, never()).stuurVacatureEmail(anyString(), any());
|
||||||
|
|
||||||
|
verify(eventService).logInfo("VacatureService", "checkAndSendNewVacatures", "Geen nieuwe vacatures gevonden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void checkAndSendNewVacatures_HandlesIOException() throws IOException {
|
||||||
|
Vacature vacature = new Vacature();
|
||||||
|
vacature.setUrl("url1");
|
||||||
|
|
||||||
|
when(scraperService.scrapeVacatures(anyString())).thenReturn(List.of(vacature));
|
||||||
|
|
||||||
|
IOException exception = new IOException("Scrape failed");
|
||||||
|
doThrow(exception).when(scraperService).getVacatureDetails(any(Vacature.class));
|
||||||
|
|
||||||
|
PanacheQuery<Vacature> query = mock(PanacheQuery.class);
|
||||||
|
when(vacatureRepository.findAll()).thenReturn(query);
|
||||||
|
when(query.stream()).thenReturn(Stream.empty());
|
||||||
|
|
||||||
|
vacatureService.checkAndSendNewVacatures();
|
||||||
|
|
||||||
|
// Pass the actual exception object directly
|
||||||
|
verify(eventService, times(2)).logError("VacatureService", "checkAndSendNewVacatures", "checkAndSendNewVacatures failed", exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAllVacatures() {
|
||||||
|
List<Vacature> vacatures = List.of(new Vacature());
|
||||||
|
when(vacatureRepository.listAll()).thenReturn(vacatures);
|
||||||
|
|
||||||
|
List<Vacature> result = vacatureService.getAllVacatures();
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
verify(eventService).logInfo("VacatureService", "getAllVacatures", "fetching all vacatures");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cleanVacatures() {
|
||||||
|
Vacature expired = new Vacature();
|
||||||
|
expired.setTitel("Oude Vacature");
|
||||||
|
expired.setClosingDate(LocalDate.now().minusDays(1));
|
||||||
|
VacatureDetail detail = mock(VacatureDetail.class);
|
||||||
|
expired.setDetail(detail);
|
||||||
|
|
||||||
|
Vacature valid = new Vacature();
|
||||||
|
valid.setClosingDate(LocalDate.now().plusDays(1));
|
||||||
|
|
||||||
|
when(vacatureRepository.listAll()).thenReturn(List.of(expired, valid));
|
||||||
|
|
||||||
|
vacatureService.cleanVacatures();
|
||||||
|
|
||||||
|
verify(vacatureDetailRepository).delete(detail);
|
||||||
|
verify(vacatureRepository).delete(expired);
|
||||||
|
verify(vacatureRepository, never()).delete(valid);
|
||||||
|
|
||||||
|
verify(eventService).logSucces("VacatureService", "cleanVacatures", "Cleaning vacatures done");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
void dagelijksControleerEnVerstuur() {
|
||||||
|
PanacheQuery<Vacature> query = mock(PanacheQuery.class);
|
||||||
|
when(vacatureRepository.findAll()).thenReturn(query);
|
||||||
|
when(query.stream()).thenReturn(Stream.empty());
|
||||||
|
|
||||||
|
vacatureService.dagelijksControleerEnVerstuur();
|
||||||
|
|
||||||
|
verify(eventService).logInfo("VacatureService", "checkAndSendNewVacatures", "Checking for new vacatures");
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/test/java/nl/veenm/jobfindr/util/EventServiceTest.java
Normal file
80
src/test/java/nl/veenm/jobfindr/util/EventServiceTest.java
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package nl.veenm.jobfindr.util;
|
||||||
|
|
||||||
|
import nl.veenm.jobfindr.domain.Event;
|
||||||
|
import nl.veenm.jobfindr.repository.EventRepository;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class EventServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
EventRepository eventRepository;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
EventService eventService;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<Event> eventCaptor;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogInfo_SavesInfoEvent() {
|
||||||
|
eventService.logInfo("MyClass", "myMethod", "Dit is een info bericht");
|
||||||
|
|
||||||
|
verify(eventRepository).persist(eventCaptor.capture());
|
||||||
|
Event capturedEvent = eventCaptor.getValue();
|
||||||
|
|
||||||
|
assertNotNull(capturedEvent.getTimestamp());
|
||||||
|
assertEquals("MyClass", capturedEvent.getClassName());
|
||||||
|
assertEquals("myMethod", capturedEvent.getMethodName());
|
||||||
|
assertEquals("Dit is een info bericht", capturedEvent.getMessage());
|
||||||
|
assertEquals("INFO", capturedEvent.getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogSucces_SavesSuccessEvent() {
|
||||||
|
eventService.logSucces("MyClass", "myMethod", "Dit is gelukt");
|
||||||
|
|
||||||
|
verify(eventRepository).persist(eventCaptor.capture());
|
||||||
|
Event capturedEvent = eventCaptor.getValue();
|
||||||
|
|
||||||
|
assertNotNull(capturedEvent.getTimestamp());
|
||||||
|
assertEquals("MyClass", capturedEvent.getClassName());
|
||||||
|
assertEquals("myMethod", capturedEvent.getMethodName());
|
||||||
|
assertEquals("Dit is gelukt", capturedEvent.getMessage());
|
||||||
|
assertEquals("SUCCESS", capturedEvent.getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogError_SavesErrorEventWithExceptionMessage() {
|
||||||
|
Exception dummyException = new RuntimeException("Database connectie verbroken");
|
||||||
|
|
||||||
|
eventService.logError("MyClass", "myMethod", "Er ging iets mis", dummyException);
|
||||||
|
|
||||||
|
verify(eventRepository).persist(eventCaptor.capture());
|
||||||
|
Event capturedEvent = eventCaptor.getValue();
|
||||||
|
|
||||||
|
assertNotNull(capturedEvent.getTimestamp());
|
||||||
|
assertEquals("MyClass", capturedEvent.getClassName());
|
||||||
|
assertEquals("myMethod", capturedEvent.getMethodName());
|
||||||
|
assertEquals("Er ging iets mis", capturedEvent.getMessage());
|
||||||
|
assertEquals("ERROR", capturedEvent.getLevel());
|
||||||
|
assertEquals("Database connectie verbroken", capturedEvent.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCleanEvents_CallsDeleteAll() {
|
||||||
|
eventService.cleanEvents();
|
||||||
|
|
||||||
|
verify(eventRepository).deleteAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user