diff --git a/backend/src/test/kotlin/com/condado/newsletter/service/EmailReaderServiceTest.kt b/backend/src/test/kotlin/com/condado/newsletter/service/EmailReaderServiceTest.kt new file mode 100644 index 0000000..de9daff --- /dev/null +++ b/backend/src/test/kotlin/com/condado/newsletter/service/EmailReaderServiceTest.kt @@ -0,0 +1,113 @@ +package com.condado.newsletter.service + +import io.mockk.every +import io.mockk.mockk +import jakarta.mail.Folder +import jakarta.mail.Message +import jakarta.mail.Session +import jakarta.mail.Store +import jakarta.mail.internet.InternetAddress +import jakarta.mail.internet.MimeMessage +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.time.LocalDateTime +import java.util.Date + +class EmailReaderServiceTest { + + private val mockStore: Store = mockk(relaxed = true) + private val service = EmailReaderService(storeFactory = { mockStore }) + + @Test + fun should_returnEmailsSortedChronologically_when_multipleEmailsFetched() { + val folder: Folder = mockk(relaxed = true) + val session: Session = mockk() + + val newer = buildMimeMessage(session, "Newer email", "bob@x.com", Date()) + val older = buildMimeMessage(session, "Older email", "alice@x.com", Date(System.currentTimeMillis() - 100_000)) + + every { mockStore.getFolder(any()) } returns folder + every { folder.messageCount } returns 2 + every { folder.getMessages() } returns arrayOf(newer, older) + + val result = service.readEmails("INBOX", contextWindowDays = 10) + + assertThat(result).hasSize(2) + assertThat(result[0].subject).isEqualTo("Older email") + assertThat(result[1].subject).isEqualTo("Newer email") + } + + @Test + fun should_returnEmptyList_when_imapConnectionFails() { + every { mockStore.getFolder(any()) } throws RuntimeException("Connection refused") + + val result = service.readEmails("INBOX", contextWindowDays = 3) + + assertThat(result).isEmpty() + } + + @Test + fun should_filterEmailsOlderThanContextWindow_when_windowIs3Days() { + val folder: Folder = mockk(relaxed = true) + val session: Session = mockk() + + val recentDate = Date() + val oldDate = Date(System.currentTimeMillis() - 10L * 24 * 60 * 60 * 1000) + + val recent = buildMimeMessage(session, "Recent", "a@x.com", recentDate) + val old = buildMimeMessage(session, "Old", "b@x.com", oldDate) + + every { mockStore.getFolder(any()) } returns folder + every { folder.messageCount } returns 2 + every { folder.getMessages() } returns arrayOf(recent, old) + + val result = service.readEmails("INBOX", contextWindowDays = 3) + + assertThat(result).hasSize(1) + assertThat(result[0].subject).isEqualTo("Recent") + } + + @Test + fun should_stripHtml_when_emailBodyContainsHtmlTags() { + val folder: Folder = mockk(relaxed = true) + val session: Session = mockk() + + val msg = buildMimeMessage( + session, + subject = "HTML email", + from = "dev@x.com", + date = Date(), + body = "

Hello world

" + ) + + every { mockStore.getFolder(any()) } returns folder + every { folder.messageCount } returns 1 + every { folder.getMessages() } returns arrayOf(msg) + + val result = service.readEmails("INBOX", contextWindowDays = 10) + + assertThat(result).hasSize(1) + assertThat(result[0].body).doesNotContain("

", "", "", "

") + assertThat(result[0].body).contains("Hello") + assertThat(result[0].body).contains("world") + } + + // ── helper ────────────────────────────────────────────────────────────── + + private fun buildMimeMessage( + session: Session, + subject: String, + from: String, + date: Date, + body: String = "Plain text body" + ): MimeMessage { + val msg: MimeMessage = mockk(relaxed = true) + every { msg.subject } returns subject + every { msg.from } returns arrayOf(InternetAddress(from)) + every { msg.sentDate } returns date + every { msg.receivedDate } returns date + every { msg.contentType } returns "text/plain" + every { msg.content } returns body + return msg + } +}