refactor: postのサービスをリファクタリング

This commit is contained in:
usbharu 2023-06-03 20:55:54 +09:00
parent 56bc156aa2
commit de44bd7fc6
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
10 changed files with 225 additions and 578 deletions

View File

@ -14,6 +14,7 @@ import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.routing.register
import dev.usbharu.hideout.service.activitypub.ActivityPubService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
import dev.usbharu.hideout.service.api.IPostApiService
import dev.usbharu.hideout.service.api.IUserApiService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.auth.IJwtService
@ -23,7 +24,6 @@ import dev.usbharu.hideout.service.core.IdGenerateService
import dev.usbharu.hideout.service.core.TwitterSnowflakeIdGenerateService
import dev.usbharu.hideout.service.job.JobQueueParentService
import dev.usbharu.hideout.service.job.KJobJobQueueParentService
import dev.usbharu.hideout.service.post.IPostService
import dev.usbharu.hideout.service.user.IUserAuthService
import dev.usbharu.hideout.service.user.IUserService
import dev.usbharu.kjob.exposed.ExposedKJob
@ -111,7 +111,7 @@ fun Application.parent() {
activityPubService = inject<ActivityPubService>().value,
userService = inject<IUserService>().value,
activityPubUserService = inject<ActivityPubUserService>().value,
postService = inject<IPostService>().value,
postService = inject<IPostApiService>().value,
userApiService = inject<IUserApiService>().value,
)
}

View File

@ -5,13 +5,12 @@ import dev.usbharu.hideout.routing.activitypub.outbox
import dev.usbharu.hideout.routing.activitypub.usersAP
import dev.usbharu.hideout.routing.api.internal.v1.posts
import dev.usbharu.hideout.routing.api.internal.v1.users
import dev.usbharu.hideout.routing.api.mastodon.v1.statuses
import dev.usbharu.hideout.routing.wellknown.webfinger
import dev.usbharu.hideout.service.activitypub.ActivityPubService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
import dev.usbharu.hideout.service.api.IPostApiService
import dev.usbharu.hideout.service.api.IUserApiService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.post.IPostService
import dev.usbharu.hideout.service.user.IUserService
import io.ktor.server.application.*
import io.ktor.server.plugins.autohead.*
@ -23,7 +22,7 @@ fun Application.configureRouting(
activityPubService: ActivityPubService,
userService: IUserService,
activityPubUserService: ActivityPubUserService,
postService: IPostService,
postService: IPostApiService,
userApiService: IUserApiService
) {
install(AutoHeadResponse)
@ -32,10 +31,6 @@ fun Application.configureRouting(
outbox()
usersAP(activityPubUserService, userService)
webfinger(userService)
route("/api/v1") {
statuses(postService)
}
route("/api/internal/v1") {
posts(postService)
users(userService, userApiService)

View File

@ -1,13 +1,11 @@
package dev.usbharu.hideout.routing.api.internal.v1
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.form.Post
import dev.usbharu.hideout.exception.ParameterNotExistException
import dev.usbharu.hideout.exception.PostNotFoundException
import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.service.post.IPostService
import dev.usbharu.hideout.util.AcctUtil
import dev.usbharu.hideout.service.api.IPostApiService
import dev.usbharu.hideout.util.InstantParseUtil
import io.ktor.http.*
import io.ktor.server.application.*
@ -18,7 +16,7 @@ import io.ktor.server.response.*
import io.ktor.server.routing.*
@Suppress("LongMethod")
fun Route.posts(postService: IPostService) {
fun Route.posts(postApiService: IPostApiService) {
route("/posts") {
authenticate(TOKEN_AUTH) {
post {
@ -34,7 +32,7 @@ fun Route.posts(postService: IPostService) {
repolyId = receive.replyId,
userId = userId
)
val create = postService.create(postCreateDto)
val create = postApiService.createPost(postCreateDto)
call.response.header("Location", create.url)
call.respond(HttpStatusCode.OK)
}
@ -47,16 +45,13 @@ fun Route.posts(postService: IPostService) {
val minId = call.request.queryParameters["minId"]?.toLong()
val maxId = call.request.queryParameters["maxId"]?.toLong()
val limit = call.request.queryParameters["limit"]?.toInt()
call.respond(HttpStatusCode.OK, postService.findAll(since, until, minId, maxId, limit, userId))
call.respond(HttpStatusCode.OK, postApiService.getAll(since, until, minId, maxId, limit, userId))
}
get("/{id}") {
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
val id = call.parameters["id"]?.toLong()
?: throw ParameterNotExistException("Parameter(id='postsId') does not exist.")
val post = (
postService.findByIdForUser(id, userId)
?: throw PostNotFoundException("$id was not found or is not authorized.")
)
val post = postApiService.getById(id, userId)
call.respond(post)
}
}
@ -67,27 +62,14 @@ fun Route.posts(postService: IPostService) {
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
val targetUserName = call.parameters["name"]
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
val targetUserId = targetUserName.toLongOrNull()
val posts = if (targetUserId == null) {
val acct = AcctUtil.parse(targetUserName)
postService.findByUserNameAndDomainForUser(
acct.username,
acct.domain ?: Config.configData.domain,
forUserId = userId
)
} else {
postService.findByUserIdForUser(targetUserId, forUserId = userId)
}
val posts = postApiService.getByUser(targetUserName, userId = userId)
call.respond(posts)
}
get("/{id}") {
val userId = call.principal<JWTPrincipal>()?.payload?.getClaim("uid")?.asLong()
val id = call.parameters["id"]?.toLong()
?: throw ParameterNotExistException("Parameter(name='postsId' does not exist.")
val post = (
postService.findByIdForUser(id, userId)
?: throw PostNotFoundException("$id was not found or is not authorized.")
)
val post = postApiService.getById(id, userId)
call.respond(post)
}
}

View File

@ -1,21 +1,18 @@
package dev.usbharu.hideout.routing.api.mastodon.v1
import dev.usbharu.hideout.service.post.IPostService
import io.ktor.server.routing.*
@Suppress("UnusedPrivateMember")
fun Route.statuses(postService: IPostService) {
// route("/statuses") {
// post {
// val status: StatusForPost = call.receive()
// val post = dev.usbharu.hideout.domain.model.hideout.form.Post(
// userId = status.userId,
// createdAt = System.currentTimeMillis(),
// text = status.status,
// visibility = 1
// )
// postService.create(post)
// call.respond(status)
// @Suppress("UnusedPrivateMember")
// fun Route.statuses(postService: IPostService) {
// // route("/statuses") {
// // post {
// // val status: StatusForPost = call.receive()
// // val post = dev.usbharu.hideout.domain.model.hideout.form.Post(
// // userId = status.userId,
// // createdAt = System.currentTimeMillis(),
// // text = status.status,
// // visibility = 1
// // )
// // postService.create(post)
// // call.respond(status)
// // }
// // }
// }
// }
}

View File

@ -0,0 +1,24 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import java.time.Instant
interface IPostApiService {
suspend fun createPost(postCreateDto: PostCreateDto): Post
suspend fun getById(id: Long, userId: Long?): Post
suspend fun getAll(since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null): List<Post>
suspend fun getByUser(nameOrId: String,
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null): List<Post>
}

View File

@ -1,61 +1,9 @@
package dev.usbharu.hideout.service.post
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import java.time.Instant
@Suppress("LongParameterList")
interface IPostService {
suspend fun create(post: Post): Post
suspend fun create(post: PostCreateDto): Post
suspend fun findAll(
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = 10,
userId: Long? = null
): List<Post>
suspend fun findById(id: String): Post
/**
* 権限を考慮して投稿を取得します
*
* @param id
* @param userId
* @return
*/
suspend fun findByIdForUser(id: Long, userId: Long?): Post?
/**
* 権限を考慮してユーザーの投稿を取得します
*
* @param userId
* @param forUserId
* @return
*/
suspend fun findByUserIdForUser(
userId: Long,
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
forUserId: Long? = null
): List<Post>
suspend fun findByUserNameAndDomainForUser(
userName: String,
domain: String = Config.configData.domain,
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
forUserId: Long? = null
): List<Post>
suspend fun delete(id: String)
suspend fun createLocal(post: Post): Post
suspend fun createRemote(note: Note): Post
}

View File

@ -1,130 +0,0 @@
package dev.usbharu.hideout.service.post
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.repository.IPostRepository
import dev.usbharu.hideout.repository.Posts
import dev.usbharu.hideout.repository.UsersFollowers
import dev.usbharu.hideout.repository.toPost
import dev.usbharu.hideout.service.activitypub.ActivityPubNoteService
import dev.usbharu.hideout.service.user.IUserService
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inSubQuery
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.orIfNotNull
import org.jetbrains.exposed.sql.orWhere
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.koin.core.annotation.Single
import org.slf4j.LoggerFactory
import java.time.Instant
@Single
class PostService(
private val postRepository: IPostRepository,
private val activityPubNoteService: ActivityPubNoteService,
private val userService: IUserService
) : IPostService {
private val logger = LoggerFactory.getLogger(this::class.java)
override suspend fun create(post: Post): Post {
logger.debug("create post={}", post)
val postEntity = postRepository.save(post)
activityPubNoteService.createNote(postEntity)
return post
}
override suspend fun create(post: PostCreateDto): Post {
logger.debug("create post={}", post)
val user = userService.findById(post.userId)
val id = postRepository.generateId()
val postEntity = Post(
id = id,
userId = user.id,
overview = null,
text = post.text,
createdAt = Instant.now().toEpochMilli(),
visibility = Visibility.PUBLIC,
url = "${user.url}/posts/$id",
repostId = null,
replyId = null
)
postRepository.save(postEntity)
return postEntity
}
override suspend fun findAll(
since: Instant?,
until: Instant?,
minId: Long?,
maxId: Long?,
limit: Int?,
userId: Long?
): List<Post> {
return transaction {
val select = Posts.select {
Posts.visibility.eq(Visibility.PUBLIC.ordinal)
}
if (userId != null) {
select.orWhere {
Posts.userId.inSubQuery(
UsersFollowers.slice(UsersFollowers.userId).select(UsersFollowers.followerId eq userId)
)
}
}
select.map { it.toPost() }
}
}
override suspend fun findById(id: String): Post {
TODO("Not yet implemented")
}
override suspend fun findByIdForUser(id: Long, userId: Long?): Post? {
return transaction {
val select = Posts.select(
Posts.id.eq(id).and(
Posts.visibility.eq(Visibility.PUBLIC.ordinal).orIfNotNull(
userId?.let {
Posts.userId.inSubQuery(
UsersFollowers.slice(UsersFollowers.userId).select(UsersFollowers.followerId.eq(userId))
)
}
)
)
)
select.singleOrNull()?.toPost()
}
}
override suspend fun findByUserIdForUser(
userId: Long,
since: Instant?,
until: Instant?,
minId: Long?,
maxId: Long?,
limit: Int?,
forUserId: Long?
): List<Post> {
TODO("Not yet implemented")
}
override suspend fun findByUserNameAndDomainForUser(
userName: String,
domain: String,
since: Instant?,
until: Instant?,
minId: Long?,
maxId: Long?,
limit: Int?,
forUserId: Long?
): List<Post> {
TODO("Not yet implemented")
}
override suspend fun delete(id: String) {
TODO("Not yet implemented")
}
}

View File

@ -10,7 +10,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.plugins.configureSecurity
import dev.usbharu.hideout.plugins.configureSerialization
import dev.usbharu.hideout.service.post.IPostService
import dev.usbharu.hideout.service.api.IPostApiService
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
@ -51,9 +51,9 @@ class PostsTest {
url = "https://example.com/posts/2"
)
)
val postService = mock<IPostService> {
val postService = mock<IPostApiService> {
onBlocking {
findAll(
getAll(
since = anyOrNull(),
until = anyOrNull(),
minId = anyOrNull(),
@ -118,9 +118,9 @@ class PostsTest {
)
)
val postService = mock<IPostService> {
val postService = mock<IPostApiService> {
onBlocking {
findAll(
getAll(
since = anyOrNull(),
until = anyOrNull(),
minId = anyOrNull(),
@ -165,8 +165,8 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
)
val postService = mock<IPostService> {
onBlocking { findByIdForUser(any(), anyOrNull()) } doReturn post
val postService = mock<IPostApiService> {
onBlocking { getById(any(), anyOrNull()) } doReturn post
}
application {
configureSerialization()
@ -196,8 +196,8 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
)
val postService = mock<IPostService> {
onBlocking { findByIdForUser(any(), isNotNull()) } doReturn post
val postService = mock<IPostApiService> {
onBlocking { getById(any(), isNotNull()) } doReturn post
}
val claim = mock<Claim> {
on { asLong() } doReturn 1234
@ -239,8 +239,8 @@ class PostsTest {
val payload = mock<Payload> {
on { getClaim(eq("uid")) } doReturn claim
}
val postService = mock<IPostService> {
onBlocking { create(any<PostCreateDto>()) } doAnswer {
val postService = mock<IPostApiService> {
onBlocking { createPost(any<PostCreateDto>()) } doAnswer {
val argument = it.getArgument<PostCreateDto>(0)
Post(
123L,
@ -280,7 +280,7 @@ class PostsTest {
assertEquals("https://example.com", headers["Location"])
}
argumentCaptor<PostCreateDto> {
verify(postService).create(capture())
verify(postService).createPost(capture())
assertEquals(PostCreateDto("test", userId = 1234), firstValue)
}
}
@ -308,16 +308,16 @@ class PostsTest {
url = "https://example.com/posts/2"
)
)
val postService = mock<IPostService> {
val postService = mock<IPostApiService> {
onBlocking {
findByUserIdForUser(
userId = any(),
getByUser(
nameOrId = any(),
since = anyOrNull(),
until = anyOrNull(),
minId = anyOrNull(),
maxId = anyOrNull(),
limit = anyOrNull(),
forUserId = anyOrNull()
userId = anyOrNull()
)
} doReturn posts
}
@ -360,17 +360,16 @@ class PostsTest {
url = "https://example.com/posts/2"
)
)
val postService = mock<IPostService> {
val postService = mock<IPostApiService> {
onBlocking {
findByUserNameAndDomainForUser(
userName = eq("test1"),
domain = eq(Config.configData.domain),
getByUser(
nameOrId = eq("test1"),
since = anyOrNull(),
until = anyOrNull(),
minId = anyOrNull(),
maxId = anyOrNull(),
limit = anyOrNull(),
forUserId = anyOrNull()
userId = anyOrNull()
)
} doReturn posts
}
@ -413,17 +412,16 @@ class PostsTest {
url = "https://example.com/posts/2"
)
)
val postService = mock<IPostService> {
val postService = mock<IPostApiService> {
onBlocking {
findByUserNameAndDomainForUser(
userName = eq("test1"),
domain = eq("example.com"),
getByUser(
nameOrId = eq("test1@example.com"),
since = anyOrNull(),
until = anyOrNull(),
minId = anyOrNull(),
maxId = anyOrNull(),
limit = anyOrNull(),
forUserId = anyOrNull()
userId = anyOrNull()
)
} doReturn posts
}
@ -466,17 +464,16 @@ class PostsTest {
url = "https://example.com/posts/2"
)
)
val postService = mock<IPostService> {
val postService = mock<IPostApiService> {
onBlocking {
findByUserNameAndDomainForUser(
userName = eq("test1"),
domain = eq("example.com"),
getByUser(
nameOrId = eq("@test1@example.com"),
since = anyOrNull(),
until = anyOrNull(),
minId = anyOrNull(),
maxId = anyOrNull(),
limit = anyOrNull(),
forUserId = anyOrNull()
userId = anyOrNull()
)
} doReturn posts
}
@ -509,8 +506,8 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2"
)
val postService = mock<IPostService> {
onBlocking { findByIdForUser(eq(12345L), anyOrNull()) } doReturn post
val postService = mock<IPostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
}
application {
configureSerialization()
@ -541,8 +538,8 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2"
)
val postService = mock<IPostService> {
onBlocking { findByIdForUser(eq(12345L), anyOrNull()) } doReturn post
val postService = mock<IPostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
}
application {
configureSerialization()
@ -573,8 +570,8 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2"
)
val postService = mock<IPostService> {
onBlocking { findByIdForUser(eq(12345L), anyOrNull()) } doReturn post
val postService = mock<IPostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
}
application {
configureSerialization()
@ -605,8 +602,8 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2"
)
val postService = mock<IPostService> {
onBlocking { findByIdForUser(eq(12345L), anyOrNull()) } doReturn post
val postService = mock<IPostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
}
application {
configureSerialization()

View File

@ -1,166 +0,0 @@
@file:OptIn(ExperimentalCoroutinesApi::class)
package dev.usbharu.hideout.service.post
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.repository.Posts
import dev.usbharu.hideout.repository.UsersFollowers
import dev.usbharu.hideout.service.core.TwitterSnowflakeIdGenerateService
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.batchInsert
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.transaction
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.kotlin.mock
import java.time.Instant
import kotlin.test.assertContentEquals
class PostServiceTest {
lateinit var db: Database
@BeforeEach
fun setUp() {
db = Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
transaction(db) {
SchemaUtils.create(Posts)
connection.prepareStatement("SET REFERENTIAL_INTEGRITY FALSE", false).executeUpdate()
}
}
@AfterEach
fun tearDown() {
transaction(db) {
SchemaUtils.drop(Posts)
}
}
@Test
fun `findAll 公開投稿を取得できる`() = runTest {
val postService = PostService(mock(), mock(), mock())
suspend fun createPost(userId: Long, text: String, visibility: Visibility = Visibility.PUBLIC): Post {
return Post(
TwitterSnowflakeIdGenerateService.generateId(),
userId,
null,
text,
Instant.now().toEpochMilli(),
visibility,
"https://example.com${(userId.toString() + text).hashCode()}"
)
}
val userA: Long = 1
val userB: Long = 2
val posts = listOf(
createPost(userA, "hello"),
createPost(userA, "hello1"),
createPost(userA, "hello2"),
createPost(userA, "hello3"),
createPost(userA, "hello4"),
createPost(userA, "hello5"),
createPost(userA, "hello6"),
createPost(userB, "good bay ", Visibility.FOLLOWERS),
createPost(userB, "good bay1", Visibility.FOLLOWERS),
createPost(userB, "good bay2", Visibility.FOLLOWERS),
createPost(userB, "good bay3", Visibility.FOLLOWERS),
createPost(userB, "good bay4", Visibility.FOLLOWERS),
createPost(userB, "good bay5", Visibility.FOLLOWERS),
createPost(userB, "good bay6", Visibility.FOLLOWERS),
)
transaction {
Posts.batchInsert(posts) {
this[Posts.id] = it.id
this[Posts.userId] = it.userId
this[Posts.overview] = it.overview
this[Posts.text] = it.text
this[Posts.createdAt] = it.createdAt
this[Posts.visibility] = it.visibility.ordinal
this[Posts.url] = it.url
this[Posts.replyId] = it.replyId
this[Posts.repostId] = it.repostId
this[Posts.sensitive] = it.sensitive
this[Posts.apId] = it.apId
}
}
val expect = posts.filter { it.visibility == Visibility.PUBLIC }
val actual = postService.findAll()
assertContentEquals(expect, actual)
}
@Test
fun `findAll フォロー限定投稿を見れる`() = runTest {
val postService = PostService(mock(), mock(), mock())
suspend fun createPost(userId: Long, text: String, visibility: Visibility = Visibility.PUBLIC): Post {
return Post(
TwitterSnowflakeIdGenerateService.generateId(),
userId,
null,
text,
Instant.now().toEpochMilli(),
visibility,
"https://example.com${(userId.toString() + text).hashCode()}"
)
}
val userA: Long = 1
val userB: Long = 2
val posts = listOf(
createPost(userA, "hello"),
createPost(userA, "hello1"),
createPost(userA, "hello2"),
createPost(userA, "hello3"),
createPost(userA, "hello4"),
createPost(userA, "hello5"),
createPost(userA, "hello6"),
createPost(userB, "good bay ", Visibility.FOLLOWERS),
createPost(userB, "good bay1", Visibility.FOLLOWERS),
createPost(userB, "good bay2", Visibility.FOLLOWERS),
createPost(userB, "good bay3", Visibility.FOLLOWERS),
createPost(userB, "good bay4", Visibility.FOLLOWERS),
createPost(userB, "good bay5", Visibility.FOLLOWERS),
createPost(userB, "good bay6", Visibility.FOLLOWERS),
)
transaction(db) {
SchemaUtils.create(UsersFollowers)
}
transaction {
Posts.batchInsert(posts) {
this[Posts.id] = it.id
this[Posts.userId] = it.userId
this[Posts.overview] = it.overview
this[Posts.text] = it.text
this[Posts.createdAt] = it.createdAt
this[Posts.visibility] = it.visibility.ordinal
this[Posts.url] = it.url
this[Posts.replyId] = it.replyId
this[Posts.repostId] = it.repostId
this[Posts.sensitive] = it.sensitive
this[Posts.apId] = it.apId
}
UsersFollowers.insert {
it[id] = 100L
it[userId] = userB
it[followerId] = userA
}
}
val actual = postService.findAll(userId = userA)
assertContentEquals(posts, actual)
}
}