feat(backend): implement step 10 — JWT authentication (JwtService, AuthService, AuthController, JwtAuthFilter, SecurityConfig)

This commit is contained in:
2026-03-26 19:08:09 -03:00
parent 9065db504e
commit 031ad3d4b2
9 changed files with 243 additions and 138 deletions

View File

@@ -6,7 +6,9 @@ import com.condado.newsletter.model.VirtualEntity
import com.condado.newsletter.repository.DispatchLogRepository
import com.condado.newsletter.repository.VirtualEntityRepository
import com.condado.newsletter.scheduler.EntityScheduler
import com.condado.newsletter.service.JwtService
import com.ninjasquad.springmockk.MockkBean
import jakarta.servlet.http.Cookie
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
@@ -21,17 +23,13 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@AutoConfigureMockMvc
class DispatchLogControllerTest {
@Autowired
lateinit var mockMvc: MockMvc
@Autowired lateinit var mockMvc: MockMvc
@Autowired lateinit var virtualEntityRepository: VirtualEntityRepository
@Autowired lateinit var dispatchLogRepository: DispatchLogRepository
@Autowired lateinit var jwtService: JwtService
@MockkBean lateinit var entityScheduler: EntityScheduler
@Autowired
lateinit var virtualEntityRepository: VirtualEntityRepository
@Autowired
lateinit var dispatchLogRepository: DispatchLogRepository
@MockkBean
lateinit var entityScheduler: EntityScheduler
private fun authCookie() = Cookie("jwt", jwtService.generateToken())
@AfterEach
fun cleanUp() {
@@ -41,42 +39,19 @@ class DispatchLogControllerTest {
@Test
fun should_return200AndAllLogs_when_getAllLogs() {
val entity = virtualEntityRepository.save(VirtualEntity(
name = "Log Entity",
email = "log@condado.com",
jobTitle = "Logger"
))
dispatchLogRepository.save(DispatchLog(
virtualEntity = entity,
emailSubject = "Test Subject",
status = DispatchStatus.SENT
))
mockMvc.perform(get("/api/v1/dispatch-logs"))
.andExpect(status().isOk)
.andExpect(jsonPath("$").isArray)
.andExpect(jsonPath("$[0].emailSubject").value("Test Subject"))
val entity = virtualEntityRepository.save(VirtualEntity(name = "Log Entity", email = "log@condado.com", jobTitle = "Logger"))
dispatchLogRepository.save(DispatchLog(virtualEntity = entity, emailSubject = "Test Subject", status = DispatchStatus.SENT))
mockMvc.perform(get("/api/v1/dispatch-logs").cookie(authCookie()))
.andExpect(status().isOk).andExpect(jsonPath("$").isArray).andExpect(jsonPath("$[0].emailSubject").value("Test Subject"))
}
@Test
fun should_return200AndFilteredLogs_when_getByEntityId() {
val entity1 = virtualEntityRepository.save(VirtualEntity(
name = "Entity One",
email = "one@condado.com",
jobTitle = "Job One"
))
val entity2 = virtualEntityRepository.save(VirtualEntity(
name = "Entity Two",
email = "two@condado.com",
jobTitle = "Job Two"
))
val entity1 = virtualEntityRepository.save(VirtualEntity(name = "Entity One", email = "one@condado.com", jobTitle = "Job One"))
val entity2 = virtualEntityRepository.save(VirtualEntity(name = "Entity Two", email = "two@condado.com", jobTitle = "Job Two"))
dispatchLogRepository.save(DispatchLog(virtualEntity = entity1, emailSubject = "Log One", status = DispatchStatus.SENT))
dispatchLogRepository.save(DispatchLog(virtualEntity = entity2, emailSubject = "Log Two", status = DispatchStatus.FAILED))
mockMvc.perform(get("/api/v1/dispatch-logs/entity/${entity1.id}"))
.andExpect(status().isOk)
.andExpect(jsonPath("$").isArray)
.andExpect(jsonPath("$.length()").value(1))
.andExpect(jsonPath("$[0].emailSubject").value("Log One"))
mockMvc.perform(get("/api/v1/dispatch-logs/entity/${entity1.id}").cookie(authCookie()))
.andExpect(status().isOk).andExpect(jsonPath("$.length()").value(1)).andExpect(jsonPath("$[0].emailSubject").value("Log One"))
}
}

View File

@@ -3,12 +3,14 @@ package com.condado.newsletter.controller
import com.condado.newsletter.model.VirtualEntity
import com.condado.newsletter.repository.VirtualEntityRepository
import com.condado.newsletter.scheduler.EntityScheduler
import com.condado.newsletter.service.JwtService
import com.fasterxml.jackson.databind.ObjectMapper
import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import io.mockk.just
import io.mockk.runs
import io.mockk.verify
import jakarta.servlet.http.Cookie
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
@@ -27,135 +29,72 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@AutoConfigureMockMvc
class VirtualEntityControllerTest {
@Autowired
lateinit var mockMvc: MockMvc
@Autowired
lateinit var virtualEntityRepository: VirtualEntityRepository
@MockkBean
lateinit var entityScheduler: EntityScheduler
@Autowired lateinit var mockMvc: MockMvc
@Autowired lateinit var virtualEntityRepository: VirtualEntityRepository
@Autowired lateinit var jwtService: JwtService
@MockkBean lateinit var entityScheduler: EntityScheduler
private val objectMapper = ObjectMapper()
private fun authCookie() = Cookie("jwt", jwtService.generateToken())
@AfterEach
fun cleanUp() {
virtualEntityRepository.deleteAll()
}
fun cleanUp() { virtualEntityRepository.deleteAll() }
@Test
fun should_return201AndBody_when_postWithValidPayload() {
val payload = mapOf(
"name" to "Fulano da Silva",
"email" to "fulano@condado.com",
"jobTitle" to "Diretor de Nada"
)
mockMvc.perform(
post("/api/v1/virtual-entities")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(payload))
)
.andExpect(status().isCreated)
.andExpect(jsonPath("$.name").value("Fulano da Silva"))
.andExpect(jsonPath("$.email").value("fulano@condado.com"))
.andExpect(jsonPath("$.jobTitle").value("Diretor de Nada"))
.andExpect(jsonPath("$.id").isNotEmpty)
val payload = mapOf("name" to "Fulano da Silva", "email" to "fulano@condado.com", "jobTitle" to "Diretor de Nada")
mockMvc.perform(post("/api/v1/virtual-entities").cookie(authCookie()).contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(payload)))
.andExpect(status().isCreated).andExpect(jsonPath("$.name").value("Fulano da Silva")).andExpect(jsonPath("$.id").isNotEmpty)
}
@Test
fun should_return400_when_postWithMissingRequiredField() {
val payload = mapOf("name" to "Fulano") // missing email and jobTitle
mockMvc.perform(
post("/api/v1/virtual-entities")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(payload))
)
val payload = mapOf("name" to "Fulano")
mockMvc.perform(post("/api/v1/virtual-entities").cookie(authCookie()).contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(payload)))
.andExpect(status().isBadRequest)
}
@Test
fun should_return200AndList_when_getAllEntities() {
virtualEntityRepository.save(VirtualEntity(
name = "Test Entity",
email = "test@condado.com",
jobTitle = "Tester"
))
mockMvc.perform(get("/api/v1/virtual-entities"))
.andExpect(status().isOk)
.andExpect(jsonPath("$").isArray)
.andExpect(jsonPath("$[0].name").value("Test Entity"))
virtualEntityRepository.save(VirtualEntity(name = "Test Entity", email = "test@condado.com", jobTitle = "Tester"))
mockMvc.perform(get("/api/v1/virtual-entities").cookie(authCookie()))
.andExpect(status().isOk).andExpect(jsonPath("$").isArray).andExpect(jsonPath("$[0].name").value("Test Entity"))
}
@Test
fun should_return200AndEntity_when_getById() {
val entity = virtualEntityRepository.save(VirtualEntity(
name = "Test Entity",
email = "entity@condado.com",
jobTitle = "Test Job"
))
mockMvc.perform(get("/api/v1/virtual-entities/${entity.id}"))
.andExpect(status().isOk)
.andExpect(jsonPath("$.name").value("Test Entity"))
val entity = virtualEntityRepository.save(VirtualEntity(name = "Test Entity", email = "entity@condado.com", jobTitle = "Test Job"))
mockMvc.perform(get("/api/v1/virtual-entities/${entity.id}").cookie(authCookie()))
.andExpect(status().isOk).andExpect(jsonPath("$.name").value("Test Entity"))
}
@Test
fun should_return404_when_getByIdNotFound() {
val randomId = java.util.UUID.randomUUID()
mockMvc.perform(get("/api/v1/virtual-entities/$randomId"))
mockMvc.perform(get("/api/v1/virtual-entities/${java.util.UUID.randomUUID()}").cookie(authCookie()))
.andExpect(status().isNotFound)
}
@Test
fun should_return200_when_putWithValidPayload() {
val entity = virtualEntityRepository.save(VirtualEntity(
name = "Old Name",
email = "old@condado.com",
jobTitle = "Old Job"
))
val payload = mapOf("name" to "New Name")
mockMvc.perform(
put("/api/v1/virtual-entities/${entity.id}")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(payload))
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.name").value("New Name"))
.andExpect(jsonPath("$.email").value("old@condado.com"))
val entity = virtualEntityRepository.save(VirtualEntity(name = "Old Name", email = "old@condado.com", jobTitle = "Old Job"))
mockMvc.perform(put("/api/v1/virtual-entities/${entity.id}").cookie(authCookie()).contentType(MediaType.APPLICATION_JSON).content("""{"name":"New Name"}"""))
.andExpect(status().isOk).andExpect(jsonPath("$.name").value("New Name")).andExpect(jsonPath("$.email").value("old@condado.com"))
}
@Test
fun should_return200AndDeactivated_when_delete() {
val entity = virtualEntityRepository.save(VirtualEntity(
name = "Active Entity",
email = "active@condado.com",
jobTitle = "Active Job"
))
mockMvc.perform(delete("/api/v1/virtual-entities/${entity.id}"))
.andExpect(status().isOk)
.andExpect(jsonPath("$.active").value(false))
val entity = virtualEntityRepository.save(VirtualEntity(name = "Active Entity", email = "active@condado.com", jobTitle = "Active Job"))
mockMvc.perform(delete("/api/v1/virtual-entities/${entity.id}").cookie(authCookie()))
.andExpect(status().isOk).andExpect(jsonPath("$.active").value(false))
}
@Test
fun should_return200_when_triggerEndpointCalled() {
val entity = virtualEntityRepository.save(VirtualEntity(
name = "Trigger Entity",
email = "trigger@condado.com",
jobTitle = "Trigger Job"
))
val entity = virtualEntityRepository.save(VirtualEntity(name = "Trigger Entity", email = "trigger@condado.com", jobTitle = "Trigger Job"))
every { entityScheduler.runPipeline(any()) } just runs
mockMvc.perform(post("/api/v1/virtual-entities/${entity.id}/trigger"))
mockMvc.perform(post("/api/v1/virtual-entities/${entity.id}/trigger").cookie(authCookie()))
.andExpect(status().isOk)
verify(exactly = 1) { entityScheduler.runPipeline(any()) }
}
}