test(backend): add failing tests for step 8 — EntityScheduler

This commit is contained in:
2026-03-26 18:54:15 -03:00
parent bc8ec8de4f
commit 958d881b4b

View File

@@ -0,0 +1,158 @@
package com.condado.newsletter.scheduler
import com.condado.newsletter.model.DispatchLog
import com.condado.newsletter.model.DispatchStatus
import com.condado.newsletter.model.EmailContext
import com.condado.newsletter.model.ParsedAiResponse
import com.condado.newsletter.model.VirtualEntity
import com.condado.newsletter.repository.DispatchLogRepository
import com.condado.newsletter.service.AiService
import com.condado.newsletter.service.AiServiceException
import com.condado.newsletter.service.EmailReaderService
import com.condado.newsletter.service.EmailSenderService
import com.condado.newsletter.service.PromptBuilderService
import io.mockk.called
import io.mockk.capture
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mutableListOf
import io.mockk.runs
import io.mockk.slot
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.time.LocalDateTime
import java.util.UUID
class EntitySchedulerTest {
private val emailReaderService: EmailReaderService = mockk()
private val promptBuilderService: PromptBuilderService = mockk()
private val aiService: AiService = mockk()
private val emailSenderService: EmailSenderService = mockk()
private val dispatchLogRepository: DispatchLogRepository = mockk()
private lateinit var scheduler: EntityScheduler
private val entity = VirtualEntity(
name = "João Gerente",
email = "joao@condado.com",
jobTitle = "Gerente de Nada",
personality = "Muito formal",
scheduleCron = "0 9 * * *",
contextWindowDays = 3,
active = true
).apply { id = UUID.randomUUID() }
private val inactiveEntity = VirtualEntity(
name = "Maria Inativa",
email = "maria@condado.com",
jobTitle = "Consultora de Vibe",
active = false
).apply { id = UUID.randomUUID() }
@BeforeEach
fun setUp() {
scheduler = EntityScheduler(
emailReaderService = emailReaderService,
promptBuilderService = promptBuilderService,
aiService = aiService,
emailSenderService = emailSenderService,
dispatchLogRepository = dispatchLogRepository,
recipients = "recipient@example.com",
inboxFolder = "INBOX"
)
}
@Test
fun should_runFullPipeline_when_entityIsTriggered() {
val emails = listOf(EmailContext("sender@example.com", "Subject", "Body", LocalDateTime.now()))
val prompt = "Generated prompt"
val aiResponse = ParsedAiResponse(subject = "Weekly memo", body = "Dear colleagues...")
every { emailReaderService.readEmails("INBOX", 3) } returns emails
every { promptBuilderService.buildPrompt(entity, emails) } returns prompt
every { aiService.generate(prompt) } returns aiResponse
every { emailSenderService.send(entity.email, listOf("recipient@example.com"), aiResponse.subject, aiResponse.body) } just runs
every { dispatchLogRepository.save(any()) } answers { firstArg() }
scheduler.runPipeline(entity)
verify(exactly = 1) { emailReaderService.readEmails("INBOX", 3) }
verify(exactly = 1) { promptBuilderService.buildPrompt(entity, emails) }
verify(exactly = 1) { aiService.generate(prompt) }
verify(exactly = 1) { emailSenderService.send(entity.email, listOf("recipient@example.com"), aiResponse.subject, aiResponse.body) }
}
@Test
fun should_saveDispatchLogWithStatusSent_when_pipelineSucceeds() {
val emails = emptyList<EmailContext>()
val prompt = "prompt"
val aiResponse = ParsedAiResponse(subject = "Subject", body = "Body")
val savedLog = slot<DispatchLog>()
every { emailReaderService.readEmails("INBOX", 3) } returns emails
every { promptBuilderService.buildPrompt(entity, emails) } returns prompt
every { aiService.generate(prompt) } returns aiResponse
every { emailSenderService.send(any(), any(), any(), any()) } just runs
every { dispatchLogRepository.save(capture(savedLog)) } answers { firstArg() }
scheduler.runPipeline(entity)
assertThat(savedLog.isCaptured).isTrue()
assertThat(savedLog.captured.status).isEqualTo(DispatchStatus.SENT)
assertThat(savedLog.captured.emailSubject).isEqualTo("Subject")
assertThat(savedLog.captured.emailBody).isEqualTo("Body")
}
@Test
fun should_saveDispatchLogWithStatusFailed_when_aiServiceThrows() {
val emails = emptyList<EmailContext>()
val prompt = "prompt"
val savedLog = slot<DispatchLog>()
every { emailReaderService.readEmails("INBOX", 3) } returns emails
every { promptBuilderService.buildPrompt(entity, emails) } returns prompt
every { aiService.generate(prompt) } throws AiServiceException("API error")
every { dispatchLogRepository.save(capture(savedLog)) } answers { firstArg() }
scheduler.runPipeline(entity)
assertThat(savedLog.isCaptured).isTrue()
assertThat(savedLog.captured.status).isEqualTo(DispatchStatus.FAILED)
assertThat(savedLog.captured.errorMessage).contains("API error")
}
@Test
fun should_saveDispatchLogWithStatusFailed_when_emailSenderThrows() {
val emails = emptyList<EmailContext>()
val prompt = "prompt"
val aiResponse = ParsedAiResponse(subject = "Subject", body = "Body")
val savedLog = slot<DispatchLog>()
every { emailReaderService.readEmails("INBOX", 3) } returns emails
every { promptBuilderService.buildPrompt(entity, emails) } returns prompt
every { aiService.generate(prompt) } returns aiResponse
every { emailSenderService.send(any(), any(), any(), any()) } throws RuntimeException("SMTP error")
every { dispatchLogRepository.save(capture(savedLog)) } answers { firstArg() }
scheduler.runPipeline(entity)
assertThat(savedLog.isCaptured).isTrue()
assertThat(savedLog.captured.status).isEqualTo(DispatchStatus.FAILED)
assertThat(savedLog.captured.errorMessage).contains("SMTP error")
}
@Test
fun should_notTrigger_when_entityIsInactive() {
scheduler.runPipeline(inactiveEntity)
verify { emailReaderService wasNot called }
verify { promptBuilderService wasNot called }
verify { aiService wasNot called }
verify { emailSenderService wasNot called }
verify { dispatchLogRepository wasNot called }
}
}