test(backend): add failing tests for step 8 — EntityScheduler
This commit is contained in:
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user