test: フォロー受付処理のJobのテストを追加

This commit is contained in:
usbharu 2023-04-12 21:53:49 +09:00
parent 71ad3c9c7d
commit f4df345407
7 changed files with 208 additions and 2 deletions

View File

@ -65,6 +65,7 @@ dependencies {
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
testImplementation ("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
testImplementation("io.ktor:ktor-client-mock:$ktor_version")
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")

View File

@ -13,4 +13,26 @@ open class Accept : Object {
this.`object` = `object`
this.actor = actor
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Accept) return false
if (!super.equals(other)) return false
if (`object` != other.`object`) return false
return actor == other.actor
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + (`object`?.hashCode() ?: 0)
result = 31 * result + (actor?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return "Accept(`object`=$`object`, actor=$actor) ${super.toString()}"
}
}

View File

@ -37,6 +37,10 @@ open class JsonLd {
return context.hashCode()
}
override fun toString(): String {
return "JsonLd(context=$context)"
}
}

View File

@ -39,6 +39,10 @@ open class Object : JsonLd {
return result
}
override fun toString(): String {
return "Object(type=$type, name=$name) ${super.toString()}"
}
}

View File

@ -10,14 +10,14 @@ sealed class ActivityPubResponse(
)
class ActivityPubStringResponse(
httpStatusCode: HttpStatusCode,
httpStatusCode: HttpStatusCode = HttpStatusCode.OK,
val message: String,
contentType: ContentType = ContentType.Application.Activity
) :
ActivityPubResponse(httpStatusCode, contentType)
class ActivityPubObjectResponse(
httpStatusCode: HttpStatusCode,
httpStatusCode: HttpStatusCode = HttpStatusCode.OK,
val message: JsonLd,
contentType: ContentType = ContentType.Application.Activity
) :

View File

@ -0,0 +1,153 @@
@file:OptIn(ExperimentalCoroutinesApi::class)
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package dev.usbharu.hideout.service.activitypub
import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.ap.*
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.config.ConfigData
import dev.usbharu.hideout.domain.model.UserEntity
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
import dev.usbharu.hideout.service.impl.UserService
import dev.usbharu.hideout.service.job.JobQueueParentService
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import kjob.core.dsl.ScheduleContext
import kjob.core.job.JobProps
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.*
import utils.JsonObjectMapper
class ActivityPubFollowServiceImplTest {
@Test
fun `receiveFollow フォロー受付処理`() = runTest {
val jobQueueParentService = mock<JobQueueParentService> {
onBlocking { schedule(eq(ReceiveFollowJob), any()) } doReturn Unit
}
val activityPubFollowService = ActivityPubFollowServiceImpl(jobQueueParentService, mock(), mock(), mock())
activityPubFollowService.receiveFollow(
Follow(
emptyList(),
"Follow",
"https://example.com",
"https://follower.example.com"
)
)
verify(jobQueueParentService, times(1)).schedule(eq(ReceiveFollowJob), any())
argumentCaptor<ScheduleContext<ReceiveFollowJob>.(ReceiveFollowJob) -> Unit> {
verify(jobQueueParentService, times(1)).schedule(eq(ReceiveFollowJob), capture())
val scheduleContext = ScheduleContext<ReceiveFollowJob>(Json)
firstValue.invoke(scheduleContext, ReceiveFollowJob)
val actor = scheduleContext.props.props[ReceiveFollowJob.actor.name]
val targetActor = scheduleContext.props.props[ReceiveFollowJob.targetActor.name]
val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name]
assertEquals("https://follower.example.com", actor)
assertEquals("https://example.com", targetActor)
assertEquals(
"""{"type":"Follow","name":"Follow","object":"https://example.com","actor":"https://follower.example.com","@context":null}""",
follow
)
}
}
@Test
fun `receiveFollowJob フォロー受付処理のJob`() = runTest {
Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper)
val person = Person(
type = emptyList(),
name = "follower",
id = "https://follower.example.com",
preferredUsername = "followerUser",
summary = "This user is follower user.",
inbox = "https://follower.example.com/inbox",
outbox = "https://follower.example.com/outbox",
url = "https://follower.example.com",
icon = Image(
type = emptyList(),
name = "https://follower.example.com/image",
mediaType = "image/png",
url = "https://follower.example.com/image"
),
publicKey = Key(
type = emptyList(),
name = "Public Key",
id = "https://follower.example.com#main-key",
owner = "https://follower.example.com",
publicKeyPem = "BEGIN PUBLIC KEY...END PUBLIC KEY",
)
)
val activityPubUserService = mock<ActivityPubUserService> {
onBlocking { fetchPerson(anyString()) } doReturn person
}
val userService = mock<UserService> {
onBlocking { findByUrls(any()) } doReturn listOf(
UserEntity(
id = 1L,
name = "test",
domain = "example.com",
screenName = "testUser",
description = "This user is test user.",
inbox = "https://example.com/inbox",
outbox = "https://example.com/outbox",
url = "https://example.com"
),
UserEntity(
id = 2L,
name = "follower",
domain = "follower.example.com",
screenName = "followerUser",
description = "This user is test follower user.",
inbox = "https://follower.example.com/inbox",
outbox = "https://follower.example.com/outbox",
url = "https://follower.example.com"
)
)
onBlocking { addFollowers(any(), any()) } doReturn Unit
}
val activityPubFollowService =
ActivityPubFollowServiceImpl(
mock(),
activityPubUserService,
userService,
HttpClient(MockEngine { httpRequestData ->
assertEquals(person.inbox, httpRequestData.url.toString())
val accept = Accept(
type = emptyList(),
name = "Follow",
`object` = Follow(
type = emptyList(),
name = "Follow",
`object` = "https://example.com",
actor = "https://follower.example.com"
),
actor = "https://example.com"
)
accept.context += "https://www.w3.org/ns/activitystreams"
assertEquals(
accept,
Config.configData.objectMapper.readValue<Accept>(
httpRequestData.body.toByteArray().decodeToString()
)
)
respondOk()
})
)
activityPubFollowService.receiveFollowJob(
JobProps(
data = mapOf<String, Any>(
ReceiveFollowJob.actor.name to "https://follower.example.com",
ReceiveFollowJob.targetActor.name to "https://example.com",
ReceiveFollowJob.follow.name to """{"type":"Follow","name":"Follow","object":"https://example.com","actor":"https://follower.example.com","@context":null}"""
),
json = Json
)
)
}
}

View File

@ -0,0 +1,22 @@
package utils
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonSetter
import com.fasterxml.jackson.annotation.Nulls
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
object JsonObjectMapper {
val objectMapper: com.fasterxml.jackson.databind.ObjectMapper =
jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
init {
objectMapper.configOverride(List::class.java).setSetterInfo(
JsonSetter.Value.forValueNulls(
Nulls.AS_EMPTY
)
)
}
}