test: InboxControllerImplのテストを追加

This commit is contained in:
usbharu 2024-02-20 13:13:04 +09:00
parent 526354df1f
commit 0d24f41145
1 changed files with 441 additions and 73 deletions

View File

@ -19,13 +19,17 @@ package dev.usbharu.hideout.activitypub.interfaces.api.inbox
import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException
import dev.usbharu.hideout.activitypub.service.common.APService import dev.usbharu.hideout.activitypub.service.common.APService
import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.activitypub.service.common.ActivityType
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
import dev.usbharu.hideout.util.Base64Util
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks import org.mockito.InjectMocks
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.* import org.mockito.kotlin.*
import org.springframework.http.MediaType import org.springframework.http.MediaType
@ -33,12 +37,21 @@ import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.test.web.servlet.setup.MockMvcBuilders
import java.net.URI
import java.security.MessageDigest
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.*
@ExtendWith(MockitoExtension::class) @ExtendWith(MockitoExtension::class)
class InboxControllerImplTest { class InboxControllerImplTest {
private lateinit var mockMvc: MockMvc private lateinit var mockMvc: MockMvc
@Spy
private val httpSignatureHeaderChecker =
HttpSignatureHeaderChecker(ApplicationConfig(URI.create("https://example.com").toURL()))
@Mock @Mock
private lateinit var apService: APService private lateinit var apService: APService
@ -50,6 +63,10 @@ class InboxControllerImplTest {
mockMvc = MockMvcBuilders.standaloneSetup(inboxController).build() mockMvc = MockMvcBuilders.standaloneSetup(inboxController).build()
} }
private val dateTimeFormatter: DateTimeFormatter =
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US)
@Test @Test
fun `inbox 正常なPOSTリクエストをしたときAcceptが返ってくる`() = runTest { fun `inbox 正常なPOSTリクエストをしたときAcceptが返ってくる`() = runTest {
@ -58,24 +75,25 @@ class InboxControllerImplTest {
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
whenever( whenever(
apService.processActivity( apService.processActivity(
eq(json), eq(json), eq(ActivityType.Follow), any(), any()
eq(ActivityType.Follow),
any(),
any()
) )
).doReturn(Unit) ).doReturn(Unit)
mockMvc val sha256 = MessageDigest.getInstance("SHA-256")
.post("/inbox") {
content = json val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
contentType = MediaType.APPLICATION_JSON
header("Signature", "") mockMvc.post("/inbox") {
} content = json
.asyncDispatch() contentType = MediaType.APPLICATION_JSON
.andExpect { header("Signature", "a")
status { isAccepted() } header("Host", "example.com")
} header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}.asyncDispatch().andExpect {
status { isAccepted() }
}
} }
@ -83,17 +101,19 @@ class InboxControllerImplTest {
fun `inbox parseActivityに失敗したときAcceptが返ってくる`() = runTest { fun `inbox parseActivityに失敗したときAcceptが返ってくる`() = runTest {
val json = """{"type":"Hoge"}""" val json = """{"type":"Hoge"}"""
whenever(apService.parseActivity(eq(json))).doThrow(JsonParseException::class) whenever(apService.parseActivity(eq(json))).doThrow(JsonParseException::class)
val sha256 = MessageDigest.getInstance("SHA-256")
mockMvc val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
.post("/inbox") { mockMvc.post("/inbox") {
content = json content = json
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
header("Signature", "") header("Signature", "a")
} header("Host", "example.com")
.asyncDispatch() header("Date", ZonedDateTime.now().format(dateTimeFormatter))
.andExpect { header("Digest", digest)
status { isAccepted() } }.asyncDispatch().andExpect {
} status { isAccepted() }
}
} }
@ -103,23 +123,22 @@ class InboxControllerImplTest {
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
whenever( whenever(
apService.processActivity( apService.processActivity(
eq(json), eq(json), eq(ActivityType.Follow), any(), any()
eq(ActivityType.Follow),
any(),
any()
) )
).doThrow(FailedToGetResourcesException::class) ).doThrow(FailedToGetResourcesException::class)
val sha256 = MessageDigest.getInstance("SHA-256")
mockMvc val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
.post("/inbox") { mockMvc.post("/inbox") {
content = json content = json
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
header("Signature", "") header("Signature", "a")
} header("Host", "example.com")
.asyncDispatch() header("Date", ZonedDateTime.now().format(dateTimeFormatter))
.andExpect { header("Digest", digest)
status { isAccepted() } }.asyncDispatch().andExpect {
} status { isAccepted() }
}
} }
@ -137,17 +156,19 @@ class InboxControllerImplTest {
whenever(apService.processActivity(eq(json), eq(ActivityType.Follow), any(), any())).doReturn( whenever(apService.processActivity(eq(json), eq(ActivityType.Follow), any(), any())).doReturn(
Unit Unit
) )
val sha256 = MessageDigest.getInstance("SHA-256")
mockMvc val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
.post("/users/hoge/inbox") { mockMvc.post("/users/hoge/inbox") {
content = json content = json
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
header("Signature", "") header("Signature", "a")
} header("Host", "example.com")
.asyncDispatch() header("Date", ZonedDateTime.now().format(dateTimeFormatter))
.andExpect { header("Digest", digest)
status { isAccepted() } }.asyncDispatch().andExpect {
} status { isAccepted() }
}
} }
@ -155,17 +176,19 @@ class InboxControllerImplTest {
fun `user-inbox parseActivityに失敗したときAcceptが返ってくる`() = runTest { fun `user-inbox parseActivityに失敗したときAcceptが返ってくる`() = runTest {
val json = """{"type":"Hoge"}""" val json = """{"type":"Hoge"}"""
whenever(apService.parseActivity(eq(json))).doThrow(JsonParseException::class) whenever(apService.parseActivity(eq(json))).doThrow(JsonParseException::class)
val sha256 = MessageDigest.getInstance("SHA-256")
mockMvc val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
.post("/users/hoge/inbox") { mockMvc.post("/users/hoge/inbox") {
content = json content = json
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
header("Signature", "") header("Signature", "a")
} header("Host", "example.com")
.asyncDispatch() header("Date", ZonedDateTime.now().format(dateTimeFormatter))
.andExpect { header("Digest", digest)
status { isAccepted() } }.asyncDispatch().andExpect {
} status { isAccepted() }
}
} }
@ -175,23 +198,22 @@ class InboxControllerImplTest {
whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow)
whenever( whenever(
apService.processActivity( apService.processActivity(
eq(json), eq(json), eq(ActivityType.Follow), any(), any()
eq(ActivityType.Follow),
any(),
any()
) )
).doThrow(FailedToGetResourcesException::class) ).doThrow(FailedToGetResourcesException::class)
val sha256 = MessageDigest.getInstance("SHA-256")
mockMvc val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
.post("/users/hoge/inbox") { mockMvc.post("/users/hoge/inbox") {
content = json content = json
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
header("Signature", "") header("Signature", "a")
} header("Host", "example.com")
.asyncDispatch() header("Date", ZonedDateTime.now().format(dateTimeFormatter))
.andExpect { header("Digest", digest)
status { isAccepted() } }.asyncDispatch().andExpect {
} status { isAccepted() }
}
} }
@ -199,4 +221,350 @@ class InboxControllerImplTest {
fun `user-inbox GETリクエストには405を返す`() { fun `user-inbox GETリクエストには405を返す`() {
mockMvc.get("/users/hoge/inbox").andExpect { status { isMethodNotAllowed() } } mockMvc.get("/users/hoge/inbox").andExpect { status { isMethodNotAllowed() } }
} }
}
@Test
fun `inbox Dateヘッダーが無いと400`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
}
.asyncDispatch()
.andExpect {
status {
isBadRequest()
}
}
}
@Test
fun `user-inbox Dateヘッダーが無いと400`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
}
.asyncDispatch()
.andExpect {
status {
isBadRequest()
}
}
}
@Test
fun `inbox Dateヘッダーが未来だと401`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().plusDays(1).format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status {
isUnauthorized()
}
}
}
@Test
fun `user-inbox Dateヘッダーが未来だと401`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().plusDays(1).format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status {
isUnauthorized()
}
}
}
@Test
fun `inbox Dateヘッダーが過去過ぎると401`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().minusDays(1).format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status {
isUnauthorized()
}
}
}
@Test
fun `user-inbox Dateヘッダーが過去過ぎると401`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().minusDays(1).format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status {
isUnauthorized()
}
}
}
@Test
fun `inbox Hostヘッダーが無いと400`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status {
isBadRequest()
}
}
}
@Test
fun `user-inbox Hostヘッダーが無いと400`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status {
isBadRequest()
}
}
}
@Test
fun `inbox Hostヘッダーが間違ってると401`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Host", "example.jp")
}
.asyncDispatch()
.andExpect {
status {
isUnauthorized()
}
}
}
@Test
fun `user-inbox Hostヘッダーが間違ってると401`() {
val json = """{"type":"Follow"}"""
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Host", "example.jp")
}
.asyncDispatch()
.andExpect {
status {
isUnauthorized()
}
}
}
@Test
fun `inbox Digestヘッダーがないと400`() = runTest {
val json = """{"type":"Follow"}"""
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Signature", "")
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status { isBadRequest() }
}
}
@Test
fun `inbox Digestヘッダーが間違ってると401`() = runTest {
val json = """{"type":"Follow"}"""
val sha256 = MessageDigest.getInstance("SHA-256")
val digest = Base64Util.encode(sha256.digest(("$json aaaaaaaa").toByteArray()))
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Signature", "")
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}
.asyncDispatch()
.andExpect {
status { isUnauthorized() }
}
}
@Test
fun `user-inbox Digestヘッダーがないと400`() = runTest {
val json = """{"type":"Follow"}"""
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Signature", "")
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
}
.asyncDispatch()
.andExpect {
status { isBadRequest() }
}
}
@Test
fun `user-inbox Digestヘッダーが間違ってると401`() = runTest {
val json = """{"type":"Follow"}"""
val sha256 = MessageDigest.getInstance("SHA-256")
val digest = Base64Util.encode(sha256.digest(("$json aaaaaaaa").toByteArray()))
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Signature", "")
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}
.asyncDispatch()
.andExpect {
status { isUnauthorized() }
}
}
@Test
fun `inbox Signatureヘッダーがないと401`() = runTest {
val json = """{"type":"Follow"}"""
val sha256 = MessageDigest.getInstance("SHA-256")
val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}
.asyncDispatch()
.andExpect {
status { isUnauthorized() }
}
}
@Test
fun `inbox Signatureヘッダーが空だと401`() = runTest {
val json = """{"type":"Follow"}"""
val sha256 = MessageDigest.getInstance("SHA-256")
val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
mockMvc
.post("/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Signature", "")
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}
.asyncDispatch()
.andExpect {
status { isUnauthorized() }
}
}
@Test
fun `user-inbox Digestヘッダーがないと401`() = runTest {
val json = """{"type":"Follow"}"""
val sha256 = MessageDigest.getInstance("SHA-256")
val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}
.asyncDispatch()
.andExpect {
status { isUnauthorized() }
}
}
@Test
fun `user-inbox Digestヘッダーが空だと401`() = runTest {
val json = """{"type":"Follow"}"""
val sha256 = MessageDigest.getInstance("SHA-256")
val digest = Base64Util.encode(sha256.digest(json.toByteArray()))
mockMvc
.post("/users/hoge/inbox") {
content = json
contentType = MediaType.APPLICATION_JSON
header("Signature", "")
header("Host", "example.com")
header("Date", ZonedDateTime.now().format(dateTimeFormatter))
header("Digest", digest)
}
.asyncDispatch()
.andExpect {
status { isUnauthorized() }
}
}
}