FacturasAI · Quarkus 3.8 LTS · JUnit 5 + Mockito + REST Assured · Apache Camel 4.x
@ExtendWith(MockitoExtension.class)
@DisplayName("NotificationService Unit Tests")
class NotificationServiceTest {
@Mock private NotificationRepository repo;
@Mock private CamelNotificationPublisher publisher;
@Mock private EventService eventService;
@InjectMocks private NotificationService sut;
private SendNotificationCommand validCmd;
@BeforeEach
void setUp() {
validCmd = new SendNotificationCommand(
"rcpt-001", "Factura FA-2024-0042 procesada con éxito", NotificationType.INVOICE_PROCESSED
);
}
@Nested
@DisplayName("Tests for sendNotification")
class SendNotification {
@Test
@DisplayName("Should persist notification and publish to RabbitMQ via Camel")
void givenValidRequest_whenSend_thenPersistsAndPublishes() {
// ARRANGE
doNothing().when(repo).persist(any(Notification.class));
// ACT
NotificationReceipt receipt = sut.sendNotification(validCmd);
// ASSERT
assertThat(receipt).isNotNull();
assertThat(receipt.recipientId()).isEqualTo("rcpt-001");
verify(repo).persist(any(Notification.class));
verify(publisher).publishAsync(receipt);
verify(eventService).createSuccessEvent(receipt, "NOTIFICATION_SENT");
}
@Test
@DisplayName("Should reject blank notification message with 400")
void givenBlankMessage_whenSend_thenThrows400() {
// ARRANGE
SendNotificationCommand invalid = new SendNotificationCommand(
"rcpt-001", "", NotificationType.INVOICE_PROCESSED
);
// ACT & ASSERT
WebApplicationException ex = assertThrows(
WebApplicationException.class, () -> sut.sendNotification(invalid)
);
assertThat(ex.getResponse().getStatus()).isEqualTo(400);
verify(repo, never()).persist(any());
}
}
}
@ApplicationScoped
public class NotificationService {
@Inject NotificationRepository repo;
@Inject CamelNotificationPublisher publisher;
@Inject EventService eventService;
public NotificationReceipt sendNotification(SendNotificationCommand cmd) {
Objects.requireNonNull(cmd, "Command cannot be null");
if (cmd.message() == null || cmd.message().isBlank())
throw new WebApplicationException(Response.status(400)
.entity("message is required").build());
if (cmd.recipientId() == null || cmd.recipientId().isBlank())
throw new NullPointerException("recipientId cannot be blank");
Notification n = Notification.from(cmd);
try {
repo.persist(n);
} catch (PersistenceException e) {
eventService.createErrorEvent(cmd, "NOTIFICATION_PERSIST_FAILED", e.getMessage());
throw e;
}
NotificationReceipt receipt = NotificationReceipt.from(n);
try {
publisher.publishAsync(receipt);
} catch (RuntimeException e) {
n.setStatus(NotificationStatus.FAILED);
repo.update(n);
throw e;
}
eventService.createSuccessEvent(receipt, "NOTIFICATION_SENT");
return receipt;
}
public List<Notification> getByRecipient(String recipientId, int offset, int limit) {
if (recipientId == null || recipientId.isBlank())
throw new WebApplicationException(Response.status(400).build());
return repo.findByRecipient(recipientId, offset, limit);
}
}
| Clase | Líneas cubiertas | Ramas cubiertas | Métodos | Estado |
|---|---|---|---|---|
| NotificationService | 91% | 80% | 8/8 | ✓ PASS |
| NotificationResource | 88% | 75% | 5/5 | ✓ PASS |
| NotificationCamelRoute | 82% | 64% | 4/4 | ✓ PASS |
| NotificationRepository | 85% | 72% | 6/6 | ✓ PASS |
| BUNDLE (total) | 87% | 73% | 23/24 | BUILD SUCCESS |