Merge pull request #603 from usbharu/repository-test

リポジトリのテストを追加
This commit is contained in:
usbharu 2024-09-14 20:44:12 +09:00 committed by GitHub
commit 5f97c45906
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 5635 additions and 196 deletions

View File

@ -2,3 +2,4 @@ org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED
kotlin.compiler.preciseCompilationResultsBackup=true

View File

@ -132,6 +132,8 @@ dependencies {
testImplementation(libs.ktor.client.mock)
testImplementation(libs.h2db)
testImplementation(libs.mockito.kotlin)
testImplementation("org.assertj:assertj-db:2.0.2")
testImplementation("com.ninja-squad:DbSetup-kotlin:2.1.0")
}
detekt {

View File

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
kotlin.code.style=official
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED
kotlin.compiler.preciseCompilationResultsBackup=true

View File

@ -56,7 +56,7 @@ class MigrationLocalActorApplicationService(
val canAccountMigration =
localActorMigrationCheckDomainService.canAccountMigration(userDetail, fromActor, toActor)
if (canAccountMigration.canMigration) {
fromActor.moveTo = toActorId
fromActor.setMoveTo(toActorId)
actorRepository.save(fromActor)
} else {
when (canAccountMigration) {

View File

@ -40,7 +40,7 @@ class GetPostDetailApplicationService(
val iconMedia = actor.icon?.let { mediaRepository.findById(it) }
val mediaList = mediaRepository.findByIds(post.mediaIds)
val mediaList = mediaRepository.findByIdIn(post.mediaIds)
val reactions = reactionsQueryService.findAllByPostId(post.id)
@ -82,7 +82,7 @@ class GetPostDetailApplicationService(
actor to iconMedia
}
val mediaList = mediaRepository.findByIds(post.mediaIds)
val mediaList = mediaRepository.findByIdIn(post.mediaIds)
return PostDetail.of(
post = post,
actor = first,

View File

@ -85,30 +85,39 @@ class Actor(
}
var alsoKnownAs = alsoKnownAs
set(value) {
require(value.none { it == id })
field = value
private set
fun setAlsoKnownAs(alsoKnownAs: Set<ActorId>) {
require(alsoKnownAs.none { it == id })
this.alsoKnownAs = alsoKnownAs
}
var moveTo = moveTo
set(value) {
require(value != id)
private set
fun setMoveTo(moveTo: ActorId?) {
require(moveTo != id)
addDomainEvent(ActorDomainEventFactory(this).createEvent(MOVE))
field = value
this.moveTo = moveTo
}
var emojis = emojiIds
private set
var description = description
set(value) {
private set
fun setDescription(description: ActorDescription) {
addDomainEvent(ActorDomainEventFactory(this).createEvent(UPDATE))
field = value
this.description = description
}
var screenName = screenName
set(value) {
private set
fun setScreenName(screenName: ActorScreenName) {
addDomainEvent(ActorDomainEventFactory(this).createEvent(UPDATE))
field = value
this.screenName = screenName
}
var deleted = deleted
@ -135,4 +144,48 @@ class Actor(
fun checkUpdate() {
addDomainEvent(ActorDomainEventFactory(this).createEvent(CHECK_UPDATE))
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Actor
return id == other.id
}
override fun hashCode(): Int = id.hashCode()
override fun toString(): String {
return "Actor(" +
"id=$id, " +
"name=$name, " +
"domain=$domain, " +
"inbox=$inbox, " +
"outbox=$outbox, " +
"url=$url, " +
"publicKey=$publicKey, " +
"privateKey=$privateKey, " +
"createdAt=$createdAt, " +
"keyId=$keyId, " +
"followersEndpoint=$followersEndpoint, " +
"followingEndpoint=$followingEndpoint, " +
"instance=$instance, " +
"locked=$locked, " +
"followersCount=$followersCount, " +
"followingCount=$followingCount, " +
"postsCount=$postsCount, " +
"lastPostAt=$lastPostAt, " +
"lastUpdateAt=$lastUpdateAt, " +
"banner=$banner, " +
"icon=$icon, " +
"suspend=$suspend, " +
"alsoKnownAs=$alsoKnownAs, " +
"moveTo=$moveTo, " +
"emojis=$emojis, " +
"description=$description, " +
"screenName=$screenName, " +
"deleted=$deleted" +
")"
}
}

View File

@ -19,6 +19,17 @@ package dev.usbharu.hideout.core.domain.model.actor
class ActorDescription(description: String) {
val description: String = description.take(LENGTH)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ActorDescription
return description == other.description
}
override fun hashCode(): Int = description.hashCode()
companion object {
const val LENGTH = 10000
val empty = ActorDescription("")

View File

@ -20,6 +20,17 @@ class ActorScreenName(screenName: String) {
val screenName: String = screenName.take(LENGTH)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ActorScreenName
return screenName == other.screenName
}
override fun hashCode(): Int = screenName.hashCode()
companion object {
const val LENGTH = 300
val empty = ActorScreenName("")

View File

@ -4,4 +4,17 @@ class FilterKeyword(
val id: FilterKeywordId,
var keyword: FilterKeywordKeyword,
val mode: FilterMode
)
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FilterKeyword
return id == other.id
}
override fun hashCode(): Int {
return id.hashCode()
}
}

View File

@ -4,6 +4,19 @@ class FilterName(name: String) {
val name = name.take(LENGTH)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FilterName
return name == other.name
}
override fun hashCode(): Int {
return name.hashCode()
}
companion object {
const val LENGTH = 300
}

View File

@ -19,6 +19,6 @@ package dev.usbharu.hideout.core.domain.model.media
interface MediaRepository {
suspend fun save(media: Media): Media
suspend fun findById(id: MediaId): Media?
suspend fun findByIds(ids: List<MediaId>): List<Media>
suspend fun findByIdIn(ids: List<MediaId>): List<Media>
suspend fun delete(media: Media)
}

View File

@ -34,18 +34,4 @@ interface RelationshipRepository {
targetIds: List<ActorId>,
following: Boolean
): List<Relationship>
suspend fun findByTargetId(
targetId: ActorId,
option: FindRelationshipOption? = null,
inverseOption: FindRelationshipOption? = null
): List<Relationship>
}
data class FindRelationshipOption(
val follow: Boolean? = null,
val block: Boolean? = null,
val mute: Boolean? = null,
val followRequest: Boolean? = null,
val muteFollowRequest: Boolean? = null
)

View File

@ -26,6 +26,17 @@ class Timeline(
addDomainEvent(TimelineEventFactory(this).createEvent(TimelineEvent.CHANGE_VISIBILITY))
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Timeline
return id == other.id
}
override fun hashCode(): Int = id.hashCode()
companion object {
fun create(
id: TimelineId,

View File

@ -35,7 +35,7 @@ class ActorQueryMapper(private val actorResultRowMapper: ResultRowMapper<Actor>)
.first()
.let(actorResultRowMapper::map)
.apply {
alsoKnownAs = buildAlsoKnownAs(it)
setAlsoKnownAs(buildAlsoKnownAs(it))
clearDomainEvents()
}
}

View File

@ -24,7 +24,6 @@ import dev.usbharu.hideout.core.domain.model.support.domain.Domain
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
import org.jetbrains.exposed.sql.ResultRow
import org.springframework.stereotype.Component
import java.net.URI
@Component
class ActorResultRowMapper : ResultRowMapper<Actor> {
@ -35,15 +34,15 @@ class ActorResultRowMapper : ResultRowMapper<Actor> {
domain = Domain(resultRow[Actors.domain]),
screenName = ActorScreenName(resultRow[Actors.screenName]),
description = ActorDescription(resultRow[Actors.description]),
inbox = URI.create(resultRow[Actors.inbox]),
outbox = URI.create(resultRow[Actors.outbox]),
url = URI.create(resultRow[Actors.url]),
inbox = resultRow[Actors.inbox],
outbox = resultRow[Actors.outbox],
url = resultRow[Actors.url],
publicKey = ActorPublicKey(resultRow[Actors.publicKey]),
privateKey = resultRow[Actors.privateKey]?.let { ActorPrivateKey(it) },
createdAt = resultRow[Actors.createdAt],
keyId = ActorKeyId(resultRow[Actors.keyId]),
followersEndpoint = resultRow[Actors.followers]?.let { URI.create(it) },
followingEndpoint = resultRow[Actors.following]?.let { URI.create(it) },
followersEndpoint = resultRow[Actors.followers],
followingEndpoint = resultRow[Actors.following],
instance = InstanceId(resultRow[Actors.instance]),
locked = resultRow[Actors.locked],
followersCount = resultRow[Actors.followersCount]?.let { ActorRelationshipCount(it) },

View File

@ -33,7 +33,7 @@ class FilterQueryMapper(private val filterResultRowMapper: ResultRowMapper<Filte
it
.first()
.let(filterResultRowMapper::map)
.apply {
.run {
reconstructWith(
it.mapNotNull { resultRow: ResultRow ->
FilterKeyword(

View File

@ -0,0 +1,22 @@
package dev.usbharu.hideout.core.infrastructure.exposed
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.ColumnType
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.vendors.currentDialect
import java.net.URI
class UriColumnType(val colLength: Int) : ColumnType<URI>() {
override fun sqlType(): String = currentDialect.dataTypeProvider.varcharType(colLength)
override fun valueFromDB(value: Any): URI? = when (value) {
is URI -> value
is String -> URI(value)
is CharSequence -> URI(value.toString())
else -> error("Unexpected value of type String: $value of ${value::class.qualifiedName}")
}
override fun notNullValueToDB(value: URI): Any = value.toString()
}
fun Table.uri(name: String, colLength: Int): Column<URI> = registerColumn(name, UriColumnType(colLength))

View File

@ -90,9 +90,9 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi
name = it[Actors.name],
domain = it[Actors.domain],
screenName = it[Actors.screenName],
url = URI.create(it[Actors.url]),
url = it[Actors.url],
locked = it[Actors.locked],
icon = it.getOrNull(iconMedia[Media.url])?.let { URI.create(it) }
icon = it.getOrNull(iconMedia[Media.url])
),
overview = it[authorizedQuery[Posts.overview]],
text = it[authorizedQuery[Posts.text]],

View File

@ -75,6 +75,7 @@ class ExposedActorInstanceRelationshipRepository(override val domainEventPublish
ActorInstanceRelationships.actorId eq actorId.id and
(ActorInstanceRelationships.instanceId eq instanceId.instanceId)
}
.limit(1)
.singleOrNull()
?.toActorInstanceRelationship()
}

View File

@ -5,6 +5,7 @@ import dev.usbharu.hideout.core.domain.model.support.domain.Domain
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
import dev.usbharu.hideout.core.infrastructure.exposed.uri
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp
@ -30,15 +31,15 @@ class ExposedActorRepository(
it[domain] = actor.domain.domain
it[screenName] = actor.screenName.screenName
it[description] = actor.description.description
it[inbox] = actor.inbox.toString()
it[outbox] = actor.outbox.toString()
it[url] = actor.url.toString()
it[inbox] = actor.inbox
it[outbox] = actor.outbox
it[url] = actor.url
it[publicKey] = actor.publicKey.publicKey
it[privateKey] = actor.privateKey?.privateKey
it[createdAt] = actor.createdAt
it[keyId] = actor.keyId.keyId
it[following] = actor.followingEndpoint?.toString()
it[followers] = actor.followersEndpoint?.toString()
it[following] = actor.followingEndpoint
it[followers] = actor.followersEndpoint
it[instance] = actor.instance.instanceId
it[locked] = actor.locked
it[followingCount] = actor.followingCount?.relationshipCount
@ -69,8 +70,8 @@ class ExposedActorRepository(
override suspend fun delete(actor: Actor) {
query {
Actors.deleteWhere { id eq actor.id.id }
ActorsAlsoKnownAs.deleteWhere { actorId eq actor.id.id }
Actors.deleteWhere { id eq actor.id.id }
onComplete {
update(actor)
}
@ -126,15 +127,15 @@ object Actors : Table("actors") {
val domain = varchar("domain", Domain.LENGTH)
val screenName = varchar("screen_name", ActorScreenName.LENGTH)
val description = varchar("description", ActorDescription.LENGTH)
val inbox = varchar("inbox", 1000).uniqueIndex()
val outbox = varchar("outbox", 1000).uniqueIndex()
val url = varchar("url", 1000).uniqueIndex()
val inbox = uri("inbox", 1000).uniqueIndex()
val outbox = uri("outbox", 1000).uniqueIndex()
val url = uri("url", 1000).uniqueIndex()
val publicKey = varchar("public_key", 10000)
val privateKey = varchar("private_key", 100000).nullable()
val createdAt = timestamp("created_at")
val keyId = varchar("key_id", 1000)
val following = varchar("following", 1000).nullable()
val followers = varchar("followers", 1000).nullable()
val following = uri("following", 1000).nullable()
val followers = uri("followers", 1000).nullable()
val instance = long("instance").references(Instance.id)
val locked = bool("locked")
val followingCount = integer("following_count").nullable()

View File

@ -31,8 +31,7 @@ import org.springframework.stereotype.Repository
import java.net.URI
@Repository
class CustomEmojiRepositoryImpl : CustomEmojiRepository,
AbstractRepository() {
class ExposedCustomEmojiRepository : CustomEmojiRepository, AbstractRepository() {
override val logger: Logger
get() = Companion.logger
@ -50,7 +49,12 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
}
override suspend fun findById(id: Long): CustomEmoji? = query {
return@query CustomEmojis.selectAll().where { CustomEmojis.id eq id }.singleOrNull()?.toCustomEmoji()
CustomEmojis
.selectAll()
.where { CustomEmojis.id eq id }
.limit(1)
.singleOrNull()
?.toCustomEmoji()
}
override suspend fun delete(customEmoji: CustomEmoji): Unit = query {
@ -58,7 +62,7 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
}
override suspend fun findByNamesAndDomain(names: List<String>, domain: String): List<CustomEmoji> = query {
return@query CustomEmojis
CustomEmojis
.selectAll()
.where {
CustomEmojis.name inList names and (CustomEmojis.domain eq domain)
@ -67,7 +71,7 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
}
override suspend fun findByIds(ids: List<Long>): List<CustomEmoji> = query {
return@query CustomEmojis
CustomEmojis
.selectAll()
.where {
CustomEmojis.id inList ids
@ -76,7 +80,7 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
}
companion object {
private val logger = LoggerFactory.getLogger(CustomEmojiRepositoryImpl::class.java)
private val logger = LoggerFactory.getLogger(ExposedCustomEmojiRepository::class.java)
}
}

View File

@ -63,18 +63,20 @@ class ExposedFilterRepository(private val filterQueryMapper: QueryMapper<Filter>
val filterId = FilterKeywords
.selectAll()
.where { FilterKeywords.id eq filterKeywordId.id }
.limit(1)
.firstOrNull()?.get(FilterKeywords.filterId) ?: return@query null
val where = Filters.selectAll().where { Filters.id eq filterId }
val where = Filters.leftJoin(FilterKeywords).selectAll().where { Filters.id eq filterId }
return@query filterQueryMapper.map(where).firstOrNull()
}
override suspend fun findByFilterId(filterId: FilterId): Filter? = query {
val where = Filters.selectAll().where { Filters.id eq filterId.id }
val where = Filters.leftJoin(FilterKeywords).selectAll().where { Filters.id eq filterId.id }
return@query filterQueryMapper.map(where).firstOrNull()
}
override suspend fun findByUserDetailId(userDetailId: UserDetailId): List<Filter> = query {
return@query Filters.selectAll().where { Filters.userId eq userDetailId.id }.let(filterQueryMapper::map)
return@query Filters.leftJoin(FilterKeywords).selectAll().where { Filters.userId eq userDetailId.id }
.let(filterQueryMapper::map)
}
companion object {

View File

@ -17,6 +17,7 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.model.instance.*
import dev.usbharu.hideout.core.infrastructure.exposed.uri
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp
@ -27,7 +28,7 @@ import java.net.URI
import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity
@Repository
class InstanceRepositoryImpl : InstanceRepository,
class ExposedInstanceRepository : InstanceRepository,
AbstractRepository() {
override val logger: Logger
get() = Companion.logger
@ -37,9 +38,9 @@ class InstanceRepositoryImpl : InstanceRepository,
it[id] = instance.id.instanceId
it[name] = instance.name.name
it[description] = instance.description.description
it[url] = instance.url.toString()
it[iconUrl] = instance.iconUrl.toString()
it[sharedInbox] = instance.sharedInbox?.toString()
it[url] = instance.url
it[iconUrl] = instance.iconUrl
it[sharedInbox] = instance.sharedInbox
it[software] = instance.software.software
it[version] = instance.version.version
it[isBlocked] = instance.isBlocked
@ -52,7 +53,7 @@ class InstanceRepositoryImpl : InstanceRepository,
}
override suspend fun findById(id: InstanceId): InstanceEntity? = query {
return@query Instance.selectAll().where { Instance.id eq id.instanceId }
return@query Instance.selectAll().where { Instance.id eq id.instanceId }.limit(1)
.singleOrNull()?.toInstance()
}
@ -61,11 +62,11 @@ class InstanceRepositoryImpl : InstanceRepository,
}
override suspend fun findByUrl(url: URI): dev.usbharu.hideout.core.domain.model.instance.Instance? = query {
return@query Instance.selectAll().where { Instance.url eq url.toString() }.singleOrNull()?.toInstance()
return@query Instance.selectAll().where { Instance.url eq url }.limit(1).singleOrNull()?.toInstance()
}
companion object {
private val logger = LoggerFactory.getLogger(InstanceRepositoryImpl::class.java)
private val logger = LoggerFactory.getLogger(ExposedInstanceRepository::class.java)
}
}
@ -74,9 +75,9 @@ fun ResultRow.toInstance(): InstanceEntity {
id = InstanceId(this[Instance.id]),
name = InstanceName(this[Instance.name]),
description = InstanceDescription(this[Instance.description]),
url = URI.create(this[Instance.url]),
iconUrl = URI.create(this[Instance.iconUrl]),
sharedInbox = this[Instance.sharedInbox]?.let { URI.create(it) },
url = this[Instance.url],
iconUrl = this[Instance.iconUrl],
sharedInbox = this[Instance.sharedInbox],
software = InstanceSoftware(this[Instance.software]),
version = InstanceVersion(this[Instance.version]),
isBlocked = this[Instance.isBlocked],
@ -90,9 +91,9 @@ object Instance : Table("instance") {
val id = long("id")
val name = varchar("name", 1000)
val description = varchar("description", 5000)
val url = varchar("url", 255).uniqueIndex()
val iconUrl = varchar("icon_url", 255)
val sharedInbox = varchar("shared_inbox", 255).nullable().uniqueIndex()
val url = uri("url", 255).uniqueIndex()
val iconUrl = uri("icon_url", 255)
val sharedInbox = uri("shared_inbox", 255).nullable().uniqueIndex()
val software = varchar("software", 255)
val version = varchar("version", 255)
val isBlocked = bool("is_blocked")

View File

@ -18,16 +18,16 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.media.*
import dev.usbharu.hideout.core.infrastructure.exposed.uri
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
import java.net.URI
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
@Repository
class MediaRepositoryImpl : MediaRepository, AbstractRepository() {
class ExposedMediaRepository : MediaRepository, AbstractRepository() {
override val logger: Logger
get() = Companion.logger
@ -35,9 +35,9 @@ class MediaRepositoryImpl : MediaRepository, AbstractRepository() {
Media.upsert {
it[id] = media.id.id
it[name] = media.name.name
it[url] = media.url.toString()
it[remoteUrl] = media.remoteUrl?.toString()
it[thumbnailUrl] = media.thumbnailUrl?.toString()
it[url] = media.url
it[remoteUrl] = media.remoteUrl
it[thumbnailUrl] = media.thumbnailUrl
it[type] = media.type.name
it[blurhash] = media.blurHash?.hash
it[mimeType] = media.mimeType.type + "/" + media.mimeType.subtype
@ -51,12 +51,13 @@ class MediaRepositoryImpl : MediaRepository, AbstractRepository() {
return query {
return@query Media
.selectAll().where { Media.id eq id.id }
.limit(1)
.singleOrNull()
?.toMedia()
}
}
override suspend fun findByIds(ids: List<MediaId>): List<dev.usbharu.hideout.core.domain.model.media.Media> {
override suspend fun findByIdIn(ids: List<MediaId>): List<dev.usbharu.hideout.core.domain.model.media.Media> {
return query {
return@query Media
.selectAll()
@ -72,7 +73,7 @@ class MediaRepositoryImpl : MediaRepository, AbstractRepository() {
}
companion object {
private val logger = LoggerFactory.getLogger(MediaRepositoryImpl::class.java)
private val logger = LoggerFactory.getLogger(ExposedMediaRepository::class.java)
}
}
@ -82,9 +83,9 @@ fun ResultRow.toMedia(): EntityMedia {
return EntityMedia(
id = MediaId(this[Media.id]),
name = MediaName(this[Media.name]),
url = URI.create(this[Media.url]),
remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) },
thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) },
url = this[Media.url],
remoteUrl = this[Media.remoteUrl],
thumbnailUrl = this[Media.thumbnailUrl],
type = fileType,
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
@ -99,9 +100,9 @@ fun ResultRow.toMediaOrNull(): EntityMedia? {
return EntityMedia(
id = MediaId(this.getOrNull(Media.id) ?: return null),
name = MediaName(this.getOrNull(Media.name) ?: return null),
url = URI.create(this.getOrNull(Media.url) ?: return null),
remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) },
thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) },
url = this.getOrNull(Media.url) ?: return null,
remoteUrl = this[Media.remoteUrl],
thumbnailUrl = this[Media.thumbnailUrl],
type = FileType.valueOf(this[Media.type]),
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
@ -113,9 +114,9 @@ fun ResultRow.toMediaOrNull(): EntityMedia? {
object Media : Table("media") {
val id = long("id")
val name = varchar("name", 255)
val url = varchar("url", 255).uniqueIndex()
val remoteUrl = varchar("remote_url", 255).uniqueIndex().nullable()
val thumbnailUrl = varchar("thumbnail_url", 255).uniqueIndex().nullable()
val url = uri("url", 255).uniqueIndex()
val remoteUrl = uri("remote_url", 255).uniqueIndex().nullable()
val thumbnailUrl = uri("thumbnail_url", 255).uniqueIndex().nullable()
val type = varchar("type", 100)
val blurhash = varchar("blurhash", 255).nullable()
val mimeType = varchar("mime_type", 255)

View File

@ -163,6 +163,8 @@ class ExposedPostRepository(
override suspend fun findById(id: PostId): Post? = query {
Posts
.leftJoin(PostsMedia)
.leftJoin(PostsEmojis)
.leftJoin(PostsVisibleActors)
.selectAll()
.where {
Posts.id eq id.id
@ -174,6 +176,9 @@ class ExposedPostRepository(
override suspend fun findAllById(ids: List<PostId>): List<Post> {
return query {
Posts
.leftJoin(PostsMedia)
.leftJoin(PostsEmojis)
.leftJoin(PostsVisibleActors)
.selectAll()
.where {
Posts.id inList ids.map { it.id }
@ -182,21 +187,43 @@ class ExposedPostRepository(
}
}
override suspend fun findByActorId(id: ActorId, page: Page?): PaginationList<Post, PostId> = PaginationList(
query {
Posts
override suspend fun findByActorId(id: ActorId, page: Page?): PaginationList<Post, PostId> {
val postList = query {
val query = Posts
.selectAll()
.where {
actorId eq actorId
actorId eq id.id
}
.let(postQueryMapper::map)
},
null,
null
page(page, query)
query.let(postQueryMapper::map)
}
val posts = if (page?.minId != null) {
postList.reversed()
} else {
postList
}
return PaginationList(
posts,
posts.lastOrNull()?.id,
posts.firstOrNull()?.id
)
}
override suspend fun delete(post: Post) {
query {
PostsMedia.deleteWhere {
postId eq post.id.id
}
PostsEmojis.deleteWhere {
postId eq post.id.id
}
PostsVisibleActors.deleteWhere {
postId eq post.id.id
}
Posts.deleteWhere {
id eq post.id.id
}
@ -218,17 +245,7 @@ class ExposedPostRepository(
Posts.actorId eq actorId.id and (visibility inList visibilityList.map { it.name })
}
if (of?.minId != null) {
query.orderBy(Posts.createdAt, SortOrder.ASC)
of.minId?.let { query.andWhere { Posts.id greater it } }
of.maxId?.let { query.andWhere { Posts.id less it } }
} else {
query.orderBy(Posts.createdAt, SortOrder.DESC)
of?.sinceId?.let { query.andWhere { Posts.id greater it } }
of?.maxId?.let { query.andWhere { Posts.id less it } }
}
of?.limit?.let { query.limit(it) }
page(of, query)
query.let(postQueryMapper::map)
}
@ -246,6 +263,23 @@ class ExposedPostRepository(
)
}
private fun page(
page: Page?,
query: Query
) {
if (page?.minId != null) {
query.orderBy(createdAt, SortOrder.ASC)
page.minId!!.let { query.andWhere { id greater it } }
page.maxId?.let { query.andWhere { id less it } }
} else {
query.orderBy(createdAt, SortOrder.DESC)
page?.sinceId?.let { query.andWhere { id greater it } }
page?.maxId?.let { query.andWhere { id less it } }
}
page?.limit?.let { query.limit(it) }
}
companion object {
private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java)
}

View File

@ -17,7 +17,6 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.relationship.FindRelationshipOption
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
@ -69,7 +68,7 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve
override suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): Relationship? = query {
Relationships.selectAll().where {
Relationships.actorId eq actorId.id and (Relationships.targetActorId eq targetId.id)
}.singleOrNull()?.toRelationships()
}.limit(1).singleOrNull()?.toRelationships()
}
override suspend fun findByActorIdsAndTargetIdAndBlocking(
@ -78,7 +77,9 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve
blocking: Boolean
): List<Relationship> = query {
Relationships.selectAll().where {
Relationships.actorId inList actorIds.map { it.id } and (Relationships.targetActorId eq targetId.id)
Relationships.actorId inList actorIds.map {
it.id
} and (Relationships.targetActorId eq targetId.id) and (Relationships.blocking eq blocking)
}.map { it.toRelationships() }
}
@ -88,49 +89,19 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve
following: Boolean
): List<Relationship> = query {
Relationships.selectAll().where {
Relationships.actorId eq actorId.id and (Relationships.targetActorId inList targetIds.map { it.id })
Relationships.actorId eq actorId.id and (
Relationships.targetActorId inList targetIds.map {
it.id
}
) and (Relationships.following eq following)
}.map { it.toRelationships() }
}
override suspend fun findByTargetId(
targetId: ActorId,
option: FindRelationshipOption?,
inverseOption: FindRelationshipOption?
): List<Relationship> {
val query1 = Relationships.selectAll().where { Relationships.actorId eq targetId.id }
inverseOption.apply(query1)
// todo 逆のほうがいいかも
val query = query1.alias("INV").selectAll().where {
Relationships.targetActorId eq targetId.id
}
option.apply(query)
return query.map(ResultRow::toRelationships)
}
companion object {
private val logger = LoggerFactory.getLogger(ExposedRelationshipRepository::class.java)
}
}
fun FindRelationshipOption?.apply(query: Query) {
if (this?.follow != null) {
query.andWhere { Relationships.following eq this@apply.follow }
}
if (this?.mute != null) {
query.andWhere { Relationships.muting eq this@apply.mute }
}
if (this?.block != null) {
query.andWhere { Relationships.blocking eq this@apply.block }
}
if (this?.followRequest != null) {
query.andWhere { Relationships.followRequesting eq this@apply.followRequest }
}
if (this?.muteFollowRequest != null) {
query.andWhere { Relationships.mutingFollowRequest eq this@apply.muteFollowRequest }
}
}
fun ResultRow.toRelationships(): Relationship = Relationship(
actorId = ActorId(this[Relationships.actorId]),
targetActorId = ActorId(this[Relationships.targetActorId]),

View File

@ -54,7 +54,7 @@ class ExposedTimelineRepository(override val domainEventPublisher: DomainEventPu
override suspend fun findById(id: TimelineId): Timeline? {
return query {
Timelines.selectAll().where { Timelines.id eq id.value }.firstOrNull()?.toTimeline()
Timelines.selectAll().where { Timelines.id eq id.value }.limit(1).firstOrNull()?.toTimeline()
}
}

View File

@ -32,15 +32,14 @@ import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class UserDetailRepositoryImpl(override val domainEventPublisher: DomainEventPublisher) :
class ExposedUserDetailRepository(override val domainEventPublisher: DomainEventPublisher) :
UserDetailRepository,
AbstractRepository(),
DomainEventPublishableRepository<UserDetail> {
override val logger: Logger
get() = Companion.logger
override suspend fun save(userDetail: UserDetail): UserDetail {
val userDetail1 = query {
override suspend fun save(userDetail: UserDetail): UserDetail = query {
UserDetails.upsert {
it[id] = userDetail.id.id
it[actorId] = userDetail.actorId.id
@ -56,9 +55,6 @@ class UserDetailRepositoryImpl(override val domainEventPublisher: DomainEventPub
userDetail
}
return userDetail1
}
override suspend fun delete(userDetail: UserDetail) {
query {
UserDetails.deleteWhere { id eq userDetail.id.id }
@ -71,6 +67,7 @@ class UserDetailRepositoryImpl(override val domainEventPublisher: DomainEventPub
override suspend fun findByActorId(actorId: Long): UserDetail? = query {
return@query UserDetails
.selectAll().where { UserDetails.actorId eq actorId }
.limit(1)
.singleOrNull()
?.let {
userDetail(it)
@ -80,6 +77,7 @@ class UserDetailRepositoryImpl(override val domainEventPublisher: DomainEventPub
override suspend fun findById(id: UserDetailId): UserDetail? = query {
UserDetails
.selectAll().where { UserDetails.id eq id.id }
.limit(1)
.singleOrNull()
?.let {
userDetail(it)
@ -107,7 +105,7 @@ class UserDetailRepositoryImpl(override val domainEventPublisher: DomainEventPub
)
companion object {
private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java)
private val logger = LoggerFactory.getLogger(ExposedUserDetailRepository::class.java)
}
}

View File

@ -158,7 +158,7 @@ open class DefaultTimelineStore(
actorRepository.findAllById(actorIds).associateBy { it.id }
override suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media> =
mediaRepository.findByIds(mediaIds).associateBy { it.id }
mediaRepository.findByIdIn(mediaIds).associateBy { it.id }
override suspend fun getReactions(postIds: List<PostId>): Map<PostId, List<Reactions>> =
reactionsQueryService.findAllByPostIdIn(postIds).groupBy { PostId(it.postId) }

View File

@ -34,7 +34,7 @@ class ActorsTest {
val actor = TestActorFactory.create(publicKey = ActorPublicKey(""))
val actorIds = setOf(ActorId(100), ActorId(200))
actor.alsoKnownAs = actorIds
actor.setAlsoKnownAs(actorIds)
assertEquals(actorIds, actor.alsoKnownAs)
}
@ -44,7 +44,7 @@ class ActorsTest {
val actor = TestActorFactory.create(publicKey = ActorPublicKey(""))
actor.moveTo = ActorId(100)
actor.setMoveTo(ActorId(100))
assertContainsEvent(actor, ActorEvent.MOVE.eventName)
}
@ -54,7 +54,7 @@ class ActorsTest {
val actor = TestActorFactory.create(publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> {
actor.alsoKnownAs = setOf(actor.id)
actor.setAlsoKnownAs(setOf(actor.id))
}
}
@ -63,7 +63,7 @@ class ActorsTest {
val actor = TestActorFactory.create(publicKey = ActorPublicKey(""))
assertThrows<IllegalArgumentException> {
actor.moveTo = actor.id
actor.setMoveTo(actor.id)
}
}
@ -71,7 +71,7 @@ class ActorsTest {
fun descriptionが更新されたときupdateイベントが発生する() {
val actor = TestActorFactory.create(publicKey = ActorPublicKey(""))
actor.description = ActorDescription("hoge fuga")
actor.setDescription(ActorDescription("hoge fuga"))
assertContainsEvent(actor, ActorEvent.UPDATE.eventName)
}
@ -80,7 +80,7 @@ class ActorsTest {
fun screenNameが更新されたときupdateイベントが発生する() {
val actor = TestActorFactory.create(publicKey = ActorPublicKey(""))
actor.screenName = ActorScreenName("fuga hoge")
actor.setScreenName(ActorScreenName("fuga hoge"))
assertContainsEvent(actor, ActorEvent.UPDATE.eventName)
}

View File

@ -2,6 +2,7 @@ package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
import kotlinx.coroutines.runBlocking
@ -32,12 +33,14 @@ object TestActorFactory {
followingCount: Int = 0,
postCount: Int = 0,
lastPostDate: Instant? = null,
lastUpdateAt: Instant = createdAt,
suspend: Boolean = false,
alsoKnownAs: Set<ActorId> = emptySet(),
moveTo: Long? = null,
emojiIds: Set<CustomEmojiId> = emptySet(),
deleted: Boolean = false,
roles: Set<Role> = emptySet(),
icon: Long? = null,
banner: Long? = null,
): Actor {
return runBlocking {
Actor(
@ -61,13 +64,14 @@ object TestActorFactory {
followingCount = ActorRelationshipCount(followingCount),
postsCount = ActorPostsCount(postCount),
lastPostAt = lastPostDate,
lastUpdateAt = lastUpdateAt,
suspend = suspend,
alsoKnownAs = alsoKnownAs,
moveTo = moveTo?.let { ActorId(it) },
emojiIds = emojiIds,
deleted = deleted,
icon = null,
banner = null,
icon = icon?.let { MediaId(it) },
banner = banner?.let { MediaId(it) },
)
}

View File

@ -1,6 +1,7 @@
package dev.usbharu.hideout.core.domain.model.post
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
@ -29,13 +30,14 @@ object TestPostFactory {
visibleActors: List<Long> = emptyList(),
hide: Boolean = false,
moveTo: Long? = null,
emojiIds: List<Long> = emptyList(),
): Post {
return Post(
PostId(id),
ActorId(actorId),
instanceId = InstanceId(instanceId),
overview = overview?.let { PostOverview(it) },
content = PostContent(content, content, emptyList()),
content = PostContent(content, content, emojiIds.map { CustomEmojiId(it) }),
createdAt = createdAt,
visibility = visibility,
url = url,

View File

@ -0,0 +1,289 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup.Operations
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import kotlinx.coroutines.test.runTest
import org.assertj.db.api.Assertions.assertThat
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import utils.AbstractRepositoryTest
import utils.columns
import utils.disableReferenceIntegrityConstraints
import utils.isEqualTo
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ExtendWith(MockitoExtension::class)
class ExposedActorInstanceRelationshipRepositoryTest : AbstractRepositoryTest(ActorInstanceRelationships) {
@InjectMocks
lateinit var repository: ExposedActorInstanceRelationshipRepository
@Mock
lateinit var domainEventPublisher: DomainEventPublisher
@Test
fun save_idが同じレコードがない場合はinsert() = runTest {
dbSetup(to = dataSource) {
execute(Operations.sql("SET REFERENTIAL_INTEGRITY FALSE"))
insertInto(Instance.tableName) {
columns(
Instance.columns
)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
insertInto("public.actors") {
columns(
Actors.columns
)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
}.launch()
val actorInstanceRelationship = ActorInstanceRelationship(
actorId = ActorId(1), instanceId = InstanceId(1), blocking = false, muting = false, doNotSendPrivate = false
)
repository.save(actorInstanceRelationship)
dbSetup(to = dataSource) {
execute(Operations.sql("SET REFERENTIAL_INTEGRITY TRUE"))
}
assertThat(assertTable).row(0).isEqualTo(ActorInstanceRelationships.actorId, 1)
.isEqualTo(ActorInstanceRelationships.actorId, 1).isEqualTo(ActorInstanceRelationships.blocking, false)
.isEqualTo(ActorInstanceRelationships.muting, false)
.isEqualTo(ActorInstanceRelationships.doNotSendPrivate, false)
}
@Test
fun save_idが同じレコードがある場合はupdate() = runTest {
dbSetup(to = dataSource) {
execute(Operations.sql("SET REFERENTIAL_INTEGRITY FALSE"))
insertInto(Instance.tableName) {
columns(
Instance.columns
)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
insertInto("public.actors") {
columns(
Actors.columns
)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(ActorInstanceRelationships.tableName) {
columns(ActorInstanceRelationships.columns)
values(1, 1, true, true, true)
}
}.launch()
val actorInstanceRelationship = ActorInstanceRelationship(
actorId = ActorId(1), instanceId = InstanceId(1), blocking = false, muting = false, doNotSendPrivate = false
)
repository.save(actorInstanceRelationship)
dbSetup(to = dataSource) {
execute(Operations.sql("SET REFERENTIAL_INTEGRITY TRUE"))
}
assertThat(assertTable).row(0).isEqualTo(ActorInstanceRelationships.actorId, 1)
.isEqualTo(ActorInstanceRelationships.actorId, 1).isEqualTo(ActorInstanceRelationships.blocking, false)
.isEqualTo(ActorInstanceRelationships.muting, false)
.isEqualTo(ActorInstanceRelationships.doNotSendPrivate, false)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(Operations.sql("SET REFERENTIAL_INTEGRITY FALSE"))
insertInto(ActorInstanceRelationships.tableName) {
columns(ActorInstanceRelationships.columns)
values(1, 1, true, true, true)
}
}.launch()
val actorInstanceRelationship = ActorInstanceRelationship(
actorId = ActorId(1), instanceId = InstanceId(1), blocking = false, muting = false, doNotSendPrivate = false
)
change.setStartPointNow()
repository.delete(actorInstanceRelationship)
change.setEndPointNow()
assertThat(change)
.changeOfDeletionOnTable(ActorInstanceRelationships.tableName)
.rowAtStartPoint()
.value(ActorInstanceRelationships.instanceId.name).isEqualTo(1)
.value(ActorInstanceRelationships.actorId.name).isEqualTo(1)
}
@Test
fun findByActorIdAndInstanceId_指定したActorIdとInstanceIdで存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(ActorInstanceRelationships.tableName) {
columns(ActorInstanceRelationships.columns)
values(1, 1, true, true, true)
}
}.launch()
val expected = ActorInstanceRelationship(
actorId = ActorId(1), instanceId = InstanceId(1), blocking = true, muting = true, doNotSendPrivate = true
)
val actual = repository.findByActorIdAndInstanceId(ActorId(1), InstanceId(1))
assertNotNull(actual)
assertEquals(expected, actual)
assertEquals(expected.actorId, actual.actorId)
assertEquals(expected.instanceId, actual.instanceId)
assertEquals(expected.blocking, actual.blocking)
assertEquals(expected.muting, actual.muting)
assertEquals(expected.doNotSendPrivate, actual.doNotSendPrivate)
}
@Test
fun findByActorIdAndInstanceId_指定したActorIdとInstanceIdで存在しないとnull() = runTest {
assertNull(repository.findByActorIdAndInstanceId(ActorId(1), InstanceId(1)))
}
@Test
fun save_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
}.launch()
val actorInstanceRelationship = ActorInstanceRelationship(
actorId = ActorId(1), instanceId = InstanceId(1), blocking = false, muting = false, doNotSendPrivate = false
)
actorInstanceRelationship.block()
repository.save(actorInstanceRelationship)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
@Test
fun delete_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(ActorInstanceRelationships.tableName) {
columns(ActorInstanceRelationships.columns)
values(1, 1, true, true, true)
}
}.launch()
val actorInstanceRelationship = ActorInstanceRelationship(
actorId = ActorId(1), instanceId = InstanceId(1), blocking = false, muting = false, doNotSendPrivate = false
)
actorInstanceRelationship.block()
repository.delete(actorInstanceRelationship)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
}

View File

@ -0,0 +1,558 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.*
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import dev.usbharu.hideout.core.infrastructure.exposed.ActorQueryMapper
import dev.usbharu.hideout.core.infrastructure.exposed.ActorResultRowMapper
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.db.api.Assertions.assertThat
import org.assertj.db.type.Changes
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import utils.*
import java.net.URI
import java.sql.Timestamp
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ExtendWith(MockitoExtension::class)
class ExposedActorRepositoryTest : AbstractRepositoryTest(Actors) {
@InjectMocks
lateinit var repository: ExposedActorRepository
@Mock
lateinit var domainEventPublisher: DomainEventPublisher
@Spy
val actorQueryMapper = ActorQueryMapper(ActorResultRowMapper())
@Test
fun save_idが同じレコードがない場合はinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Instance.tableName) {
columns(Instance.columns)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val actor = TestActorFactory.create()
repository.save(actor)
assertThat(assertTable)
.row(1)
.isEqualTo(Actors.id, actor.id.id)
.isEqualTo(Actors.name, actor.name.name)
.isEqualTo(Actors.domain, actor.domain.domain)
.isEqualTo(Actors.screenName, actor.screenName.screenName)
.isEqualTo(Actors.description, actor.description.description)
.value(Actors.url).isEqualTo(actor.url.toString())
.value(Actors.inbox).isEqualTo(actor.inbox.toString())
.value(Actors.outbox).isEqualTo(actor.outbox.toString())
.isEqualTo(Actors.publicKey, actor.publicKey.publicKey)
.isEqualTo(Actors.privateKey, actor.privateKey?.privateKey)
}
@Test
fun save_idが同じレコードがある場合はupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Instance.tableName) {
columns(Instance.columns)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(ActorsAlsoKnownAs.tableName) {
columns(ActorsAlsoKnownAs.columns)
values(1, 2)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val actor = TestActorFactory.create(1, alsoKnownAs = setOf(ActorId(1)))
repository.save(actor)
assertThat(assertTable)
.row(1)
.isEqualTo(Actors.id, actor.id.id)
.isEqualTo(Actors.name, actor.name.name)
.isEqualTo(Actors.domain, actor.domain.domain)
.isEqualTo(Actors.screenName, actor.screenName.screenName)
.isEqualTo(Actors.description, actor.description.description)
.value(Actors.url).isEqualTo(actor.url.toString())
.value(Actors.inbox).isEqualTo(actor.inbox.toString())
.value(Actors.outbox).isEqualTo(actor.outbox.toString())
.isEqualTo(Actors.publicKey, actor.publicKey.publicKey)
.isEqualTo(Actors.privateKey, actor.privateKey?.privateKey)
assertThat(getTable(ActorsAlsoKnownAs.tableName))
.row(0)
.isEqualTo(ActorsAlsoKnownAs.actorId, 1)
.isEqualTo(ActorsAlsoKnownAs.alsoKnownAs, 1)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(ActorsAlsoKnownAs.tableName) {
columns(ActorsAlsoKnownAs.columns)
values(1, 1)
}
}.launch()
val actor = TestActorFactory.create(1, alsoKnownAs = setOf(ActorId(1)))
val changes = Changes(dataSource)
changes.withSuspend {
repository.delete(actor)
}
assertThat(changes)
.changeOfDeletionOnTable(Actors.tableName)
.rowAtStartPoint()
.value(Actors.id.name).isEqualTo(actor.id.id)
.changeOfDeletionOnTable(ActorsAlsoKnownAs.tableName)
.rowAtStartPoint()
.value(ActorsAlsoKnownAs.alsoKnownAs.name)
.isEqualTo(actor.alsoKnownAs.first().id)
}
@Test
fun findById_指定されたIdがあれば返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
}.launch()
val expect = TestActorFactory.create(
id = 1,
actorName = "b",
domain = "test-hideout-dev.usbharu.dev",
actorScreenName = "b",
description = "",
inbox = URI.create("https://test-hideout-dev.usbharu.dev/users/b/inbox"),
outbox = URI.create("https://test-hideout-dev.usbharu.dev/users/b/outbox"),
uri = URI.create("https://test-hideout-dev.usbharu.dev/users/b"),
publicKey = ActorPublicKey("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----"),
privateKey = ActorPrivateKey(
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
),
createdAt = Timestamp.valueOf("2024-09-09 17:12:03.941339").toInstant(),
keyId = "https://test-hideout-dev.usbharu.dev/users/b#main-key",
followingEndpoint = URI.create("https://test-hideout-dev.usbharu.dev/users/b/following"),
followersEndpoint = URI.create("https://test-hideout-dev.usbharu.dev/users/b/followers"),
instanceId = 1,
locked = false,
followersCount = 0,
followingCount = 0,
postCount = 0,
lastPostDate = null,
lastUpdateAt = Timestamp.valueOf("2024-09-09 17:12:03.941339").toInstant(),
suspend = false,
alsoKnownAs = emptySet(),
moveTo = null,
emojiIds = emptySet(),
deleted = false,
banner = null,
icon = null
)
val actual = repository.findById(ActorId(1))
assertEquals(actual, expect)
}
private fun assertEquals(
actual: Actor?,
expect: Actor
) {
assertNotNull(actual)
kotlin.test.assertEquals(expect, actual)
assertEquals(expect.id, actual.id)
assertEquals(expect.name, actual.name)
assertEquals(expect.domain, actual.domain)
assertEquals(expect.screenName, actual.screenName)
assertEquals(expect.description, actual.description)
assertEquals(expect.inbox, actual.inbox)
assertEquals(expect.outbox, actual.outbox)
assertEquals(expect.url, actual.url)
assertEquals(expect.publicKey, actual.publicKey)
assertEquals(expect.privateKey, actual.privateKey)
assertEquals(expect.createdAt, actual.createdAt)
assertEquals(expect.keyId, actual.keyId)
assertEquals(expect.followingEndpoint, actual.followingEndpoint)
assertEquals(expect.followersEndpoint, actual.followersEndpoint)
assertEquals(expect.postsCount, actual.postsCount)
assertEquals(expect.lastPostAt, actual.lastPostAt)
assertEquals(expect.lastUpdateAt, actual.lastUpdateAt)
assertEquals(expect.suspend, actual.suspend)
assertEquals(expect.moveTo, actual.moveTo)
assertEquals(expect.emojis, actual.emojis)
assertEquals(expect.deleted, actual.deleted)
assertEquals(expect.banner, actual.banner)
assertEquals(expect.icon, actual.icon)
assertEquals(expect.banner, actual.banner)
}
@Test
fun findById_指定されたIdがなければnull() = runTest {
assertNull(repository.findById(ActorId(1)))
}
@Test
fun findByNameAndDomain_指定されたNameとDomainがあれば返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
}.launch()
val expect = TestActorFactory.create(
id = 1,
actorName = "b",
domain = "test-hideout-dev.usbharu.dev",
actorScreenName = "b",
description = "",
inbox = URI.create("https://test-hideout-dev.usbharu.dev/users/b/inbox"),
outbox = URI.create("https://test-hideout-dev.usbharu.dev/users/b/outbox"),
uri = URI.create("https://test-hideout-dev.usbharu.dev/users/b"),
publicKey = ActorPublicKey("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----"),
privateKey = ActorPrivateKey(
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
),
createdAt = Timestamp.valueOf("2024-09-09 17:12:03.941339").toInstant(),
keyId = "https://test-hideout-dev.usbharu.dev/users/b#main-key",
followingEndpoint = URI.create("https://test-hideout-dev.usbharu.dev/users/b/following"),
followersEndpoint = URI.create("https://test-hideout-dev.usbharu.dev/users/b/followers"),
instanceId = 1,
locked = false,
followersCount = 0,
followingCount = 0,
postCount = 0,
lastPostDate = null,
lastUpdateAt = Timestamp.valueOf("2024-09-09 17:12:03.941339").toInstant(),
suspend = false,
alsoKnownAs = emptySet(),
moveTo = null,
emojiIds = emptySet(),
deleted = false,
banner = null,
icon = null
)
val actual = repository.findByNameAndDomain("b", "test-hideout-dev.usbharu.dev")
assertEquals(actual, expect)
}
@Test
fun findByNameAndDomain_指定されたNameとDomainがなければnull() = runTest {
assertNull(repository.findByNameAndDomain("a", "b"))
}
@Test
fun findAllById_指定されたIdすべて返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
values(
2,
"a",
"test-hideout-dev.usbharu.dev",
"a",
"",
"https://test-hideout-dev.usbharu.dev/users/a/inbox",
"https://test-hideout-dev.usbharu.dev/users/a/outbox",
"https://test-hideout-dev.usbharu.dev/users/a",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/a#main-key",
"https://test-hideout-dev.usbharu.dev/users/a/following",
"https://test-hideout-dev.usbharu.dev/users/a/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
}.launch()
val findAllById = repository.findAllById(listOf(ActorId(1), ActorId(2)))
assertThat(findAllById)
.hasSize(2)
}
@Test
fun save_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
}.launch()
val actor = TestActorFactory.create()
actor.checkUpdate()
repository.save(actor)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
@Test
fun delete_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(ActorsAlsoKnownAs.tableName) {
columns(ActorsAlsoKnownAs.columns)
values(1, 1)
}
}.launch()
val actor = TestActorFactory.create(1, alsoKnownAs = setOf(ActorId(1)))
actor.delete()
repository.delete(actor)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
}

View File

@ -0,0 +1,70 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.application.Application
import dev.usbharu.hideout.core.domain.model.application.ApplicationId
import dev.usbharu.hideout.core.domain.model.application.ApplicationName
import kotlinx.coroutines.test.runTest
import org.assertj.db.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import utils.AbstractRepositoryTest
import utils.columns
import utils.isEqualTo
import utils.withSuspend
class ExposedApplicationRepositoryTest : AbstractRepositoryTest(Applications) {
@Test
fun save_idが同じレコードが存在しないとinsert() = runTest {
val application = Application(ApplicationId(1), ApplicationName("test-application"))
ExposedApplicationRepository().save(application)
assertThat(assertTable)
.row(0)
.isEqualTo(Applications.id, application.applicationId.id)
.isEqualTo(Applications.name, application.name.name)
}
@Test
fun save_idが同じレコードが存在したらupdate() = runTest {
dbSetup(to = dataSource) {
insertInto(Applications.tableName) {
columns(Applications.columns)
values(1, "application-test")
}
}.launch()
val application = Application(ApplicationId(1), ApplicationName("test-application"))
ExposedApplicationRepository().save(application)
assertThat(assertTable)
.row(0)
.isEqualTo(Applications.id, application.applicationId.id)
.isEqualTo(Applications.name, application.name.name)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
insertInto(Applications.tableName) {
columns(Applications.columns)
values(1, "test-application")
}
}.launch()
val application = Application(ApplicationId(1), ApplicationName("test-application"))
change.withSuspend {
ExposedApplicationRepository().delete(application)
}
assertThat(change)
.changeOfDeletionOnTable(Applications.tableName)
.rowAtStartPoint()
.value(Applications.id.name).isEqualTo(application.applicationId.id)
.value(Applications.name.name).isEqualTo(application.name.name)
}
}

View File

@ -0,0 +1,324 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
import kotlinx.coroutines.test.runTest
import org.assertj.db.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import utils.*
import java.net.URI
import java.sql.Timestamp
import java.time.Instant
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
class ExposedCustomEmojiRepositoryTest : AbstractRepositoryTest(CustomEmojis) {
@Test
fun save_idが同じレコードが存在しないとinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Instance.tableName) {
columns(Instance.columns)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val customEmoji = CustomEmoji(
CustomEmojiId(1),
"name",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com"),
null,
Instant.parse("2020-01-01T00:00:00Z")
)
ExposedCustomEmojiRepository().save(customEmoji)
assertThat(assertTable).row(0).isEqualTo(CustomEmojis.id, customEmoji.id.emojiId)
.isEqualTo(CustomEmojis.name, customEmoji.name).isEqualTo(CustomEmojis.domain, customEmoji.domain.domain)
.isEqualTo(CustomEmojis.instanceId, customEmoji.instanceId.instanceId)
.isEqualTo(CustomEmojis.url, customEmoji.url.toString())
.isEqualTo(CustomEmojis.category, customEmoji.category).value(CustomEmojis.createdAt.name)
.isEqualTo(Timestamp.from(customEmoji.createdAt))
}
@Test
fun save_idが同じレコードが存在したらupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Instance.tableName) {
columns(Instance.columns)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(
1,
"emoji",
"example.com",
1,
"https://example.com",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val customEmoji = CustomEmoji(
CustomEmojiId(1),
"name",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com"),
null,
Instant.parse("2020-01-01T00:00:00Z")
)
ExposedCustomEmojiRepository().save(customEmoji)
assertThat(assertTable).row(0).isEqualTo(CustomEmojis.id, customEmoji.id.emojiId)
.isEqualTo(CustomEmojis.name, customEmoji.name).isEqualTo(CustomEmojis.domain, customEmoji.domain.domain)
.isEqualTo(CustomEmojis.instanceId, customEmoji.instanceId.instanceId)
.isEqualTo(CustomEmojis.url, customEmoji.url.toString())
.isEqualTo(CustomEmojis.category, customEmoji.category).value(CustomEmojis.createdAt.name)
.isEqualTo(Timestamp.from(customEmoji.createdAt))
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(
1,
"emoji",
"example.com",
1,
"https://example.com",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val customEmoji = CustomEmoji(
CustomEmojiId(1),
"name",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com"),
null,
Instant.parse("2020-01-01T00:00:00Z")
)
change.withSuspend {
ExposedCustomEmojiRepository().delete(customEmoji)
}
assertThat(change).changeOfDeletionOnTable(CustomEmojis.tableName).rowAtStartPoint().value(CustomEmojis.id.name)
.isEqualTo(customEmoji.id.emojiId)
}
@Test
fun findById_指定したIdで存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(
1,
"emoji",
"example.com",
1,
"https://example.com",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val customEmoji = CustomEmoji(
CustomEmojiId(1),
"emoji",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com"),
null,
Instant.parse("2020-01-01T00:00:00Z")
)
val actual = ExposedCustomEmojiRepository().findById(1)
assertEquals(customEmoji, actual)
assertNotNull(actual)
assertEquals(customEmoji.id, actual.id)
assertEquals(customEmoji.createdAt, actual.createdAt)
assertEquals(customEmoji.url, actual.url)
assertEquals(customEmoji.category, actual.category)
assertEquals(customEmoji.instanceId, actual.instanceId)
assertEquals(customEmoji.domain, actual.domain)
assertEquals(customEmoji.name, actual.name)
}
@Test
fun findById_指定したIdで存在しないとnull() = runTest {
assertNull(ExposedCustomEmojiRepository().findById(1))
}
@Test
fun findByNamesAndDomain_指定した条件全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(
1,
"emoji",
"example.com",
1,
"https://example.com/1",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
values(
2,
"emoji2",
"example.com",
1,
"https://example.com/2",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
values(
3,
"emoji3",
"example.com",
1,
"https://example.com/3",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val expected = listOf(
CustomEmoji(
CustomEmojiId(1),
"emoji",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com/1"),
null,
Instant.parse("2020-01-01T00:00:00Z")
), CustomEmoji(
CustomEmojiId(2),
"emoji2",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com/2"),
null,
Instant.parse("2020-01-01T00:00:00Z")
)
)
val actual = ExposedCustomEmojiRepository().findByNamesAndDomain(listOf("emoji", "emoji2"), "example.com")
assertContentEquals(expected, actual)
}
@Test
fun findByIds_指定された条件全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(
1,
"emoji",
"example.com",
1,
"https://example.com/1",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
values(
2,
"emoji2",
"example.com",
1,
"https://example.com/2",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
values(
3,
"emoji3",
"example.com",
1,
"https://example.com/3",
null,
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val expected = listOf(
CustomEmoji(
CustomEmojiId(1),
"emoji",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com/1"),
null,
Instant.parse("2020-01-01T00:00:00Z")
), CustomEmoji(
CustomEmojiId(3),
"emoji3",
Domain("example.com"),
InstanceId(1),
URI.create("https://example.com/3"),
null,
Instant.parse("2020-01-01T00:00:00Z")
)
)
val actual = ExposedCustomEmojiRepository().findByIds(listOf(1, 3))
assertContentEquals(expected, actual)
}
}

View File

@ -0,0 +1,280 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.filter.*
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.infrastructure.exposed.FilterQueryMapper
import dev.usbharu.hideout.core.infrastructure.exposed.FilterResultRowMapper
import kotlinx.coroutines.test.runTest
import org.assertj.db.api.Assertions.assertThat
import org.assertj.db.type.Changes
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import utils.*
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ExtendWith(MockitoExtension::class)
class ExposedFilterRepositoryTest : AbstractRepositoryTest(Filters) {
@InjectMocks
lateinit var repository: ExposedFilterRepository
@Spy
val filterQueryMapper: FilterQueryMapper = FilterQueryMapper(FilterResultRowMapper())
@Test
fun save_idが同じレコードが存在しないとinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 2, "VeeeeeeeeeeeeeeeeryStrongPassword", false, null, null)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val filter = Filter(
FilterId(1),
UserDetailId(1),
FilterName("filter"),
setOf(),
FilterAction.HIDE,
setOf(FilterKeyword(FilterKeywordId(1), FilterKeywordKeyword("keyword"), FilterMode.NONE))
)
repository.save(filter)
assertThat(assertTable).row(0).isEqualTo(Filters.id, filter.id.id)
.isEqualTo(Filters.userId, filter.userDetailId.id).isEqualTo(Filters.name, filter.name.name)
.isEqualTo(Filters.filterAction, filter.filterAction.name)
.isEqualTo(Filters.context, filter.filterContext.joinToString(",") { it.name })
assertThat(getTable(FilterKeywords.tableName)).row(0)
.isEqualTo(FilterKeywords.id, filter.filterKeywords.first().id.id)
.isEqualTo(FilterKeywords.filterId, filter.id.id)
.isEqualTo(FilterKeywords.keyword, filter.filterKeywords.first().keyword.keyword)
.isEqualTo(FilterKeywords.mode, filter.filterKeywords.first().mode.name)
}
@Test
fun save_idが同じレコードが存在したらupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 2, "VeeeeeeeeeeeeeeeeryStrongPassword", false, null, null)
}
insertInto(Filters.tableName) {
columns(Filters.columns)
values(1, 1, "name", "", "WARN")
}
insertInto(FilterKeywords.tableName) {
columns(FilterKeywords.columns)
values(1, 1, "aaaaaaaaaaaaaaaaa", "REGEX")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val filter = Filter(
FilterId(1),
UserDetailId(1),
FilterName("filter"),
setOf(),
FilterAction.HIDE,
setOf(FilterKeyword(FilterKeywordId(1), FilterKeywordKeyword("keyword"), FilterMode.NONE))
)
repository.save(filter)
assertThat(assertTable).row(0).isEqualTo(Filters.id, filter.id.id)
.isEqualTo(Filters.userId, filter.userDetailId.id).isEqualTo(Filters.name, filter.name.name)
.isEqualTo(Filters.filterAction, filter.filterAction.name)
.isEqualTo(Filters.context, filter.filterContext.joinToString(",") { it.name })
assertThat(getTable(FilterKeywords.tableName)).row(0)
.isEqualTo(FilterKeywords.id, filter.filterKeywords.first().id.id)
.isEqualTo(FilterKeywords.filterId, filter.id.id)
.isEqualTo(FilterKeywords.keyword, filter.filterKeywords.first().keyword.keyword)
.isEqualTo(FilterKeywords.mode, filter.filterKeywords.first().mode.name)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 2, "VeeeeeeeeeeeeeeeeryStrongPassword", false, null, null)
}
insertInto(Filters.tableName) {
columns(Filters.columns)
values(1, 1, "name", "", "WARN")
}
insertInto(FilterKeywords.tableName) {
columns(FilterKeywords.columns)
values(1, 1, "aaaaaaaaaaaaaaaaa", "REGEX")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val filter = Filter(
FilterId(1),
UserDetailId(1),
FilterName("filter"),
setOf(),
FilterAction.HIDE,
setOf(FilterKeyword(FilterKeywordId(1), FilterKeywordKeyword("keyword"), FilterMode.NONE))
)
val changes = Changes(dataSource)
changes.withSuspend {
repository.delete(filter)
}
assertThat(changes).changeOfDeletionOnTable(Filters.tableName).rowAtStartPoint().value(Filters.id.name)
.isEqualTo(filter.id.id).changeOfDeletionOnTable(FilterKeywords.tableName).rowAtStartPoint()
.value(FilterKeywords.id.name).isEqualTo(filter.filterKeywords.first().id.id)
}
@Test
fun findByFilterKeywordId_指定された条件で存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Filters.tableName) {
columns(Filters.columns)
values(1, 1, "name", "PUBLIC", "WARN")
}
insertInto(FilterKeywords.tableName) {
columns(FilterKeywords.columns)
values(1, 1, "keyword", "REGEX")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expected = Filter(
FilterId(1),
UserDetailId(1),
FilterName("name"),
setOf(FilterContext.PUBLIC),
FilterAction.WARN,
setOf(FilterKeyword(FilterKeywordId(1), FilterKeywordKeyword("keyword"), FilterMode.REGEX))
)
val actual = repository.findByFilterKeywordId(FilterKeywordId(1))
assertEquals(expected, actual)
}
private fun assertEquals(
expected: Filter, actual: Filter?
) {
kotlin.test.assertEquals(expected, actual)
assertNotNull(actual)
assertEquals(expected.id, actual.id)
assertEquals(expected.name, actual.name)
assertContentEquals(expected.filterContext, actual.filterContext.asIterable())
assertEquals(expected.userDetailId, actual.userDetailId)
assertEquals(expected.filterKeywords.size, actual.filterKeywords.size)
assertContentEquals(expected.filterKeywords, actual.filterKeywords.asIterable())
}
@Test
fun findByFilterKeywordId_指定された条件で存在しないとnull() = runTest {
assertNull(repository.findByFilterKeywordId(FilterKeywordId(1)))
}
@Test
fun findByFilterId_指定された条件で存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Filters.tableName) {
columns(Filters.columns)
values(1, 1, "name", "PUBLIC", "WARN")
}
insertInto(FilterKeywords.tableName) {
columns(FilterKeywords.columns)
values(1, 1, "keyword", "REGEX")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expected = Filter(
FilterId(1),
UserDetailId(1),
FilterName("name"),
setOf(FilterContext.PUBLIC),
FilterAction.WARN,
setOf(FilterKeyword(FilterKeywordId(1), FilterKeywordKeyword("keyword"), FilterMode.REGEX))
)
val actual = repository.findByFilterId(FilterId(1))
assertEquals(expected, actual)
}
@Test
fun findByFilterId_指定された条件で存在しないとnull() = runTest {
assertNull(repository.findByFilterId(FilterId(1)))
}
@Test
fun findByUserDetailId_指定された条件全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Filters.tableName) {
columns(Filters.columns)
values(1, 1, "name", "PUBLIC", "WARN")
values(2, 1, "name2", "PUBLIC", "WARN")
values(3, 1, "name3", "PUBLIC", "HIDE")
}
insertInto(FilterKeywords.tableName) {
columns(FilterKeywords.columns)
values(1, 1, "keyword", "REGEX")
values(2, 2, "keyword2", "REGEX")
values(3, 1, "keyword3", "REGEX")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expected = listOf(
Filter(
FilterId(1), UserDetailId(1), FilterName("name"), setOf(FilterContext.PUBLIC), FilterAction.WARN, setOf(
FilterKeyword(FilterKeywordId(1), FilterKeywordKeyword("keyword"), FilterMode.REGEX),
FilterKeyword(FilterKeywordId(3), FilterKeywordKeyword("keyword3"), FilterMode.REGEX)
)
), Filter(
FilterId(2),
UserDetailId(1),
FilterName("name2"),
setOf(FilterContext.PUBLIC),
FilterAction.WARN,
setOf(
FilterKeyword(FilterKeywordId(2), FilterKeywordKeyword("keyword2"), FilterMode.REGEX)
)
), Filter(
FilterId(3),
UserDetailId(1),
FilterName("name3"),
setOf(FilterContext.PUBLIC),
FilterAction.HIDE,
setOf(
)
)
)
val actual = repository.findByUserDetailId(UserDetailId(1))
assertContentEquals(expected, actual)
}
}

View File

@ -0,0 +1,275 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.instance.*
import dev.usbharu.hideout.core.domain.model.instance.Instance
import kotlinx.coroutines.test.runTest
import org.assertj.db.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import utils.AbstractRepositoryTest
import utils.columns
import utils.isEqualTo
import utils.value
import java.net.URI
import java.sql.Timestamp
import java.time.Instant
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Instance as InstanceTable
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ExposedInstanceRepositoryTest : AbstractRepositoryTest(InstanceTable) {
@Test
fun save_idが同じレコードがない場合はinsertされる() = runTest {
ExposedInstanceRepository().save(
Instance(
id = InstanceId(1),
name = InstanceName("test"),
description = InstanceDescription("id"),
url = URI.create("https://www.example.com"),
iconUrl = URI.create("https://www.example.com"),
sharedInbox = null,
software = InstanceSoftware(""),
version = InstanceVersion(""),
isBlocked = false,
isMuted = false,
moderationNote = InstanceModerationNote(""),
createdAt = Instant.parse("2020-01-01T00:00:00Z"),
)
)
val table = assertTable
assertThat(table).row(1).isEqualTo(InstanceTable.id, 1).isEqualTo(InstanceTable.name, "test")
.value(InstanceTable.url).isEqualTo("https://www.example.com")
.value(InstanceTable.iconUrl).isEqualTo("https://www.example.com")
.isEqualTo(InstanceTable.sharedInbox, null)
.isEqualTo(InstanceTable.software, "").isEqualTo(InstanceTable.version, "")
.isEqualTo(InstanceTable.isBlocked, false).isEqualTo(InstanceTable.isMuted, false)
.isEqualTo(InstanceTable.moderationNote, "").value(InstanceTable.createdAt)
.isEqualTo(Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")))
}
@Test
fun save_idが同じレコードがある場合はupdateされる() = runTest {
dbSetup(to = dataSource) {
insertInto(InstanceTable.tableName) {
columns(
InstanceTable.columns
)
values(
1,
"system",
"",
"https://example.com",
"",
null,
"",
"",
false,
false,
"",
"2024-09-10 16:59:50.160202"
)
}
}.launch()
ExposedInstanceRepository().save(
Instance(
id = InstanceId(1),
name = InstanceName("test"),
description = InstanceDescription("id"),
url = URI.create("https://www.example.com"),
iconUrl = URI.create("https://www.example.com"),
sharedInbox = null,
software = InstanceSoftware(""),
version = InstanceVersion(""),
isBlocked = false,
isMuted = false,
moderationNote = InstanceModerationNote(""),
createdAt = Instant.parse("2020-01-01T00:00:00Z"),
)
)
val table = assertTable
assertThat(table).row(1).isEqualTo(InstanceTable.id, 1).isEqualTo(InstanceTable.name, "test")
.value(InstanceTable.url).isEqualTo("https://www.example.com")
.value(InstanceTable.iconUrl).isEqualTo("https://www.example.com")
.isEqualTo(InstanceTable.sharedInbox, null)
.isEqualTo(InstanceTable.software, "").isEqualTo(InstanceTable.version, "")
.isEqualTo(InstanceTable.isBlocked, false).isEqualTo(InstanceTable.isMuted, false)
.isEqualTo(InstanceTable.moderationNote, "").value(InstanceTable.createdAt)
.isEqualTo(Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")))
}
@Test
fun findById_指定したidで存在したら返す() = runTest {
dbSetup(to = dataSource) {
insertInto(InstanceTable.tableName) {
columns(
InstanceTable.columns
)
values(
1,
"test",
"description",
"https://www.example.com",
"https://www.example.com",
null,
"",
"",
false,
false,
"",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val actual = ExposedInstanceRepository().findById(InstanceId(1))
val expected = Instance(
id = InstanceId(1),
name = InstanceName("test"),
description = InstanceDescription("description"),
url = URI.create("https://www.example.com"),
iconUrl = URI.create("https://www.example.com"),
sharedInbox = null,
software = InstanceSoftware(""),
version = InstanceVersion(""),
isBlocked = false,
isMuted = false,
moderationNote = InstanceModerationNote(""),
createdAt = Instant.parse("2020-01-01T00:00:00Z"),
)
assertEquals(expected, actual)
}
@Test
fun findById_指定したIDで存在しないとnull() = runTest {
assertNull(ExposedInstanceRepository().findById(InstanceId(1)))
}
@Test
fun findByUrl_指定したURLで存在したら返す() = runTest {
dbSetup(to = dataSource) {
insertInto(InstanceTable.tableName) {
columns(
InstanceTable.columns
)
values(
1,
"test",
"description",
"https://www.example.com",
"https://www.example.com",
null,
"",
"",
false,
false,
"",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val actual = ExposedInstanceRepository().findByUrl(URI.create("https://www.example.com"))
val expected = Instance(
id = InstanceId(1),
name = InstanceName("test"),
description = InstanceDescription("description"),
url = URI.create("https://www.example.com"),
iconUrl = URI.create("https://www.example.com"),
sharedInbox = null,
software = InstanceSoftware(""),
version = InstanceVersion(""),
isBlocked = false,
isMuted = false,
moderationNote = InstanceModerationNote(""),
createdAt = Instant.parse("2020-01-01T00:00:00Z"),
)
assertEquals(expected, actual)
}
@Test
fun findByUrl_指定したURLで存在しないとnull() = runTest {
assertNull(ExposedInstanceRepository().findByUrl(URI.create("https://www.example.com")))
}
@Test
fun delete_削除() = runTest {
dbSetup(to = dataSource) {
insertInto(InstanceTable.tableName) {
columns(
InstanceTable.columns
)
values(
1,
"test",
"description",
"https://www.example.com",
"https://www.example.com",
null,
"",
"",
false,
false,
"",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z"))
)
}
}.launch()
val instance = Instance(
id = InstanceId(1),
name = InstanceName("test"),
description = InstanceDescription("description"),
url = URI.create("https://www.example.com"),
iconUrl = URI.create("https://www.example.com"),
sharedInbox = null,
software = InstanceSoftware(""),
version = InstanceVersion(""),
isBlocked = false,
isMuted = false,
moderationNote = InstanceModerationNote(""),
createdAt = Instant.parse("2020-01-01T00:00:00Z"),
)
change.setStartPointNow()
ExposedInstanceRepository().delete(instance)
change.setEndPointNow()
assertThat(change)
.hasNumberOfChanges(1)
.changeOfDeletionOnTable(InstanceTable.tableName)
.rowAtStartPoint()
.value(InstanceTable.id.name).isEqualTo(1)
}
companion object {
fun assertEquals(expected: Instance, actual: Instance?) {
assertNotNull(actual)
kotlin.test.assertEquals(expected, actual)
assertEquals(expected.name, actual.name)
assertEquals(expected.description, actual.description)
assertEquals(expected.url, actual.url)
assertEquals(expected.iconUrl, actual.iconUrl)
assertEquals(expected.sharedInbox, actual.sharedInbox)
assertEquals(expected.software, actual.software)
assertEquals(expected.version, actual.version)
assertEquals(expected.isBlocked, actual.isBlocked)
assertEquals(expected.moderationNote, actual.moderationNote)
assertEquals(expected.createdAt, actual.createdAt)
}
}
}

View File

@ -0,0 +1,363 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.media.*
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.db.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import utils.*
import java.net.URI
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
class ExposedMediaRepositoryTest : AbstractRepositoryTest(Media) {
@Test
fun save_idが同じレコードが存在しないとinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val media = EntityMedia(
id = MediaId(1),
name = MediaName("name"),
url = URI.create("https://www.example.com"),
remoteUrl = null,
thumbnailUrl = null,
type = FileType.Audio,
mimeType = MimeType("audio", "mp3", FileType.Audio),
blurHash = null,
description = null,
actorId = ActorId(1)
)
ExposedMediaRepository().save(media)
assertThat(assertTable)
.row(0)
.isEqualTo(Media.id, 1)
.isEqualTo(Media.name, "name")
.value(Media.url).isEqualTo("https://www.example.com")
.isEqualTo(Media.remoteUrl, null)
.isEqualTo(Media.thumbnailUrl, null)
.isEqualTo(Media.type, "Audio")
.isEqualTo(Media.mimeType, "audio/mp3")
.isEqualTo(Media.blurhash, null)
.isEqualTo(Media.description, null)
.isEqualTo(Media.actorId, 1)
}
@Test
fun save_idが同じレコードが存在したらupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto("public.media") {
columns(
"id",
"name",
"url",
"remote_url",
"thumbnail_url",
"type",
"blurhash",
"mime_type",
"description",
"actor_id"
)
values(
1,
"pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg",
"http://localhost:8081/files/1833054358862827520.jpeg",
null,
"http://localhost:8081/files/thumbnail-1833054358862827520.jpeg",
"Image",
"U\$JuAZWBxut7~qoLoft6j]t7Rjj[RjayWBay",
"image/jpeg",
null,
1
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val media = EntityMedia(
id = MediaId(1),
name = MediaName("name"),
url = URI.create("https://www.example.com"),
remoteUrl = null,
thumbnailUrl = null,
type = FileType.Audio,
mimeType = MimeType("audio", "mp3", FileType.Audio),
blurHash = null,
description = null,
actorId = ActorId(1)
)
ExposedMediaRepository().save(media)
assertThat(assertTable)
.row(0)
.isEqualTo(Media.id, 1)
.isEqualTo(Media.name, "name")
.value(Media.url).isEqualTo("https://www.example.com")
.isEqualTo(Media.remoteUrl, null)
.isEqualTo(Media.thumbnailUrl, null)
.isEqualTo(Media.type, "Audio")
.isEqualTo(Media.mimeType, "audio/mp3")
.isEqualTo(Media.blurhash, null)
.isEqualTo(Media.description, null)
.isEqualTo(Media.actorId, 1)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.media") {
columns(
"id",
"name",
"url",
"remote_url",
"thumbnail_url",
"type",
"blurhash",
"mime_type",
"description",
"actor_id"
)
values(
1,
"pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg",
"http://localhost:8081/files/1833054358862827520.jpeg",
null,
"http://localhost:8081/files/thumbnail-1833054358862827520.jpeg",
"Image",
"U\$JuAZWBxut7~qoLoft6j]t7Rjj[RjayWBay",
"image/jpeg",
null,
1
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val media = EntityMedia(
id = MediaId(1),
name = MediaName("name"),
url = URI.create("https://www.example.com"),
remoteUrl = null,
thumbnailUrl = null,
type = FileType.Audio,
mimeType = MimeType("audio", "mp3", FileType.Audio),
blurHash = null,
description = null,
actorId = ActorId(1)
)
change.withSuspend {
ExposedMediaRepository().delete(media)
}
assertThat(change)
.changeOfDeletionOnTable(Media.tableName)
.rowAtStartPoint()
.value(Media.id.name)
.isEqualTo(1)
}
@Test
fun findById_指定されたIdで存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.media") {
columns(
"id",
"name",
"url",
"remote_url",
"thumbnail_url",
"type",
"blurhash",
"mime_type",
"description",
"actor_id"
)
values(
1,
"pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg",
"http://localhost:8081/files/1833054358862827520.jpeg",
"http://localhost:8081/files/183305453584862827520.jpeg",
"http://localhost:8081/files/thumbnail-1833054358862827520.jpeg",
"Image",
"U\$JuAZWBxut7~qoLoft6j]t7Rjj[RjayWBay",
"image/jpeg",
null,
1
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expect = EntityMedia(
id = MediaId(1),
name = MediaName("pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg"),
url = URI.create("http://localhost:8081/files/1833054358862827520.jpeg"),
remoteUrl = URI.create("http://localhost:8081/files/183305453584862827520.jpeg"),
thumbnailUrl = URI.create("http://localhost:8081/files/thumbnail-1833054358862827520.jpeg"),
type = FileType.Image,
mimeType = MimeType("image", "jpeg", FileType.Image),
blurHash = MediaBlurHash("U\$JuAZWBxut7~qoLoft6j]t7Rjj[RjayWBay"),
description = null,
actorId = ActorId(1)
)
val actual = ExposedMediaRepository().findById(MediaId(1))
assertNotNull(actual)
assertEquals(expect, actual)
assertEquals(expect.id, actual.id)
assertEquals(expect.name, actual.name)
assertEquals(expect.url, actual.url)
assertEquals(expect.remoteUrl, actual.remoteUrl)
assertEquals(expect.thumbnailUrl, actual.thumbnailUrl)
assertEquals(expect.type, actual.type)
assertEquals(expect.mimeType, actual.mimeType)
assertEquals(expect.blurHash, actual.blurHash)
assertEquals(expect.description, actual.description)
assertEquals(expect.actorId, actual.actorId)
}
@Test
fun findById_指定されたIdで存在しないとnull() = runTest {
assertNull(ExposedMediaRepository().findById(MediaId(1)))
}
@Test
fun findByIdIn_指定されたIdすべて返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.media") {
columns(
"id",
"name",
"url",
"remote_url",
"thumbnail_url",
"type",
"blurhash",
"mime_type",
"description",
"actor_id"
)
values(
1,
"pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg",
"http://localhost:8081/files/1833054358862827520.jpeg",
null,
null,
"Image",
"U\$JuAZWBxut7~qoLoft6j]t7Rjj[RjayWBay",
"image/jpeg",
"",
1
)
values(
3,
"pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg",
"http://localhost:8081/files/18330354358862827520.jpeg",
null,
"http://localhost:8081/files/thumbn3ail-1833054358862827520.jpeg",
"Image",
null,
"image/jpeg",
null,
1
)
values(
2,
"pnc__picked_media_256f8e6d-68cd-4a76-bb38-57e35f6ca8c6.jpg",
"http://localhost:8081/files/18330545358862827520.jpeg",
"http://localhost:8081/files/183305453584862827520.jpeg",
null,
"Image",
"U\$JuAZWBxut7~qoLoft6j]t7Rjj[RjayWBay",
"image/jpeg",
null,
1
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val actual = ExposedMediaRepository().findByIdIn(listOf(MediaId(1), MediaId(3)))
assertThat(actual)
.hasSize(2)
}
}

View File

@ -0,0 +1,440 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
import dev.usbharu.hideout.core.domain.model.reaction.ReactionId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.db.api.Assertions.assertThat
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import utils.*
import java.sql.Timestamp
import java.time.Instant
import kotlin.test.*
@ExtendWith(MockitoExtension::class)
class ExposedReactionRepositoryTest : AbstractRepositoryTest(Reactions) {
@InjectMocks
lateinit var repository: ExposedReactionRepository
@Mock
lateinit var domainEventPublisher: DomainEventPublisher
@Test
fun save_idが同じレコードがなければinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Posts.tableName) {
columns(Posts.columns)
values(
1,
1,
1,
null,
"test",
"test",
Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")),
"PUBLIC",
"https://example.com",
null,
null,
false,
"https://example.com",
false,
false,
null
)
}
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(1, "emoji", "example.com", 1, "https://example.com", null, Timestamp.from(Instant.now()))
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val create = Reaction.create(
id = ReactionId(1),
postId = PostId(1),
actorId = ActorId(1),
customEmojiId = CustomEmojiId(1),
unicodeEmoji = UnicodeEmoji(""),
createdAt = Instant.parse("2021-01-01T00:00:00Z")
)
repository.save(create)
assertThat(assertTable)
.row(0)
.isEqualTo(Reactions.id, create.id.value)
.isEqualTo(Reactions.postId, create.postId.id)
.isEqualTo(Reactions.actorId, create.actorId.id)
.isEqualTo(Reactions.customEmojiId, create.customEmojiId?.emojiId)
.isEqualTo(Reactions.unicodeEmoji, create.unicodeEmoji.name)
.value(Reactions.createdAt).isEqualTo(Timestamp.from(create.createdAt))
}
@Test
fun save_idが同じレコードが存在したらupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Posts.tableName) {
columns(Posts.columns)
values(
1,
1,
1,
null,
"test",
"test",
Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")),
"PUBLIC",
"https://example.com",
null,
null,
false,
"https://example.com",
false,
false,
null
)
}
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(CustomEmojis.tableName) {
columns(CustomEmojis.columns)
values(1, "emoji", "example.com", 1, "https://example.com", null, Timestamp.from(Instant.now()))
}
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 2, "☠️", Timestamp.from(Instant.now()))
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val create = Reaction.create(
id = ReactionId(1),
postId = PostId(1),
actorId = ActorId(1),
customEmojiId = CustomEmojiId(1),
unicodeEmoji = UnicodeEmoji(""),
createdAt = Instant.parse("2021-01-01T00:00:00Z")
)
repository.save(create)
assertThat(assertTable)
.row(0)
.isEqualTo(Reactions.id, create.id.value)
.isEqualTo(Reactions.postId, create.postId.id)
.isEqualTo(Reactions.actorId, create.actorId.id)
.isEqualTo(Reactions.customEmojiId, create.customEmojiId?.emojiId)
.isEqualTo(Reactions.unicodeEmoji, create.unicodeEmoji.name)
.value(Reactions.createdAt).isEqualTo(Timestamp.from(create.createdAt))
}
@Test
fun findById_指定されたIdが存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val expected = Reaction.create(
id = ReactionId(1),
postId = PostId(1),
actorId = ActorId(1),
customEmojiId = CustomEmojiId(1),
unicodeEmoji = UnicodeEmoji(""),
createdAt = Instant.parse("2021-01-01T00:00:00Z")
)
val actual = repository.findById(ReactionId(1))
assertEquals(expected, actual)
}
private fun assertEquals(
expected: Reaction,
actual: Reaction?
) {
kotlin.test.assertEquals(expected, actual)
assertNotNull(actual)
assertEquals(expected.id, actual.id)
assertEquals(expected.postId, actual.postId)
assertEquals(expected.actorId, actual.actorId)
assertEquals(expected.customEmojiId, actual.customEmojiId)
assertEquals(expected.unicodeEmoji, actual.unicodeEmoji)
assertEquals(expected.createdAt, actual.createdAt)
}
@Test
fun findById_指定されたIdがなければnull() = runTest {
assertNull(repository.findById(ReactionId(1)))
}
@Test
fun findByPostId_指定されたId全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(2, 3, 2, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(3, 1, 3, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val actual = repository.findByPostId(PostId(1))
assertThat(actual)
.hasSize(2)
}
@Test
fun existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji_指定された条件で存在したらtrue() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(2, 3, 2, null, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(3, 1, 3, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val actual1 = repository.existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
PostId(1),
ActorId(1), CustomEmojiId
(1), ""
)
val actual2 = repository.existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
PostId(3),
ActorId(2), null, ""
)
assertTrue(actual1)
assertTrue(actual2)
}
@Test
fun existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji_指定された条件で存在しないとfalse() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(2, 3, 2, null, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(3, 1, 3, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val actual1 = repository.existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
PostId(3),
ActorId(1), CustomEmojiId
(1), ""
)
val actual2 = repository.existsByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
PostId(2),
ActorId(2), null, ""
)
assertFalse(actual1)
assertFalse(actual2)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(2, 3, 2, null, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(3, 1, 3, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val reaction = Reaction(
ReactionId(1),
PostId(1),
ActorId(1),
CustomEmojiId(1),
UnicodeEmoji(""),
Instant.parse("2021-01-01T00:00:00Z")
)
repository.delete(reaction)
}
@Test
fun findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji_指定された条件で存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(2, 3, 2, null, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(3, 1, 3, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val expected = Reaction(
ReactionId(1),
PostId(1),
ActorId(1),
CustomEmojiId(1),
UnicodeEmoji(""),
Instant.parse("2021-01-01T00:00:00Z")
)
val actual =
repository.findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
PostId(1),
ActorId(1), CustomEmojiId
(1), ""
)
assertEquals(expected, actual)
}
@Test
fun findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji_指定された条件で存在しないとnull() = runTest {
assertNull(
repository.findByPostIdAndActorIdAndCustomEmojiIdOrUnicodeEmoji(
PostId(1),
ActorId(1), CustomEmojiId
(1), ""
)
)
}
@Test
fun save_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
}.launch()
repository.save(
Reaction.create(
ReactionId(1),
PostId(1), ActorId
(1), CustomEmojiId
(1), UnicodeEmoji(""), Instant.now
()
)
)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
@Test
fun delete_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Reactions.tableName) {
columns(Reactions.columns)
values(1, 1, 1, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(2, 3, 2, null, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
values(3, 1, 3, 1, "", Timestamp.from(Instant.parse("2021-01-01T00:00:00Z")))
}
}.launch()
val reaction = Reaction(
ReactionId(1),
PostId(1), ActorId
(1), CustomEmojiId
(1), UnicodeEmoji(""), Instant.now
()
)
reaction.delete()
repository.delete(
reaction
)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
}

View File

@ -0,0 +1,503 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import kotlinx.coroutines.test.runTest
import org.assertj.db.api.Assertions.assertThat
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import utils.*
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ExtendWith(MockitoExtension::class)
class ExposedRelationshipRepositoryTest : AbstractRepositoryTest(Relationships) {
@InjectMocks
lateinit var repository: ExposedRelationshipRepository
@Mock
lateinit var domainEventPublisher: DomainEventPublisher
@Test
fun save_idが同じレコードがなければinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Actors.tableName) {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
values(
2,
"a",
"test-hideout-dev.usbharu.dev",
"a",
"",
"https://test-hideout-dev.usbharu.dev/users/a/inbox",
"https://test-hideout-dev.usbharu.dev/users/a/outbox",
"https://test-hideout-dev.usbharu.dev/users/a",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/a#main-key",
"https://test-hideout-dev.usbharu.dev/users/a/following",
"https://test-hideout-dev.usbharu.dev/users/a/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
}.launch()
val relationship = Relationship.default(
ActorId(1), ActorId(2)
)
repository.save(relationship)
assertThat(assertTable)
.row(0)
.isEqualTo(Relationships.actorId, relationship.actorId.id)
.isEqualTo(Relationships.targetActorId, relationship.targetActorId.id)
.isEqualTo(Relationships.following, relationship.following)
.isEqualTo(Relationships.blocking, relationship.blocking)
.isEqualTo(Relationships.muting, relationship.muting)
.isEqualTo(Relationships.followRequesting, relationship.followRequesting)
.isEqualTo(Relationships.mutingFollowRequest, relationship.mutingFollowRequest)
}
@Test
fun save_idが同じレコードがあればupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Actors.tableName) {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
values(
2,
"a",
"test-hideout-dev.usbharu.dev",
"a",
"",
"https://test-hideout-dev.usbharu.dev/users/a/inbox",
"https://test-hideout-dev.usbharu.dev/users/a/outbox",
"https://test-hideout-dev.usbharu.dev/users/a",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/a#main-key",
"https://test-hideout-dev.usbharu.dev/users/a/following",
"https://test-hideout-dev.usbharu.dev/users/a/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(Relationships.tableName) {
columns(Relationships.columns)
values(1, 2, true, false, false, false, false)
}
}.launch()
val relationship = Relationship(
actorId = ActorId(1),
targetActorId = ActorId(2),
following = false,
blocking = true,
muting = false,
followRequesting = false,
mutingFollowRequest = false,
)
repository.save(relationship)
assertThat(assertTable)
.row(0)
.isEqualTo(Relationships.actorId, relationship.actorId.id)
.isEqualTo(Relationships.targetActorId, relationship.targetActorId.id)
.isEqualTo(Relationships.following, relationship.following)
.isEqualTo(Relationships.blocking, relationship.blocking)
.isEqualTo(Relationships.muting, relationship.muting)
.isEqualTo(Relationships.followRequesting, relationship.followRequesting)
.isEqualTo(Relationships.mutingFollowRequest, relationship.mutingFollowRequest)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Actors.tableName) {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
values(
2,
"a",
"test-hideout-dev.usbharu.dev",
"a",
"",
"https://test-hideout-dev.usbharu.dev/users/a/inbox",
"https://test-hideout-dev.usbharu.dev/users/a/outbox",
"https://test-hideout-dev.usbharu.dev/users/a",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/a#main-key",
"https://test-hideout-dev.usbharu.dev/users/a/following",
"https://test-hideout-dev.usbharu.dev/users/a/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto(Relationships.tableName) {
columns(Relationships.columns)
values(1, 2, true, false, false, false, false)
}
}.launch()
val relationship = Relationship.default(
ActorId(1), ActorId(2)
)
change.withSuspend {
repository.delete(relationship)
}
assertThat(change)
.changeOfDeletionOnTable(Relationships.tableName)
.rowAtStartPoint()
.value(Relationships.actorId.name).isEqualTo(relationship.actorId.id)
.value(Relationships.targetActorId.name).isEqualTo(relationship.targetActorId.id)
}
@Test
fun findByActorIdAndTargetId_指定された条件で存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Relationships.tableName) {
columns(Relationships.columns)
values(1, 2, true, false, true, true, false)
values(2, 1, true, false, false, false, false)
values(4, 3, true, false, false, false, true)
values(3, 5, true, false, false, true, false)
}
}.launch()
val expected = Relationship(
actorId = ActorId(1),
targetActorId = ActorId(2),
following = true,
blocking = false,
muting = true,
followRequesting = true,
mutingFollowRequest = false
)
val actual = repository.findByActorIdAndTargetId(ActorId(1), ActorId(2))
assertEquals(expected, actual)
assertNotNull(actual)
assertEquals(expected.actorId, actual.actorId)
assertEquals(expected.targetActorId, actual.targetActorId)
assertEquals(expected.following, actual.following)
assertEquals(expected.blocking, actual.blocking)
assertEquals(expected.muting, actual.muting)
assertEquals(expected.followRequesting, actual.followRequesting)
assertEquals(expected.mutingFollowRequest, actual.mutingFollowRequest)
}
@Test
fun findByActorIdAndTargetId_指定された条件で存在しないとnull() = runTest {
assertNull(repository.findByActorIdAndTargetId(ActorId(1), ActorId(2)))
}
@Test
fun findByActorIdsAndTargetIdAndBlocking_指定された条件全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Relationships.tableName) {
columns(Relationships.columns)
values(1, 2, true, false, true, true, false)
values(1, 3, false, true, true, true, false)
values(1, 4, true, false, true, true, false)
values(2, 1, true, false, false, false, false)
values(4, 3, true, false, false, false, true)
values(3, 5, true, false, false, true, false)
}
}.launch()
val expected = listOf(
Relationship(
actorId = ActorId(1),
targetActorId = ActorId(3),
following = false,
blocking = true,
muting = true,
followRequesting = true,
mutingFollowRequest = false
)
)
val actual = repository.findByActorIdsAndTargetIdAndBlocking(
listOf(ActorId(1), ActorId(4)), ActorId(3), blocking = true
)
assertContentEquals(expected, actual)
val expected2 = listOf(
Relationship(
actorId = ActorId(4),
targetActorId = ActorId(3),
following = true,
blocking = false,
muting = false,
followRequesting = false,
mutingFollowRequest = true
)
)
val actual2 = repository.findByActorIdsAndTargetIdAndBlocking(
listOf(ActorId(1), ActorId(4)), ActorId(3), blocking = false
)
assertContentEquals(expected2, actual2)
}
@Test
fun findByActorIdAndTargetIdsAndFollowing_指定された条件全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Relationships.tableName) {
columns(Relationships.columns)
values(1, 2, true, false, true, true, false)
values(1, 3, false, false, true, true, false)
values(1, 4, true, false, true, true, false)
values(2, 1, true, false, false, false, false)
values(4, 3, true, false, false, false, true)
values(3, 5, true, false, false, true, false)
}
}.launch()
val expected = listOf(
Relationship(
actorId = ActorId(1),
targetActorId = ActorId(2),
following = false,
blocking = true,
muting = true,
followRequesting = true,
mutingFollowRequest = false
), Relationship(
actorId = ActorId(1),
targetActorId = ActorId(4),
following = false,
blocking = true,
muting = true,
followRequesting = true,
mutingFollowRequest = false
)
)
val actual = repository.findByActorIdAndTargetIdsAndFollowing(
ActorId(1), listOf(ActorId(2), ActorId(3), ActorId(4)), following = true
)
assertContentEquals(expected, actual)
val expected2 = listOf(
Relationship(
actorId = ActorId(1),
targetActorId = ActorId(3),
following = true,
blocking = false,
muting = false,
followRequesting = false,
mutingFollowRequest = true
)
)
val actual2 = repository.findByActorIdAndTargetIdsAndFollowing(
ActorId(1), listOf(ActorId(2), ActorId(3), ActorId(4)), following = false
)
assertContentEquals(expected2, actual2)
}
@Test
fun save_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
}.launch()
val relationship = Relationship(
actorId = ActorId(1),
targetActorId = ActorId(2),
following = false,
blocking = true,
muting = false,
followRequesting = false,
mutingFollowRequest = false,
)
relationship.block()
repository.save(relationship)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
@Test
fun delete_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Relationships.tableName) {
columns(Relationships.columns)
values(1, 2, true, false, false, false, false)
}
}.launch()
val relationship = Relationship(
actorId = ActorId(1),
targetActorId = ActorId(2),
following = false,
blocking = true,
muting = false,
followRequesting = false,
mutingFollowRequest = false,
)
relationship.block()
repository.delete(relationship)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
}

View File

@ -0,0 +1,254 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.db.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import utils.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
class ExposedTimelineRelationshipRepositoryTest : AbstractRepositoryTest(TimelineRelationships) {
@Test
fun save_idが同じレコードがなければinsert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "time-line", "PUBLIC", false)
}
insertInto("public.actors") {
columns(
Actors.columns
)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val relationship = TimelineRelationship(
TimelineRelationshipId(1), TimelineId(1), ActorId(1), Visible.PUBLIC
)
ExposedTimelineRelationshipRepository().save(relationship)
assertThat(assertTable)
.row(0)
.isEqualTo(TimelineRelationships.id, relationship.id.value)
.isEqualTo(TimelineRelationships.timelineId, relationship.timelineId.value)
.isEqualTo(TimelineRelationships.actorId, relationship.actorId.id)
.isEqualTo(TimelineRelationships.visible, relationship.visible.name)
}
@Test
fun save_idが同じレコードがあればupdate() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "time-line", "PUBLIC", false)
}
insertInto(TimelineRelationships.tableName) {
columns(TimelineRelationships.columns)
values(1, 2, 3, "PUBLIC")
}
insertInto("public.actors") {
columns(
Actors.columns
)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val relationship = TimelineRelationship(
TimelineRelationshipId(1), TimelineId(1), ActorId(1), Visible.PUBLIC
)
ExposedTimelineRelationshipRepository().save(relationship)
assertThat(assertTable)
.row(0)
.isEqualTo(TimelineRelationships.id, relationship.id.value)
.isEqualTo(TimelineRelationships.timelineId, relationship.timelineId.value)
.isEqualTo(TimelineRelationships.actorId, relationship.actorId.id)
.isEqualTo(TimelineRelationships.visible, relationship.visible.name)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(TimelineRelationships.tableName) {
columns(TimelineRelationships.columns)
values(1, 2, 3, "PUBLIC")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val timelineRelationship = TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(1), ActorId
(3), Visible.PUBLIC
)
change.withSuspend {
ExposedTimelineRelationshipRepository().delete(timelineRelationship)
}
assertThat(change)
.changeOfDeletionOnTable(TimelineRelationships.tableName)
.rowAtStartPoint()
.value(TimelineRelationships.id.name).isEqualTo(timelineRelationship.id.value)
}
@Test
fun findByActorId_指定されたid全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(TimelineRelationships.tableName) {
columns(TimelineRelationships.columns)
values(1, 2, 3, "PUBLIC")
values(2, 3, 3, "PUBLIC")
values(3, 3, 4, "PUBLIC")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val findByActorId = ExposedTimelineRelationshipRepository().findByActorId(actorId = ActorId(3))
assertThat(findByActorId)
.hasSize(2)
}
@Test
fun findById_指定されたidで存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(TimelineRelationships.tableName) {
columns(TimelineRelationships.columns)
values(1, 2, 3, "PUBLIC")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expected = TimelineRelationship(
id = TimelineRelationshipId(1),
timelineId = TimelineId(2),
actorId = ActorId
(3),
visible = Visible.PUBLIC
)
val actual = ExposedTimelineRelationshipRepository().findById(TimelineRelationshipId(1))
assertEquals(expected, actual)
assertNotNull(actual)
assertEquals(expected.id, actual.id)
assertEquals(expected.actorId, actual.actorId)
assertEquals(expected.timelineId, actual.timelineId)
assertEquals(expected.visible, actual.visible)
}
@Test
fun findById_指定されたIdで存在しないとnull() = runTest {
assertNull(ExposedTimelineRelationshipRepository().findById(TimelineRelationshipId(1)))
}
@Test
fun findByTimelineIdAndActorId_指定された条件で存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(TimelineRelationships.tableName) {
columns(TimelineRelationships.columns)
values(1, 2, 3, "PUBLIC")
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expected = TimelineRelationship(
id = TimelineRelationshipId(1),
timelineId = TimelineId(2),
actorId = ActorId
(3),
visible = Visible.PUBLIC
)
val actual = ExposedTimelineRelationshipRepository().findByTimelineIdAndActorId(TimelineId(2), ActorId(3))
assertEquals(expected, actual)
assertNotNull(actual)
assertEquals(expected.id, actual.id)
assertEquals(expected.actorId, actual.actorId)
assertEquals(expected.timelineId, actual.timelineId)
assertEquals(expected.visible, actual.visible)
}
@Test
fun findByTimelineIdAndActorId_指定された条件で存在しないとnull() = runTest {
ExposedTimelineRelationshipRepository().findByTimelineIdAndActorId(TimelineId(1), ActorId(1))
}
}

View File

@ -0,0 +1,250 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
import dev.usbharu.hideout.core.domain.model.timeline.TimelineName
import dev.usbharu.hideout.core.domain.model.timeline.TimelineVisibility
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.db.api.Assertions.assertThat
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import utils.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ExtendWith(MockitoExtension::class)
class ExposedTimelineRepositoryTest : AbstractRepositoryTest(Timelines) {
@InjectMocks
lateinit var repository: ExposedTimelineRepository
@Mock
lateinit var domainEventPublisher: DomainEventPublisher
@Test
fun save_idが同じレコードが存在しない場合insert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 1, "veeeeeeeeeeeeeeryStrongPassword", true, null, null)
}
}.launch()
val timeline = Timeline(
id = TimelineId(1),
userDetailId = UserDetailId(1),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
repository.save(timeline)
assertThat(assertTable).row(0).isEqualTo(Timelines.id, timeline.id.value)
.isEqualTo(Timelines.userDetailId, timeline.userDetailId.id).isEqualTo(Timelines.name, timeline.name.value)
.isEqualTo(Timelines.visibility, timeline.visibility.name).isEqualTo(Timelines.isSystem, timeline.isSystem)
}
@Test
fun save_idが同じレコードが存在する場合update() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 1, "veeeeeeeeeeeeeeryStrongPassword", true, null, null)
}
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "test-timeline", "PUBLIC", true)
}
}.launch()
val timeline = Timeline(
id = TimelineId(1),
userDetailId = UserDetailId(1),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PRIVATE,
isSystem = false
)
repository.save(timeline)
assertThat(assertTable).row(0).isEqualTo(Timelines.id, timeline.id.value)
.isEqualTo(Timelines.userDetailId, timeline.userDetailId.id).isEqualTo(Timelines.name, timeline.name.value)
.isEqualTo(Timelines.visibility, timeline.visibility.name).isEqualTo(Timelines.isSystem, timeline.isSystem)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 1, "veeeeeeeeeeeeeeryStrongPassword", true, null, null)
}
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "test-timeline", "PUBLIC", true)
}
}.launch()
val timeline = Timeline(
id = TimelineId(1),
userDetailId = UserDetailId(1),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PRIVATE,
isSystem = false
)
change.withSuspend {
repository.delete(timeline)
}
assertThat(change).changeOfDeletionOnTable(Timelines.tableName).rowAtStartPoint().value(Timelines.id.name)
.isEqualTo(timeline.id.value)
}
@Test
fun findByIds_指定されたIdすべて返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "test-timeline", "PUBLIC", true)
values(2, 1, "test-timeline2", "PUBLIC", true)
values(3, 1, "test-timeline3", "PUBLIC", true)
}
}.launch()
val findByIds = repository.findByIds(listOf(TimelineId(1), TimelineId(3)))
assertThat(findByIds).hasSize(2)
}
@Test
fun findById_指定されたIdが存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "test-timeline", "PUBLIC", true)
values(2, 1, "test-timeline2", "PUBLIC", true)
values(3, 1, "test-timeline3", "PUBLIC", true)
}
}.launch()
val actual = repository.findById(TimelineId(1))
val expected = Timeline(
TimelineId(1), UserDetailId(1), TimelineName("test-timeline"), TimelineVisibility.PUBLIC, true
)
assertEquals(expected, actual)
assertNotNull(actual)
assertEquals(expected.id, actual.id)
assertEquals(expected.userDetailId, actual.userDetailId)
assertEquals(expected.name, actual.name)
assertEquals(expected.visibility, actual.visibility)
assertEquals(expected.isSystem, actual.isSystem)
}
@Test
fun findById_指定されたIdがなければnull() = runTest {
assertNull(repository.findById(TimelineId(1)))
}
@Test
fun findAllByUserDetailIdANdVisibilityIn_指定されたVisibilityで指定されたUserDetailId全部返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "test-timeline", "PUBLIC", true)
values(2, 1, "test-timeline2", "PRIVATE", true)
values(3, 1, "test-timeline3", "PUBLIC", true)
}
}.launch()
val timelines =
repository.findAllByUserDetailIdAndVisibilityIn(UserDetailId(1), listOf(TimelineVisibility.PUBLIC))
assertThat(timelines).hasSize(2)
}
@Test
fun save_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
}.launch()
val timeline = Timeline(
id = TimelineId(1),
userDetailId = UserDetailId(1),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PRIVATE,
isSystem = false
)
timeline.setVisibility(
TimelineVisibility.PUBLIC, UserDetail.create(
UserDetailId(1), ActorId(1),
UserDetailHashedPassword("aaaaaa"),
)
)
repository.save(timeline)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
@Test
fun delete_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto(UserDetails.tableName) {
columns(UserDetails.columns)
values(1, 1, "veeeeeeeeeeeeeeryStrongPassword", true, null, null)
}
insertInto(Timelines.tableName) {
columns(Timelines.columns)
values(1, 1, "test-timeline", "PUBLIC", true)
}
}.launch()
val timeline = Timeline(
id = TimelineId(1),
userDetailId = UserDetailId(1),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PRIVATE,
isSystem = false
)
timeline.setVisibility(
TimelineVisibility.PUBLIC, UserDetail.create(
UserDetailId(1), ActorId(1),
UserDetailHashedPassword("aaaaaa"),
)
)
repository.delete(timeline)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
}

View File

@ -0,0 +1,367 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import com.ninja_squad.dbsetup_kotlin.dbSetup
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.db.api.Assertions.assertThat
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import utils.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
@ExtendWith(MockitoExtension::class)
class ExposedUserDetailRepositoryTest : AbstractRepositoryTest(UserDetails) {
@InjectMocks
lateinit var userDetailRepository: ExposedUserDetailRepository
@Mock
lateinit var domainEventPublisher: DomainEventPublisher
@Test
fun save_idが同じレコードがない場合insert() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
userDetailRepository.save(
UserDetail.create(
UserDetailId(1),
ActorId(1),
UserDetailHashedPassword("VeeeeeeeeeeeeeryStrongPassword"),
false,
null,
null
)
)
assertThat(assertTable).row(0).isEqualTo(UserDetails.id, 1).isEqualTo(UserDetails.actorId, 1)
.isEqualTo(UserDetails.password, "VeeeeeeeeeeeeeryStrongPassword")
.isEqualTo(UserDetails.lastMigration, null).isEqualTo(UserDetails.autoAcceptFolloweeFollowRequest, false)
.isEqualTo(UserDetails.homeTimelineId, null)
}
@Test
fun save_idが同じレコードがある場合update() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.actors") {
columns(Actors.columns)
values(
1,
"b",
"test-hideout-dev.usbharu.dev",
"b",
"",
"https://test-hideout-dev.usbharu.dev/users/b/inbox",
"https://test-hideout-dev.usbharu.dev/users/b/outbox",
"https://test-hideout-dev.usbharu.dev/users/b",
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuMjzmQBsSxzK6NkOpZh\nWuohaUbzCY7AafXt+3+tiL6LulYNg/YRIqKc7Q/vTJE6CHrqo7RA/OqYrSMxF/LC\nf8aX5aHwJE1A2gSgCcs1IL5GJaYRlp4NcuazpBC9NO4xIrvH//jcVnZGXGWsCbls\nHXZGZdurWOF0Bl3mYN8CdupVumrGuOPs+wbI/Gh+OHw611TcXMyAwFwU2UjvPEgk\nEACW9OvJaq1K40jVCAa3b1nXt53vlXXZEUlL78L0C9xuFbJG0K/GKMBN44GyftJO\nhA95Rf1Nhd0vKDLBiRocGcARmBo9PaSCR5651gJEk5/wfLUnNAf0xj3R8LBoOhnT\nCQIDAQAB\n-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK4yPOZAGxLHMr\no2Q6lmFa6iFpRvMJjsBp9e37f62Ivou6Vg2D9hEiopztD+9MkToIeuqjtED86pit\nIzEX8sJ/xpflofAkTUDaBKAJyzUgvkYlphGWng1y5rOkEL007jEiu8f/+NxWdkZc\nZawJuWwddkZl26tY4XQGXeZg3wJ26lW6asa44+z7Bsj8aH44fDrXVNxczIDAXBTZ\nSO88SCQQAJb068lqrUrjSNUIBrdvWde3ne+VddkRSUvvwvQL3G4VskbQr8YowE3j\ngbJ+0k6ED3lF/U2F3S8oMsGJGhwZwBGYGj09pIJHnrnWAkSTn/B8tSc0B/TGPdHw\nsGg6GdMJAgMBAAECggEAHkEhLEb70kdOGgJLUR9D/5zYBE0eXdz/MsMyd1AH+Shs\n9AmetKsYzWDmuhp9Cp5swyn328Hmn7B+DvInVn+5YvjNhY07SbaJcVls4g5UQFXk\nu6WC4ZfKap7IyAeaUg54858r8677xcWXuByN5dn+1iU2hJGYK3Cx7rx0PRrUURYG\n2BRaEEwkcPNm9u679OOTyvTmA3NhewUuDaTMkZnnAml87uYYnmFKjQcR+S2UqOm6\nvBZ/devG4TfPBeKEAya/ba8JJ8frGOtjmR9EIliTQoxI2izeAfoGs1OsCSpuPy6s\nV5f0X3HYM7CA+Fpkt2pnixuwg96LaVr4OpVxujhNlwKBgQD1827VuKFGrneNO+c+\n4EIvh+vLh462bJiaVsMHfRhNZF1/5i8gfNJ16ST60hJo11E4riHPzi3q6GWuxOYl\nCkVKvhJ2g3mgnhoehcgnT7UBkasaC7JYd+LsFDnWOTVSJOy2OqfLdLDGAuSTN3kO\nBF4p0ZqQ/AouFNin57WNRGVZ7wKBgQDTLUZtfTkOU3G1nIMTRKmZjqdER5glzHCm\n9o/1ZsQktL+nzSXqYeoWh9fr7fkmC0k/07+SHzzfWvOhWWWlRenUVL5mj7FRq+L9\n9kDjChLR3Jr4L6Sj1iaQ+0uqDSQNYSYO9ctMjAVjFiNhiAd+S6B451Q1VbDKTCHt\nkRW9omz6hwKBgBFTsgY6eJorJl77zmG+mMsSb0kqZqJxahrNa/X2GSUyoeelxsIq\nKQWHhERrUkKykJVGpzkllFSNRMSYOIJ5g8ItO82/m2z2Vm66DAzA78aJhZ1TH6Bd\n6c2p6x0tcJU15rs7zKBnuyBoCcRZTxzur9eQXaxDJVBzxYOmrkKig+VfAoGBAMCP\n2Fiehxh5HobsYNmBEuXjHsM0RZiyA0c8LakoPFL8PodUme5PupUw6cNJDJeUUwbQ\nny8vLOK+nMnUKsu6JK5pV/VNsfM3OZU6p5Bf7ylOcEE/sHF1JVWu0CAQO3+3xmx9\n1RPH2mGwHjMhRzPy4jFdP3wi10KgiY+HbLuvEJChAoGAYCsh3UhtTzGUOlPBkmLL\n17bD0wN4J/fOv8BoXPZ8H2CdqVgWy0s+s+QaPqRxNcA6YyGymBqrmQAn1Uii25r9\nKAwVAjg3S2KDEMSI2RbMMmQJSZ1u0GkxqOUC/MMeZqBYTYxVeqcQPoqJZ0Nk7IOA\nZPFif8bVfcZqeimxrFaV6YI=\n-----END PRIVATE KEY-----",
"2024-09-09 17:12:03.941339",
"https://test-hideout-dev.usbharu.dev/users/b#main-key",
"https://test-hideout-dev.usbharu.dev/users/b/following",
"https://test-hideout-dev.usbharu.dev/users/b/followers",
1,
false,
0,
0,
0,
null,
"2024-09-09 17:12:03.941339",
false,
null,
"",
false,
null,
null
)
}
insertInto("public.user_details") {
columns(UserDetails.columns)
values(
1,
1,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
userDetailRepository.save(
UserDetail.create(
UserDetailId(1),
ActorId(1),
UserDetailHashedPassword("VeeeeeeeeeeeeeryStrongPassword"),
false,
null,
null
)
)
assertThat(assertTable).row(0).isEqualTo(UserDetails.id, 1).isEqualTo(UserDetails.actorId, 1)
.isEqualTo(UserDetails.password, "VeeeeeeeeeeeeeryStrongPassword")
.isEqualTo(UserDetails.lastMigration, null).isEqualTo(UserDetails.autoAcceptFolloweeFollowRequest, false)
.isEqualTo(UserDetails.homeTimelineId, null)
}
@Test
fun delete_削除される() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.user_details") {
columns(UserDetails.columns)
values(
1,
1,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val userDetail = UserDetail(
UserDetailId(1), ActorId(1), UserDetailHashedPassword("VeeeeeeeeeeeeeryStrongPassword"), false, null, null
)
change.withSuspend {
userDetailRepository.delete(userDetail)
}
assertThat(change).changeOfDeletionOnTable(UserDetails.tableName).rowAtStartPoint().value(UserDetails.id.name)
.isEqualTo(1)
}
@Test
fun findByActorId_指定したActorIdで存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.user_details") {
columns(UserDetails.columns)
values(
1,
1,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expect = UserDetail(
id = UserDetailId(1),
actorId = ActorId(1),
password = UserDetailHashedPassword("$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm"),
autoAcceptFolloweeFollowRequest = false,
lastMigration = null,
homeTimelineId = TimelineId(1832779979297918976)
)
val actual = userDetailRepository.findByActorId(1)
assertEquals(actual, expect)
}
@Test
fun findByActorId_指定したActorIdで存在しないとnull() = runTest {
assertNull(userDetailRepository.findByActorId(1))
}
@Test
fun findById_指定したIdで存在したら返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.user_details") {
columns(UserDetails.columns)
values(
1,
1,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val expect = UserDetail(
id = UserDetailId(1),
actorId = ActorId(1),
password = UserDetailHashedPassword("$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm"),
autoAcceptFolloweeFollowRequest = false,
lastMigration = null,
homeTimelineId = TimelineId(1832779979297918976)
)
val actual = userDetailRepository.findById(UserDetailId(1))
assertEquals(actual, expect)
}
@Test
fun findById_指定したIdで存在しないとnull() = runTest {
assertNull(userDetailRepository.findById(UserDetailId(1)))
}
@Test
fun findAllById_指定されたidすべて返す() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.user_details") {
columns(UserDetails.columns)
values(
1, 1, "$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm", false, null, null
)
values(
2,
2,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
values(
3,
3,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
val userDetailList = userDetailRepository.findAllById(listOf(UserDetailId(1), UserDetailId(3)))
assertThat(userDetailList).hasSize(2)
}
@Test
fun save_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
}.launch()
userDetailRepository.save(
UserDetail.create(
UserDetailId(1),
ActorId(1),
UserDetailHashedPassword("VeeeeeeeeeeeeeryStrongPassword"),
false,
null,
null
)
)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
@Test
fun delete_ドメインイベントがパブリッシュされる() = runTest {
dbSetup(to = dataSource) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.user_details") {
columns(
UserDetails.columns
)
values(
1,
1,
"$2a$10\$EBj3lstVOv0wz3CxLpzYJu8FFrUJ2MPJW9Vlklyg.bfGEOn5sqIwm",
false,
null,
1832779979297918976
)
}
execute(enableReferenceIntegrityConstraints)
}.launch()
userDetailRepository.delete(
UserDetail.create(
UserDetailId(1),
ActorId(1),
UserDetailHashedPassword("VeeeeeeeeeeeeeryStrongPassword"),
false,
null,
null
)
)
TransactionManager.current().commit()
verify(domainEventPublisher, times(1)).publishEvent(any())
}
private fun assertEquals(
actual: UserDetail?, expect: UserDetail
) {
assertNotNull(actual)
kotlin.test.assertEquals(expect, actual)
assertEquals(expect.id, actual.id)
assertEquals(expect.actorId, actual.actorId)
assertEquals(expect.password, actual.password)
assertEquals(expect.autoAcceptFolloweeFollowRequest, actual.autoAcceptFolloweeFollowRequest)
assertEquals(expect.lastMigration, actual.lastMigration)
assertEquals(expect.homeTimelineId, actual.homeTimelineId)
}
}

View File

@ -59,7 +59,7 @@ class UserDetailsServiceImplTest {
fun userDetailが見つからない場合失敗() = runTest {
whenever(actorRepository.findByNameAndDomain(eq("test"), eq("example.com"))).doReturn(
TestActorFactory.create(
actorName = "test", id = 1
id = 1, actorName = "test"
)
)
assertThrows<UsernameNotFoundException> {

View File

@ -0,0 +1,124 @@
package utils
import com.ninja_squad.dbsetup.Operations
import com.ninja_squad.dbsetup.operation.Insert
import com.ninja_squad.dbsetup_kotlin.dbSetup
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.assertj.db.api.TableRowAssert
import org.assertj.db.api.TableRowValueAssert
import org.assertj.db.type.Changes
import org.assertj.db.type.Table
import org.flywaydb.core.Flyway
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.DatabaseConfig
import org.jetbrains.exposed.sql.Transaction
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import java.sql.Connection
import javax.sql.DataSource
abstract class AbstractRepositoryTest(private val exposedTable: org.jetbrains.exposed.sql.Table) {
protected val assertTable: Table
get() {
return Table(dataSource, exposedTable.tableName)
}
protected fun getTable(name: String): Table {
return Table(dataSource, name)
}
private lateinit var transaction: Transaction
protected lateinit var change: Changes
@BeforeEach
fun setUp() {
flyway.clean()
flyway.migrate()
transaction = TransactionManager.manager.newTransaction(Connection.TRANSACTION_READ_UNCOMMITTED)
change = Changes(assertTable)
}
@AfterEach
fun tearDown() {
dbSetup(to = dataSource) {
execute(Operations.sql("SET REFERENTIAL_INTEGRITY TRUE"))
}.launch()
transaction.rollback()
transaction.close()
}
companion object {
lateinit var dataSource: DataSource
lateinit var flyway: Flyway
@JvmStatic
@BeforeAll
fun setup() {
val hikariConfig = HikariConfig()
hikariConfig.jdbcUrl =
"jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;"
hikariConfig.driverClassName = "org.h2.Driver"
hikariConfig.transactionIsolation = "TRANSACTION_READ_UNCOMMITTED"
dataSource = HikariDataSource(hikariConfig)
flyway = Flyway.configure().cleanDisabled(false).dataSource(dataSource).load()
Database.connect(dataSource, databaseConfig = DatabaseConfig {
defaultMaxAttempts = 1
})
flyway.clean()
flyway.migrate()
}
@JvmStatic
@AfterAll
fun clean() {
// flyway.clean()
}
}
}
fun <T> TableRowAssert.value(column: Column<T>): TableRowValueAssert = value(column.name)
fun <T> TableRowValueAssert.value(column: Column<T>): TableRowValueAssert = value(column.name)
fun <T> TableRowAssert.isEqualTo(column: Column<T>, value: T): TableRowValueAssert {
return value(column).isEqualTo(value)
}
fun <T> TableRowValueAssert.isEqualTo(column: Column<T>, value: T): TableRowValueAssert {
return value(column).isEqualTo(value)
}
fun Insert.Builder.columns(columns: List<Column<*>>): Insert.Builder {
columns(*columns.map { it.name }.toTypedArray())
return this
}
fun Insert.Builder.columns(vararg columns: Column<*>): Insert.Builder {
columns(*columns.map { it.name }.toTypedArray())
return this
}
fun Changes.with(block: () -> Unit) {
setStartPointNow()
block()
setEndPointNow()
}
suspend fun Changes.withSuspend(block: suspend () -> Unit) {
setStartPointNow()
block()
setEndPointNow()
}
val disableReferenceIntegrityConstraints = Operations.sql("SET REFERENTIAL_INTEGRITY FALSE")
val enableReferenceIntegrityConstraints = Operations.sql("SET REFERENTIAL_INTEGRITY TRUE")

View File

@ -50,7 +50,7 @@ class ExposedAccountQueryServiceImpl(private val applicationConfig: ApplicationC
id = resultRow[Actors.id].toString(),
username = resultRow[Actors.name],
acct = "${resultRow[Actors.name]}@${resultRow[Actors.domain]}",
url = resultRow[Actors.url],
url = resultRow[Actors.url].toString(),
displayName = resultRow[Actors.screenName],
note = resultRow[Actors.description],
avatar = "$userUrl/icon.jpg",

View File

@ -225,13 +225,13 @@ private fun toStatus(it: ResultRow, queryAlias: QueryAlias, inReplyToAlias: Alia
id = it[Actors.id].toString(),
username = it[Actors.name],
acct = "${it[Actors.name]}@${it[Actors.domain]}",
url = it[Actors.url],
url = it[Actors.url].toString(),
displayName = it[Actors.screenName],
note = it[Actors.description],
avatar = it[Actors.url] + "/icon.jpg",
avatarStatic = it[Actors.url] + "/icon.jpg",
header = it[Actors.url] + "/header.jpg",
headerStatic = it[Actors.url] + "/header.jpg",
avatar = "${it[Actors.url]}/icon.jpg",
avatarStatic = "${it[Actors.url]}/icon.jpg",
header = "${it[Actors.url]}/header.jpg",
headerStatic = "${it[Actors.url]}/header.jpg",
locked = it[Actors.locked],
fields = emptyList(),
emojis = emptyList(),

View File

@ -1,4 +1,5 @@
kotlin.code.style=official
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED
kotlin.compiler.preciseCompilationResultsBackup=true