package com.condado.newsletter.service import com.condado.newsletter.model.ParsedAiResponse import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.springframework.web.client.RestClient class AiServiceTest { private val mockRestClient: RestClient = mockk() private val mockRequestBodyUriSpec: RestClient.RequestBodyUriSpec = mockk() private val mockRequestBodySpec: RestClient.RequestBodySpec = mockk() private val mockResponseSpec: RestClient.ResponseSpec = mockk() private val service = AiService( restClient = mockRestClient, apiKey = "test-key", model = "gpt-4o" ) @Test fun should_returnAiResponseText_when_apiCallSucceeds() { val rawResponse = "SUBJECT: Test Subject\nBODY:\nTest body content" stubRestClient(rawResponse.replace("\n", "\\n")) val result = service.generate("My test prompt") assertThat(result.subject).isEqualTo("Test Subject") assertThat(result.body).isEqualTo("Test body content") } @Test fun should_throwAiServiceException_when_apiReturnsError() { every { mockRestClient.post() } throws RuntimeException("API unavailable") assertThrows { service.generate("My prompt") } } @Test fun should_extractSubjectAndBody_when_responseIsWellFormatted() { val raw = "SUBJECT: Weekly Update\nBODY:\nDear colleagues,\n\nPlease note the snacks." val result = service.parseResponse(raw) assertThat(result.subject).isEqualTo("Weekly Update") assertThat(result.body).isEqualTo("Dear colleagues,\n\nPlease note the snacks.") } @Test fun should_throwParseException_when_responseIsMissingSubjectLine() { val raw = "BODY:\nSome body without a subject" assertThrows { service.parseResponse(raw) } } @Test fun should_throwParseException_when_responseIsMissingBodySection() { val raw = "SUBJECT: Some Subject\nNo body section here" assertThrows { service.parseResponse(raw) } } // ── helper ────────────────────────────────────────────────────────────── private fun stubRestClient(responseText: String) { every { mockRestClient.post() } returns mockRequestBodyUriSpec every { mockRequestBodyUriSpec.uri(any()) } returns mockRequestBodySpec every { mockRequestBodySpec.header(any(), any()) } returns mockRequestBodySpec every { mockRequestBodySpec.body(any()) } returns mockRequestBodySpec every { mockRequestBodySpec.retrieve() } returns mockResponseSpec every { mockResponseSpec.body(String::class.java) } returns """ { "choices": [ { "message": { "content": "$responseText" } } ] } """.trimIndent() } }