module 7 lesson 1
This commit is contained in:
@ -36,6 +36,11 @@ muschko = "9.4.0"
|
||||
jvm-compiler = "17"
|
||||
jvm-language = "21"
|
||||
|
||||
uuid = "0.8.4"
|
||||
db-cache = "0.13.0"
|
||||
clickhouse-client = "0.8.1"
|
||||
mockk = "1.13.10"
|
||||
|
||||
[plugins]
|
||||
jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
@ -108,4 +113,12 @@ kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
|
||||
logger-fluentd = { module = "org.fluentd:fluent-logger", version.ref = "fluentd" }
|
||||
|
||||
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
plugin-binaryCompatibilityValidator = "org.jetbrains.kotlinx:binary-compatibility-validator:0.13.2"
|
||||
plugin-binaryCompatibilityValidator = "org.jetbrains.kotlinx:binary-compatibility-validator:0.13.2"
|
||||
|
||||
# DB repo
|
||||
uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }
|
||||
db-cache4k = { module = "io.github.reactivecircus.cache4k:cache4k", version.ref = "db-cache" }
|
||||
|
||||
# DB
|
||||
clickhouse-client = { module = "com.clickhouse:client-v2", version.ref = "clickhouse-client"}
|
||||
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
||||
@ -3,7 +3,7 @@ headers = curl/curl.h
|
||||
|
||||
# You also need to specify linking parameters for different platforms
|
||||
compilerOpts.linux_x64 = -I/usr/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/
|
||||
linkerOpts.osx = -L/opt/homebrew/Cellar/curl/8.11.1/lib -L/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/ -lcurl
|
||||
linkerOpts.osx = -L/opt/homebrew/Cellar/curl/8.13.3/lib -L/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/ -lcurl
|
||||
linkerOpts.linux_x64 = -L/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/ -lcurl
|
||||
|
||||
---
|
||||
|
||||
@ -14,13 +14,13 @@ fun MessengerContext.toLog(logId: String) = CommonLogModel(
|
||||
)
|
||||
|
||||
private fun MessengerContext.toChatLog(): ChatLogModel? {
|
||||
val emptyReport = MessengerChat()
|
||||
val emptyChat = MessengerChat()
|
||||
return ChatLogModel(
|
||||
requestId = requestId.takeIf { it != RequestId.NONE }?.asString(),
|
||||
requestChat = chatRequest.takeIf { it != emptyReport }?.toLog(),
|
||||
requestChat = chatRequest.takeIf { it != emptyChat }?.toLog(),
|
||||
requestSearch = chatFilterRequest.takeIf { it != ChatSearchFilter.NONE }?.toLog(),
|
||||
responseChat = chatResponse.takeIf { it != emptyReport }?.toLog(),
|
||||
responseChats = chatsResponse.takeIf { it.isNotEmpty() }?.filter { it != emptyReport }?.map { it.toLog() },
|
||||
responseChat = chatResponse.takeIf { it != emptyChat }?.toLog(),
|
||||
responseChats = chatsResponse.takeIf { it.isNotEmpty() }?.filter { it != emptyChat }?.map { it.toLog() },
|
||||
).takeIf { it != ChatLogModel() }
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package ru.otus.messenger.api.v1.mappers
|
||||
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.toSet
|
||||
import ru.otus.messenger.api.v1.models.ChatCreateRequestAllOfChat
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMetadata
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
fun MessengerChat.toTransportCreate() = ChatCreateRequestAllOfChat(
|
||||
title = title.takeIf { it.isNotBlank() },
|
||||
description = description.takeIf { it.isNotBlank() },
|
||||
type = type.takeIf { it != ChatType.NONE }?.toTransportChat(),
|
||||
mode = mode.takeIf { it != ChatMode.NONE }?.toTransportChat(),
|
||||
ownerId = ownerId.takeIf { it != ChatOwnerId.NONE }?.asString(),
|
||||
participants = participants.map { it.asString() }.toSet().takeIf { it.isNotEmpty() },
|
||||
metadata = metadata.takeIf { it != ChatMetadata.NONE }?.asString()
|
||||
)
|
||||
|
||||
fun MessengerChat.toTransportRead() = id.takeIf { it != ChatId.NONE }?.asString()
|
||||
|
||||
fun MessengerChat.toTransportDelete() = id.takeIf { it != ChatId.NONE }?.asString()
|
||||
|
||||
private fun ChatType.toTransportChat() = when (this) {
|
||||
ChatType.PRIVATE -> ChatCreateRequestAllOfChat.Type.PRIVATE
|
||||
ChatType.GROUP -> ChatCreateRequestAllOfChat.Type.GROUP
|
||||
ChatType.CHANNEL -> ChatCreateRequestAllOfChat.Type.CHANNEL
|
||||
ChatType.NONE -> null
|
||||
}
|
||||
|
||||
private fun ChatMode.toTransportChat() = when (this) {
|
||||
ChatMode.PERSONAL -> ChatCreateRequestAllOfChat.Mode.PERSONAL
|
||||
ChatMode.WORK -> ChatCreateRequestAllOfChat.Mode.WORK
|
||||
ChatMode.NONE -> null
|
||||
}
|
||||
@ -22,6 +22,8 @@ jib {
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.ktor.server.core)
|
||||
implementation(libs.ktor.server.netty)
|
||||
implementation(libs.ktor.server.cors)
|
||||
@ -45,6 +47,11 @@ dependencies {
|
||||
// stubs
|
||||
implementation(project(":ok-messenger-stubs"))
|
||||
|
||||
// database
|
||||
implementation(project(":ok-messenger-repo-stubs"))
|
||||
implementation(project(":ok-messenger-repo-inmemory"))
|
||||
implementation(project(":ok-messenger-repo-clickhouse"))
|
||||
|
||||
// logging
|
||||
implementation(project(":ok-messenger-api-log-v1"))
|
||||
implementation("ru.otus.messenger.libs:ok-messenger-lib-logging")
|
||||
@ -52,4 +59,6 @@ dependencies {
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(libs.ktor.server.test)
|
||||
testImplementation(libs.ktor.client.negotiation)
|
||||
testImplementation(libs.mockk)
|
||||
testImplementation(project(":ok-messenger-repo-common"))
|
||||
}
|
||||
@ -27,6 +27,7 @@ fun Application.module(
|
||||
anyHost()
|
||||
}
|
||||
configureHTTP()
|
||||
configureMonitoring()
|
||||
configureSerialization()
|
||||
|
||||
configureRouting(appSettings)
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.otus.messenger.app.configs
|
||||
|
||||
import io.ktor.server.config.ApplicationConfig
|
||||
|
||||
data class ClickHouseConfig(
|
||||
val host: String = "localhost",
|
||||
val port: Int = 8443,
|
||||
val user: String = "default",
|
||||
val password: String = "",
|
||||
) {
|
||||
constructor(config: ApplicationConfig): this(
|
||||
host = config.propertyOrNull("$PATH.host")?.getString() ?: "localhost",
|
||||
port = config.propertyOrNull("$PATH.port")?.getString()?.toIntOrNull() ?: 8443,
|
||||
user = config.propertyOrNull("$PATH.user")?.getString() ?: "default",
|
||||
password = config.property("$PATH.password").getString(),
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val PATH = "${ConfigPaths.REPOSITORY}.db"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package ru.otus.messenger.app.configs
|
||||
|
||||
object ConfigPaths {
|
||||
const val ROOT = "messenger"
|
||||
const val REPOSITORY = "$ROOT.repository"
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ru.otus.messenger.app.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import ru.otus.messenger.app.configs.ClickHouseConfig
|
||||
import ru.otus.messenger.app.configs.ConfigPaths
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import ru.otus.messenger.repo.clickhouse.ChatRepoClickHouse
|
||||
import ru.otus.messenger.repo.clickhouse.DbProperties
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.repo.inmemory.ChatRepoInMemory
|
||||
|
||||
fun Application.getDatabaseConf(type: DbType): IRepoChat{
|
||||
val dbSettingPath = "${ConfigPaths.REPOSITORY}.${type.confName}"
|
||||
val dbSetting = environment.config.propertyOrNull(dbSettingPath)?.getString()?.lowercase()
|
||||
return when (dbSetting) {
|
||||
"in-memory", "inmemory", "memory", "mem" -> initInMemory()
|
||||
"db", "clickhouse" -> initClickHouse()
|
||||
else -> throw IllegalArgumentException(
|
||||
"$dbSettingPath must be set in application.yml to one of: 'inmemory', 'clickhouse'"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class DbType(val confName: String) {
|
||||
PROD("prod"),
|
||||
TEST("test")
|
||||
}
|
||||
|
||||
fun Application.initInMemory(): IRepoChat {
|
||||
val ttlSetting = environment.config.propertyOrNull("db.prod")?.getString()?.let {
|
||||
Duration.parse(it)
|
||||
}
|
||||
return ChatRepoInMemory(ttl = ttlSetting ?: 10.minutes)
|
||||
}
|
||||
|
||||
fun Application.initClickHouse(): IRepoChat {
|
||||
val config = ClickHouseConfig(environment.config)
|
||||
return ChatRepoClickHouse(
|
||||
properties = DbProperties(
|
||||
host = config.host,
|
||||
port = config.port,
|
||||
user = config.user,
|
||||
password = config.password,
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -1,14 +1,20 @@
|
||||
package ru.otus.messenger.app.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import ru.otus.messenger.app.base.KtorWsSessionRepo
|
||||
import ru.otus.messenger.app.common.MessengerAppSettings
|
||||
import ru.otus.messenger.app.common.MessengerAppSettingsData
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.repo.stub.ChatRepoStub
|
||||
|
||||
fun Application.initAppSettings(): MessengerAppSettings {
|
||||
val corSettings = MessengerCorSettings(
|
||||
loggerProvider = getLoggerProviderConf(),
|
||||
wsSessions = KtorWsSessionRepo(),
|
||||
repoTest = getDatabaseConf(DbType.TEST),
|
||||
repoProd = getDatabaseConf(DbType.PROD),
|
||||
repoStub = ChatRepoStub(),
|
||||
)
|
||||
return MessengerAppSettingsData(
|
||||
appUrls = environment.config.propertyOrNull("ktor.urls")?.getList() ?: emptyList(),
|
||||
|
||||
@ -7,4 +7,15 @@ ktor:
|
||||
- resources
|
||||
application:
|
||||
modules:
|
||||
- ru.otus.messenger.app.ApplicationKt.module
|
||||
- ru.otus.messenger.app.ApplicationKt.module
|
||||
logger: logback
|
||||
|
||||
messenger:
|
||||
repository:
|
||||
test: "inmemory"
|
||||
prod: "$DB_TYPE_PROD:inmemory"
|
||||
db:
|
||||
host: "$DB_HOST:localhost"
|
||||
port: "$DB_PORT:8443"
|
||||
user: "$DB_USER:default"
|
||||
password: "$DB_PASS:pass"
|
||||
@ -5,13 +5,15 @@ import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.testing.testApplication
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.app.common.MessengerAppSettingsData
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
|
||||
class ApplicationTest {
|
||||
|
||||
@Test
|
||||
fun testRoot() = testApplication {
|
||||
application {
|
||||
module()
|
||||
module(MessengerAppSettingsData(corSettings = MessengerCorSettings()))
|
||||
}
|
||||
client.get("/").apply {
|
||||
assertEquals(HttpStatusCode.Companion.OK, status)
|
||||
|
||||
@ -45,7 +45,7 @@ class ControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun TestApplicationCall.createReport(appSettings: MessengerAppSettings) {
|
||||
private suspend fun TestApplicationCall.createChat(appSettings: MessengerAppSettings) {
|
||||
val response = appSettings.controllerHelper(
|
||||
{ fromTransport(receive<ChatCreateRequest>()) },
|
||||
{ toTransportChat() },
|
||||
@ -57,7 +57,7 @@ class ControllerTest {
|
||||
|
||||
@Test
|
||||
fun ktorHelperTest() = runTest {
|
||||
val testApp = TestApplicationCall(request).apply { createReport(appSettings) }
|
||||
val testApp = TestApplicationCall(request).apply { createChat(appSettings) }
|
||||
val response = testApp.response as ChatCreateResponse
|
||||
assertEquals(ResponseResult.SUCCESS, response.result)
|
||||
}
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
package ru.otus.messenger.app.repo
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.jackson.*
|
||||
import io.ktor.server.testing.*
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
import ru.otus.messenger.app.module
|
||||
import ru.otus.messenger.app.common.MessengerAppSettings
|
||||
import ru.otus.messenger.api.v1.models.ChatCreateResponse
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportCreate
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportDelete
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportRead
|
||||
import ru.otus.messenger.api.v1.models.*
|
||||
|
||||
abstract class V1ChatRepoBaseTest {
|
||||
abstract val workMode: DebugMode
|
||||
abstract val appSettingsCreate: MessengerAppSettings
|
||||
abstract val appSettingsRead: MessengerAppSettings
|
||||
abstract val appSettingsDelete: MessengerAppSettings
|
||||
abstract val appSettingsSearch: MessengerAppSettings
|
||||
abstract val appSettingsResume: MessengerAppSettings
|
||||
|
||||
protected val uuidOld = "10000000-0000-0000-0000-000000000001"
|
||||
protected val uuidNew = "10000000-0000-0000-0000-000000000002"
|
||||
protected val initChat = MessengerChatStub.prepareResult {
|
||||
id = ChatId(uuidOld)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun create() {
|
||||
val chat = initChat.toTransportCreate()
|
||||
v1TestApplication(
|
||||
settings = appSettingsCreate,
|
||||
endpoint = "create",
|
||||
request = ChatCreateRequest(
|
||||
chat = chat,
|
||||
debug = Debug(mode = workMode),
|
||||
),
|
||||
) { response ->
|
||||
val responseObj = response.body<ChatCreateResponse>()
|
||||
assertEquals(200, response.status.value)
|
||||
assertEquals(uuidNew, responseObj.chat?.id)
|
||||
assertEquals(chat.ownerId, responseObj.chat?.ownerId)
|
||||
assertEquals(chat.title, responseObj.chat?.title)
|
||||
assertEquals(chat.description, responseObj.chat?.description)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun read() {
|
||||
val chat = initChat.toTransportRead()
|
||||
v1TestApplication(
|
||||
settings = appSettingsRead,
|
||||
endpoint = "read",
|
||||
request = ChatReadRequest(
|
||||
chatId = chat,
|
||||
debug = Debug(mode = workMode),
|
||||
),
|
||||
) { response ->
|
||||
val responseObj = response.body<IResponse>() as ChatReadResponse
|
||||
assertEquals(200, response.status.value)
|
||||
assertEquals(uuidOld, responseObj.chat?.id)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delete() {
|
||||
val chat = initChat.toTransportDelete()
|
||||
v1TestApplication(
|
||||
settings = appSettingsDelete,
|
||||
endpoint = "delete",
|
||||
request = ChatDeleteRequest(
|
||||
chatId = chat,
|
||||
debug = Debug(mode = workMode),
|
||||
),
|
||||
) { response ->
|
||||
val responseObj = response.body<ChatDeleteResponse>()
|
||||
assertEquals(200, response.status.value)
|
||||
assertEquals(ResponseResult.SUCCESS, responseObj.result)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search() = v1TestApplication(
|
||||
settings = appSettingsSearch,
|
||||
endpoint = "search",
|
||||
request = ChatSearchRequest(
|
||||
criteria = ChatSearchRequestAllOfCriteria(),
|
||||
debug = Debug(mode = workMode),
|
||||
),
|
||||
) { response ->
|
||||
val responseObj = response.body<ChatSearchResponse>()
|
||||
assertEquals(200, response.status.value)
|
||||
assertNotEquals(0, responseObj.chats?.size)
|
||||
assertEquals(uuidOld, responseObj.chats?.first()?.id)
|
||||
}
|
||||
|
||||
private inline fun <reified T: IRequest> v1TestApplication(
|
||||
settings: MessengerAppSettings,
|
||||
endpoint: String,
|
||||
request: T,
|
||||
crossinline function: suspend (HttpResponse) -> Unit,
|
||||
): Unit = testApplication {
|
||||
application { module(appSettings = settings) }
|
||||
val client = createClient {
|
||||
install(ContentNegotiation) {
|
||||
jackson()
|
||||
}
|
||||
}
|
||||
val response = client.post("/v1/chat/$endpoint") {
|
||||
contentType(ContentType.Application.Json)
|
||||
header("X-Trace-Id", "12345")
|
||||
setBody(request)
|
||||
}
|
||||
function(response)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package ru.otus.messenger.app.repo
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.app.common.MessengerAppSettings
|
||||
import ru.otus.messenger.app.common.MessengerAppSettingsData
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.repo.clickhouse.ChatRepoClickHouse
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
class V1ChatRepoClickHouseTest : V1ChatRepoBaseTest() {
|
||||
override val workMode: DebugMode = DebugMode.TEST
|
||||
private fun appSettings(repo: IRepoChat) = MessengerAppSettingsData(
|
||||
corSettings = MessengerCorSettings(
|
||||
repoTest = repo
|
||||
)
|
||||
)
|
||||
private val mockkRepo = mockk<ChatRepoClickHouse> {
|
||||
every { save(any()) } returnsArgument(0)
|
||||
coEvery { createChat(any()) } returns DbChatResponseOk(
|
||||
MessengerChatStub.prepareResult {
|
||||
id = ChatId(uuidNew)
|
||||
}
|
||||
)
|
||||
coEvery { readChat(any()) } returns DbChatResponseOk(
|
||||
MessengerChatStub.prepareResult {
|
||||
id = ChatId(uuidOld)
|
||||
}
|
||||
)
|
||||
coEvery { deleteChat(any()) } returns DbChatResponseOk(
|
||||
MessengerChatStub.prepareResult {
|
||||
id = ChatId(uuidOld)
|
||||
}
|
||||
)
|
||||
coEvery { searchChat(any()) } returns DbChatsResponseOk(
|
||||
listOf(
|
||||
MessengerChatStub.prepareResult { id = ChatId(uuidOld) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override val appSettingsCreate: MessengerAppSettings = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
mockkRepo
|
||||
)
|
||||
)
|
||||
override val appSettingsRead: MessengerAppSettings = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
mockkRepo,
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
override val appSettingsDelete: MessengerAppSettings = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
mockkRepo,
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
override val appSettingsSearch: MessengerAppSettings = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
mockkRepo,
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
override val appSettingsResume: MessengerAppSettings = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
mockkRepo,
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ru.otus.messenger.app.repo
|
||||
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.app.common.MessengerAppSettingsData
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.inmemory.ChatRepoInMemory
|
||||
|
||||
class V1ChatRepoInmemoryTest : V1ChatRepoBaseTest() {
|
||||
override val workMode: DebugMode = DebugMode.TEST
|
||||
private fun appSettings(repo: IRepoChat) = MessengerAppSettingsData(
|
||||
corSettings = MessengerCorSettings(
|
||||
repoTest = repo
|
||||
)
|
||||
)
|
||||
|
||||
override val appSettingsCreate: MessengerAppSettingsData = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(randomUuid = { uuidNew })
|
||||
)
|
||||
)
|
||||
override val appSettingsRead: MessengerAppSettingsData = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(randomUuid = { uuidNew }),
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
override val appSettingsDelete: MessengerAppSettingsData = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(randomUuid = { uuidNew }),
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
override val appSettingsSearch: MessengerAppSettingsData = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(randomUuid = { uuidNew }),
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
override val appSettingsResume: MessengerAppSettingsData = appSettings(
|
||||
repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(randomUuid = { uuidNew }),
|
||||
initObjects = listOf(initChat),
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -13,4 +13,6 @@ dependencies {
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(libs.kotlin.coroutines.test)
|
||||
testImplementation(project(":ok-messenger-repo-tests"))
|
||||
testImplementation(project(":ok-messenger-repo-inmemory"))
|
||||
}
|
||||
@ -1,9 +1,20 @@
|
||||
package ru.otus.messenger.biz
|
||||
|
||||
import ru.otus.messenger.biz.general.initRepo
|
||||
import ru.otus.messenger.biz.general.initStatus
|
||||
import ru.otus.messenger.biz.general.operation
|
||||
import ru.otus.messenger.biz.general.prepareResult
|
||||
import ru.otus.messenger.biz.general.stubs
|
||||
import ru.otus.messenger.biz.general.validation
|
||||
import ru.otus.messenger.biz.repo.checkLock
|
||||
import ru.otus.messenger.biz.repo.repoCreate
|
||||
import ru.otus.messenger.biz.repo.repoDelete
|
||||
import ru.otus.messenger.biz.repo.repoPrepareCreate
|
||||
import ru.otus.messenger.biz.repo.repoPrepareDelete
|
||||
import ru.otus.messenger.biz.repo.repoPrepareUpdate
|
||||
import ru.otus.messenger.biz.repo.repoRead
|
||||
import ru.otus.messenger.biz.repo.repoSearch
|
||||
import ru.otus.messenger.biz.repo.repoUpdate
|
||||
import ru.otus.messenger.biz.stubs.stubCreateSuccess
|
||||
import ru.otus.messenger.biz.stubs.stubReadSuccess
|
||||
import ru.otus.messenger.biz.stubs.stubUpdateSuccess
|
||||
@ -27,6 +38,8 @@ import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.cor.dsl.chain
|
||||
import ru.otus.messenger.cor.dsl.rootChain
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
@ -39,6 +52,7 @@ class MessengerProcessor(
|
||||
|
||||
private val businessChain = rootChain<MessengerContext> {
|
||||
initStatus("Инициализация статуса")
|
||||
initRepo("Инициализация репозитория")
|
||||
|
||||
operation("Создание чата", ChatCommand.CREATE) {
|
||||
stubs("Обработка стабов") {
|
||||
@ -61,6 +75,13 @@ class MessengerProcessor(
|
||||
|
||||
finishChatValidation("Завершение проверок")
|
||||
}
|
||||
|
||||
chain {
|
||||
title = "Логика сохранения"
|
||||
repoPrepareCreate("Подготовка объекта для сохранения")
|
||||
repoCreate("Создание чата в БД")
|
||||
}
|
||||
prepareResult("Подготовка ответа")
|
||||
}
|
||||
|
||||
operation("Получить чат", ChatCommand.READ) {
|
||||
@ -79,6 +100,17 @@ class MessengerProcessor(
|
||||
|
||||
finishChatValidation("Успешное завершение процедуры валидации")
|
||||
}
|
||||
|
||||
chain {
|
||||
title = "Логика чтения"
|
||||
repoRead("Чтение чата из БД")
|
||||
worker {
|
||||
title = "Подготовка ответа для Read"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle { chatRepoDone = chatRepoRead }
|
||||
}
|
||||
}
|
||||
prepareResult("Подготовка ответа")
|
||||
}
|
||||
|
||||
operation("Изменить чат", ChatCommand.UPDATE) {
|
||||
@ -105,6 +137,15 @@ class MessengerProcessor(
|
||||
|
||||
finishChatValidation("Успешное завершение процедуры валидации")
|
||||
}
|
||||
|
||||
chain {
|
||||
title = "Логика сохранения"
|
||||
repoRead("Чтение чата из БД")
|
||||
checkLock("Проверяем консистентность по оптимистичной блокировке")
|
||||
repoPrepareUpdate("Подготовка объекта для обновления")
|
||||
repoUpdate("Обновление чата в БД")
|
||||
}
|
||||
prepareResult("Подготовка ответа")
|
||||
}
|
||||
|
||||
operation("Удалить чат", ChatCommand.DELETE) {
|
||||
@ -124,6 +165,15 @@ class MessengerProcessor(
|
||||
validateIdProperFormat("Проверка формата id")
|
||||
finishChatValidation("Успешное завершение процедуры валидации")
|
||||
}
|
||||
|
||||
chain {
|
||||
title = "Логика удаления"
|
||||
repoRead("Чтение чата из БД")
|
||||
checkLock("Проверяем консистентность по оптимистичной блокировке")
|
||||
repoPrepareDelete("Подготовка объекта для удаления")
|
||||
repoDelete("Удаление чата из БД")
|
||||
}
|
||||
prepareResult("Подготовка ответа")
|
||||
}
|
||||
|
||||
operation("Поиск чата", ChatCommand.SEARCH) {
|
||||
@ -140,6 +190,9 @@ class MessengerProcessor(
|
||||
|
||||
finishChatFilterValidation("Успешное завершение процедуры валидации")
|
||||
}
|
||||
|
||||
repoSearch("Поиск чата в БД по фильтру")
|
||||
prepareResult("Подготовка ответа")
|
||||
}
|
||||
}.build()
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.biz.exception
|
||||
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
|
||||
class DbNotConfiguredException(val workMode: WorkMode): Exception(
|
||||
"Database is not configured properly for work mode $workMode"
|
||||
)
|
||||
@ -0,0 +1,28 @@
|
||||
package ru.otus.messenger.biz.general
|
||||
|
||||
import ru.otus.messenger.biz.exception.DbNotConfiguredException
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.helpers.errorSystem
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.initRepo(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Estimate main working repo depending on work mode".trimIndent()
|
||||
handle {
|
||||
chatRepo = when (workMode) {
|
||||
WorkMode.TEST -> corSettings.repoTest
|
||||
WorkMode.STUB -> corSettings.repoStub
|
||||
else -> corSettings.repoProd
|
||||
}
|
||||
if (workMode != WorkMode.STUB && chatRepo == IRepoChat.NONE) fail(
|
||||
errorSystem(
|
||||
violationCode = "dbNotConfigured",
|
||||
e = DbNotConfiguredException(workMode)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.otus.messenger.biz.general
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.prepareResult(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Подготовка данных для ответа клиенту на запрос"
|
||||
on { workMode != WorkMode.STUB }
|
||||
handle {
|
||||
chatResponse = chatRepoDone
|
||||
chatsResponse = chatsRepoDone
|
||||
state = when (val st = state) {
|
||||
ChatState.RUNNING -> ChatState.FINISHING
|
||||
else -> st
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErrWithData
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoCreate(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Добавление объявления в БД"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
val request = DbChatRequest(chatRepoPrepare)
|
||||
when(val result = chatRepo.createChat(request)) {
|
||||
is DbChatResponseOk -> chatRepoDone = result.data
|
||||
is DbChatResponseErr -> fail(result.errors)
|
||||
is DbChatResponseErrWithData -> fail(result.errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErrWithData
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoDelete(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Удаление объявления из БД по ID"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
val request = DbChatIdRequest(chatRepoPrepare)
|
||||
when(val result = chatRepo.deleteChat(request)) {
|
||||
is DbChatResponseOk -> chatRepoDone = result.data
|
||||
is DbChatResponseErr -> {
|
||||
fail(result.errors)
|
||||
chatRepoDone = chatRepoRead
|
||||
}
|
||||
is DbChatResponseErrWithData -> {
|
||||
fail(result.errors)
|
||||
chatRepoDone = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoPrepareCreate(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Подготовка объекта к сохранению в базе данных"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
chatRepoPrepare = chatValidated.deepCopy()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoPrepareDelete(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Готовим данные к удалению из БД".trimIndent()
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
chatRepoPrepare = chatValidated.deepCopy()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoPrepareUpdate(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Готовим данные к сохранению в БД: совмещаем данные, прочитанные из БД, " +
|
||||
"и данные, полученные от пользователя"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
chatRepoPrepare = chatRepoRead.deepCopy().apply {
|
||||
this.title = chatValidated.title
|
||||
description = chatValidated.description
|
||||
type = chatValidated.type
|
||||
mode = chatValidated.mode
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErrWithData
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoRead(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Chat reading from DB"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
val request = DbChatIdRequest(chatValidated)
|
||||
when(val result = chatRepo.readChat(request)) {
|
||||
is DbChatResponseOk -> chatRepoRead = result.data
|
||||
is DbChatResponseErr -> fail(result.errors)
|
||||
is DbChatResponseErrWithData -> {
|
||||
fail(result.errors)
|
||||
chatRepoRead = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoSearch(title: String) = worker {
|
||||
this.title = title
|
||||
description = "Search for chats in DB using filters"
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
val request = DbChatFilterRequest(
|
||||
ownerId = chatFilterValidated.ownerId,
|
||||
chatType = chatFilterValidated.type,
|
||||
chatMode = chatFilterValidated.mode,
|
||||
searchFields = chatFilterValidated.searchFields,
|
||||
)
|
||||
when (val result = chatRepo.searchChat(request)) {
|
||||
is DbChatsResponseOk -> chatsRepoDone = result.data.toMutableList()
|
||||
is DbChatsResponseErr -> fail(result.errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErrWithData
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.repoUpdate(title: String) = worker {
|
||||
this.title = title
|
||||
on { state == ChatState.RUNNING }
|
||||
handle {
|
||||
val request = DbChatRequest(chatRepoPrepare)
|
||||
when(val result = chatRepo.updateChat(request)) {
|
||||
is DbChatResponseOk -> chatRepoDone = result.data
|
||||
is DbChatResponseErr -> fail(result.errors)
|
||||
is DbChatResponseErrWithData -> {
|
||||
fail(result.errors)
|
||||
chatRepoDone = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.repo.errorRepoConcurrency
|
||||
import ru.otus.messenger.common.helpers.fail
|
||||
import ru.otus.messenger.cor.dsl.ICorChainDsl
|
||||
import ru.otus.messenger.cor.dsl.worker
|
||||
|
||||
fun ICorChainDsl<MessengerContext>.checkLock(title: String) = worker {
|
||||
this.title = title
|
||||
description = """
|
||||
Проверка оптимистичной блокировки. Если не равна сохраненной в БД, значит данные запроса устарели
|
||||
и необходимо их обновить вручную
|
||||
""".trimIndent()
|
||||
on { state == ChatState.RUNNING && chatValidated.id != chatRepoRead.id }
|
||||
handle {
|
||||
fail(errorRepoConcurrency(chatRepoRead).errors)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.repo.tests.ChatRepositoryMock
|
||||
|
||||
class BizRepoCreateTest {
|
||||
|
||||
private val userId = ChatOwnerId("321")
|
||||
private val command = ChatCommand.CREATE
|
||||
private val uuid = "10000000-0000-0000-0000-000000000001"
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeCreateChat = {
|
||||
DbChatResponseOk(
|
||||
data = MessengerChat(
|
||||
id = ChatId(uuid),
|
||||
title = it.chat.title,
|
||||
description = it.chat.description,
|
||||
ownerId = userId,
|
||||
type = it.chat.type,
|
||||
mode = it.chat.mode,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
private val settings = MessengerCorSettings(
|
||||
repoTest = repo
|
||||
)
|
||||
private val processor = MessengerProcessor(settings)
|
||||
|
||||
@Test
|
||||
fun repoCreateSuccessTest() = runTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
workMode = WorkMode.TEST,
|
||||
chatRequest = MessengerChat(
|
||||
title = "abc",
|
||||
description = "abc",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
),
|
||||
)
|
||||
processor.exec(ctx)
|
||||
assertEquals(ChatState.FINISHING, ctx.state)
|
||||
assertNotEquals(ChatId.NONE, ctx.chatResponse.id)
|
||||
assertEquals("abc", ctx.chatResponse.title)
|
||||
assertEquals("abc", ctx.chatResponse.description)
|
||||
assertEquals(ChatType.GROUP, ctx.chatResponse.type)
|
||||
assertEquals(ChatMode.WORK, ctx.chatResponse.mode)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.repo.tests.ChatRepositoryMock
|
||||
|
||||
class BizRepoDeleteTest {
|
||||
|
||||
private val userId = ChatOwnerId("321")
|
||||
private val command = ChatCommand.DELETE
|
||||
private val initAd = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "abc",
|
||||
description = "abc",
|
||||
ownerId = userId,
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeReadChat = {
|
||||
DbChatResponseOk(
|
||||
data = initAd,
|
||||
)
|
||||
},
|
||||
invokeDeleteChat = {
|
||||
if (it.id == initAd.id)
|
||||
DbChatResponseOk(
|
||||
data = initAd
|
||||
)
|
||||
else DbChatResponseErr()
|
||||
}
|
||||
)
|
||||
private val settings by lazy {
|
||||
MessengerCorSettings(
|
||||
repoTest = repo
|
||||
)
|
||||
}
|
||||
private val processor = MessengerProcessor(settings)
|
||||
|
||||
@Test
|
||||
fun repoDeleteSuccessTest() = runTest {
|
||||
val chatToUpdate = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
)
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
workMode = WorkMode.TEST,
|
||||
chatRequest = chatToUpdate,
|
||||
)
|
||||
processor.exec(ctx)
|
||||
assertEquals(ChatState.FINISHING, ctx.state)
|
||||
assertTrue { ctx.errors.isEmpty() }
|
||||
assertEquals(initAd.id, ctx.chatResponse.id)
|
||||
assertEquals(initAd.title, ctx.chatResponse.title)
|
||||
assertEquals(initAd.description, ctx.chatResponse.description)
|
||||
assertEquals(initAd.type, ctx.chatResponse.type)
|
||||
assertEquals(initAd.mode, ctx.chatResponse.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun repoDeleteNotFoundTest() = repoNotFoundTest(command)
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.repo.tests.ChatRepositoryMock
|
||||
|
||||
class BizRepoReadTest {
|
||||
|
||||
private val userId = ChatOwnerId("321")
|
||||
private val command = ChatCommand.READ
|
||||
private val initAd = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "abc",
|
||||
description = "abc",
|
||||
ownerId = userId,
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeReadChat = {
|
||||
DbChatResponseOk(
|
||||
data = initAd,
|
||||
)
|
||||
}
|
||||
)
|
||||
private val settings = MessengerCorSettings(repoTest = repo)
|
||||
private val processor = MessengerProcessor(settings)
|
||||
|
||||
@Test
|
||||
fun repoReadSuccessTest() = runTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
workMode = WorkMode.TEST,
|
||||
chatRequest = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
),
|
||||
)
|
||||
processor.exec(ctx)
|
||||
assertEquals(ChatState.FINISHING, ctx.state)
|
||||
assertEquals(initAd.id, ctx.chatResponse.id)
|
||||
assertEquals(initAd.title, ctx.chatResponse.title)
|
||||
assertEquals(initAd.description, ctx.chatResponse.description)
|
||||
assertEquals(initAd.type, ctx.chatResponse.type)
|
||||
assertEquals(initAd.mode, ctx.chatResponse.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun repoReadNotFoundTest() = repoNotFoundTest(command)
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatSearchFilter
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.repo.tests.ChatRepositoryMock
|
||||
|
||||
class BizRepoSearchTest {
|
||||
|
||||
private val userId = ChatOwnerId("321")
|
||||
private val command = ChatCommand.SEARCH
|
||||
private val initAd = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "abc",
|
||||
description = "abc",
|
||||
ownerId = userId,
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeSearchChat = {
|
||||
DbChatsResponseOk(
|
||||
data = listOf(initAd),
|
||||
)
|
||||
}
|
||||
)
|
||||
private val settings = MessengerCorSettings(repoTest = repo)
|
||||
private val processor = MessengerProcessor(settings)
|
||||
|
||||
@Test
|
||||
fun repoSearchSuccessTest() = runTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
workMode = WorkMode.TEST,
|
||||
chatFilterRequest = ChatSearchFilter(
|
||||
searchFields = listOf(
|
||||
ChatSearchFilter.StringSearchField(
|
||||
fieldName = "ownerId",
|
||||
stringValue = userId.asString()
|
||||
)
|
||||
),
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
),
|
||||
)
|
||||
processor.exec(ctx)
|
||||
assertEquals(ChatState.FINISHING, ctx.state)
|
||||
assertEquals(1, ctx.chatsResponse.size)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.repo.tests.ChatRepositoryMock
|
||||
|
||||
class BizRepoUpdateTest {
|
||||
|
||||
private val userId = ChatOwnerId("321")
|
||||
private val command = ChatCommand.UPDATE
|
||||
private val initAd = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "abc",
|
||||
description = "abc",
|
||||
ownerId = userId,
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeReadChat = {
|
||||
DbChatResponseOk(
|
||||
data = initAd,
|
||||
)
|
||||
},
|
||||
invokeUpdateChat = {
|
||||
DbChatResponseOk(
|
||||
data = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "xyz",
|
||||
description = "xyz",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
private val settings = MessengerCorSettings(repoTest = repo)
|
||||
private val processor = MessengerProcessor(settings)
|
||||
|
||||
@Test
|
||||
fun repoUpdateSuccessTest() = runTest {
|
||||
val chatToUpdate = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "xyz",
|
||||
description = "xyz",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
workMode = WorkMode.TEST,
|
||||
chatRequest = chatToUpdate,
|
||||
)
|
||||
processor.exec(ctx)
|
||||
assertEquals(ChatState.FINISHING, ctx.state)
|
||||
assertEquals(chatToUpdate.id, ctx.chatResponse.id)
|
||||
assertEquals(chatToUpdate.title, ctx.chatResponse.title)
|
||||
assertEquals(chatToUpdate.description, ctx.chatResponse.description)
|
||||
assertEquals(chatToUpdate.type, ctx.chatResponse.type)
|
||||
assertEquals(chatToUpdate.mode, ctx.chatResponse.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun repoUpdateNotFoundTest() = repoNotFoundTest(command)
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package ru.otus.messenger.biz.repo
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.errorNotFound
|
||||
import ru.otus.messenger.repo.tests.ChatRepositoryMock
|
||||
|
||||
private val initAd = MessengerChat(
|
||||
id = ChatId("123"),
|
||||
title = "abc",
|
||||
description = "abc",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
)
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeReadChat = {
|
||||
if (it.id == initAd.id) {
|
||||
DbChatResponseOk(
|
||||
data = initAd,
|
||||
)
|
||||
} else errorNotFound(it.id)
|
||||
}
|
||||
)
|
||||
private val settings = MessengerCorSettings(repoTest = repo)
|
||||
private val processor = MessengerProcessor(settings)
|
||||
|
||||
fun repoNotFoundTest(command: ChatCommand) = runTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
workMode = WorkMode.TEST,
|
||||
chatRequest = MessengerChat(
|
||||
id = ChatId("12345"),
|
||||
title = "xyz",
|
||||
description = "xyz",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
),
|
||||
)
|
||||
processor.exec(ctx)
|
||||
assertEquals(ChatState.FAILING, ctx.state)
|
||||
assertEquals(MessengerChat(), ctx.chatResponse)
|
||||
assertEquals(1, ctx.errors.size)
|
||||
assertNotNull(ctx.errors.find { it.code == "repo-not-found" }, "Errors must contain not-found")
|
||||
}
|
||||
@ -3,9 +3,19 @@ package ru.otus.messenger.biz.validation
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerCorSettings
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.inmemory.ChatRepoInMemory
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
abstract class BaseBizValidationTest {
|
||||
protected abstract val command: ChatCommand
|
||||
private val settings by lazy { MessengerCorSettings() }
|
||||
private val repo = ChatRepoInitialized(
|
||||
repo = ChatRepoInMemory(),
|
||||
initObjects = listOf(
|
||||
MessengerChatStub.get(),
|
||||
),
|
||||
)
|
||||
private val settings by lazy { MessengerCorSettings(repoTest = repo) }
|
||||
protected val processor by lazy { MessengerProcessor(settings) }
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package ru.otus.messenger.biz.validation
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun runBizTest(block: suspend () -> Unit) = runTest {
|
||||
withContext(Dispatchers.Default.limitedParallelism(1)) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,6 @@ package ru.otus.messenger.biz.validation
|
||||
import kotlin.test.assertContains
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
@ -11,7 +10,7 @@ import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
fun validationDescriptionCorrect(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationDescriptionCorrect(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -25,7 +24,7 @@ fun validationDescriptionCorrect(command: ChatCommand, processor: MessengerProce
|
||||
assertContains(ctx.chatValidated.description, "description")
|
||||
}
|
||||
|
||||
fun validationDescriptionTrim(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationDescriptionTrim(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -40,7 +39,7 @@ fun validationDescriptionTrim(command: ChatCommand, processor: MessengerProcesso
|
||||
assertEquals("abc", ctx.chatValidated.description)
|
||||
}
|
||||
|
||||
fun validationDescriptionEmpty(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationDescriptionEmpty(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -57,7 +56,7 @@ fun validationDescriptionEmpty(command: ChatCommand, processor: MessengerProcess
|
||||
assertContains(error?.message ?: "", "description")
|
||||
}
|
||||
|
||||
fun validationDescriptionSymbols(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationDescriptionSymbols(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
|
||||
@ -3,7 +3,6 @@ package ru.otus.messenger.biz.validation
|
||||
import kotlin.test.assertContains
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
@ -12,7 +11,7 @@ import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
fun validationIdCorrect(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationIdCorrect(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -24,7 +23,7 @@ fun validationIdCorrect(command: ChatCommand, processor: MessengerProcessor) = r
|
||||
assertNotEquals(ChatState.FAILING, ctx.state)
|
||||
}
|
||||
|
||||
fun validationIdTrim(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationIdTrim(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -38,7 +37,7 @@ fun validationIdTrim(command: ChatCommand, processor: MessengerProcessor) = runT
|
||||
assertNotEquals(ChatState.FAILING, ctx.state)
|
||||
}
|
||||
|
||||
fun validationIdEmpty(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationIdEmpty(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -55,7 +54,7 @@ fun validationIdEmpty(command: ChatCommand, processor: MessengerProcessor) = run
|
||||
assertContains(error?.message ?: "", "id")
|
||||
}
|
||||
|
||||
fun validationIdFormat(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationIdFormat(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
|
||||
@ -3,7 +3,6 @@ package ru.otus.messenger.biz.validation
|
||||
import kotlin.test.assertContains
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import ru.otus.messenger.biz.MessengerProcessor
|
||||
import ru.otus.messenger.common.MessengerContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
@ -11,7 +10,7 @@ import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
fun validationTitleCorrect(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationTitleCorrect(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -21,12 +20,13 @@ fun validationTitleCorrect(command: ChatCommand, processor: MessengerProcessor)
|
||||
},
|
||||
)
|
||||
processor.exec(ctx)
|
||||
println(ctx.errors.joinToString("\n"))
|
||||
assertEquals(0, ctx.errors.size)
|
||||
assertNotEquals(ChatState.FAILING, ctx.state)
|
||||
assertEquals("abc", ctx.chatValidated.title)
|
||||
}
|
||||
|
||||
fun validationTitleTrim(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationTitleTrim(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -41,7 +41,7 @@ fun validationTitleTrim(command: ChatCommand, processor: MessengerProcessor) = r
|
||||
assertEquals("abc", ctx.chatValidated.title)
|
||||
}
|
||||
|
||||
fun validationTitleEmpty(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationTitleEmpty(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
@ -58,7 +58,7 @@ fun validationTitleEmpty(command: ChatCommand, processor: MessengerProcessor) =
|
||||
assertContains(error?.message ?: "", "title")
|
||||
}
|
||||
|
||||
fun validationTitleSymbols(command: ChatCommand, processor: MessengerProcessor) = runTest {
|
||||
fun validationTitleSymbols(command: ChatCommand, processor: MessengerProcessor) = runBizTest {
|
||||
val ctx = MessengerContext(
|
||||
command = command,
|
||||
state = ChatState.NONE,
|
||||
|
||||
@ -2,9 +2,6 @@ plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDir("src/commonMain/kotlin")
|
||||
@ -13,8 +10,10 @@ sourceSets {
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.datetime)
|
||||
api(libs.kotlin.datetime)
|
||||
implementation(libs.kotlinx.serialization.core)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlin.coroutines)
|
||||
api("ru.otus.messenger.libs:ok-messenger-lib-logging")
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
|
||||
@ -2,6 +2,7 @@ package ru.otus.messenger.common
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import ru.otus.messenger.common.models.*
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.common.stubs.MessengerStubs
|
||||
import ru.otus.messenger.common.ws.IMessengerWsSession
|
||||
|
||||
@ -17,15 +18,27 @@ data class MessengerContext(
|
||||
|
||||
var requestId: RequestId = RequestId.NONE,
|
||||
var timeStart: Instant = Instant.NONE,
|
||||
|
||||
// objects from request
|
||||
var chatRequest: MessengerChat = MessengerChat(),
|
||||
var chatFilterRequest: ChatSearchFilter = ChatSearchFilter.NONE,
|
||||
|
||||
// objects during validation process
|
||||
var chatValidating: MessengerChat = MessengerChat(),
|
||||
var chatFilterValidating: ChatSearchFilter = ChatSearchFilter.NONE,
|
||||
|
||||
// objects after validation
|
||||
var chatValidated: MessengerChat = MessengerChat(),
|
||||
var chatFilterValidated: ChatSearchFilter = ChatSearchFilter.NONE,
|
||||
|
||||
// objects during requests to DB
|
||||
var chatRepo: IRepoChat = IRepoChat.NONE,
|
||||
var chatRepoRead: MessengerChat = MessengerChat(), // object, read from repo
|
||||
var chatRepoPrepare: MessengerChat = MessengerChat(), // prepare to save to DB
|
||||
var chatRepoDone: MessengerChat = MessengerChat(), // result from DB
|
||||
var chatsRepoDone: MutableList<MessengerChat> = mutableListOf(),
|
||||
|
||||
// objects to send to client
|
||||
var chatResponse: MessengerChat = MessengerChat(),
|
||||
var chatsResponse: MutableList<MessengerChat> = mutableListOf(),
|
||||
)
|
||||
@ -1,11 +1,15 @@
|
||||
package ru.otus.messenger.common
|
||||
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.common.ws.IMessengerWsSessionRepo
|
||||
import ru.otus.messenger.logging.common.LoggerProvider
|
||||
|
||||
data class MessengerCorSettings(
|
||||
val loggerProvider: LoggerProvider = LoggerProvider(),
|
||||
val wsSessions: IMessengerWsSessionRepo = IMessengerWsSessionRepo.NONE,
|
||||
val repoStub: IRepoChat = IRepoChat.NONE,
|
||||
val repoTest: IRepoChat = IRepoChat.NONE,
|
||||
val repoProd: IRepoChat = IRepoChat.NONE,
|
||||
) {
|
||||
companion object {
|
||||
val NONE = MessengerCorSettings()
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package ru.otus.messenger.common.exceptions
|
||||
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
|
||||
open class RepoChatException(
|
||||
@Suppress("unused")
|
||||
val chatId: ChatId,
|
||||
msg: String,
|
||||
): RepoException(msg)
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.messenger.common.exceptions
|
||||
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
|
||||
class RepoConcurrencyException(id: ChatId): RepoChatException(
|
||||
id,
|
||||
"Expected lock while actual lock in db"
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.messenger.common.exceptions
|
||||
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
|
||||
class RepoEmptyLockException(id: ChatId): RepoChatException(
|
||||
id,
|
||||
"Lock is empty in DB"
|
||||
)
|
||||
@ -0,0 +1,3 @@
|
||||
package ru.otus.messenger.common.exceptions
|
||||
|
||||
open class RepoException(msg: String): Exception(msg)
|
||||
@ -5,8 +5,14 @@ import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
|
||||
fun MessengerContext.addError(vararg error: ChatError) = errors.addAll(error)
|
||||
fun MessengerContext.addErrors(error: Collection<ChatError>) = errors.addAll(error)
|
||||
|
||||
fun MessengerContext.fail(error: ChatError) {
|
||||
addError(error)
|
||||
state = ChatState.FAILING
|
||||
}
|
||||
|
||||
fun MessengerContext.fail(errors: Collection<ChatError>) {
|
||||
addErrors(errors)
|
||||
state = ChatState.FAILING
|
||||
}
|
||||
|
||||
@ -26,4 +26,16 @@ fun errorValidation(
|
||||
group = "validation",
|
||||
message = "Validation error for field $field: $description",
|
||||
level = level,
|
||||
)
|
||||
|
||||
fun errorSystem(
|
||||
violationCode: String,
|
||||
level: LogLevel = LogLevel.ERROR,
|
||||
e: Throwable,
|
||||
) = ChatError(
|
||||
code = "system-$violationCode",
|
||||
group = "system",
|
||||
message = "System error occurred. Our stuff has been informed, please retry later",
|
||||
level = level,
|
||||
exception = e,
|
||||
)
|
||||
@ -0,0 +1,29 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import ru.otus.messenger.common.helpers.errorSystem
|
||||
|
||||
abstract class ChatRepoBase : IRepoChat {
|
||||
|
||||
protected suspend fun tryChatMethod(timeout: Duration = 10.seconds, ctx: CoroutineContext = Dispatchers.IO, block: suspend () -> IDbChatResponse) = try {
|
||||
withTimeout(timeout) {
|
||||
withContext(ctx) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
DbChatResponseErr(errorSystem("methodException", e = e))
|
||||
}
|
||||
|
||||
protected suspend fun tryChatsMethod(block: suspend () -> IDbChatsResponse) = try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
DbChatsResponseErr(errorSystem("methodException", e = e))
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatSearchFilter
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
|
||||
data class DbChatFilterRequest(
|
||||
val searchFields: List<ChatSearchFilter.SearchField> = emptyList(),
|
||||
val ownerId: ChatOwnerId = ChatOwnerId.NONE,
|
||||
val chatType: ChatType = ChatType.NONE,
|
||||
val chatMode: ChatMode = ChatMode.NONE,
|
||||
)
|
||||
@ -0,0 +1,10 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
data class DbChatIdRequest(
|
||||
val id: ChatId,
|
||||
) {
|
||||
constructor(chat: MessengerChat): this(chat.id)
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
data class DbChatRequest(
|
||||
val chat: MessengerChat
|
||||
)
|
||||
@ -0,0 +1,9 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
|
||||
data class DbChatResponseErr(
|
||||
val errors: List<ChatError> = emptyList()
|
||||
): IDbChatResponse {
|
||||
constructor(err: ChatError): this(listOf(err))
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
data class DbChatResponseErrWithData(
|
||||
val data: MessengerChat,
|
||||
val errors: List<ChatError> = emptyList()
|
||||
): IDbChatResponse {
|
||||
constructor(chat: MessengerChat, err: ChatError): this(chat, listOf(err))
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
data class DbChatResponseOk(
|
||||
val data: MessengerChat
|
||||
): IDbChatResponse
|
||||
@ -0,0 +1,10 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
|
||||
@Suppress("unused")
|
||||
data class DbChatsResponseErr(
|
||||
val errors: List<ChatError> = emptyList()
|
||||
): IDbChatsResponse {
|
||||
constructor(err: ChatError): this(listOf(err))
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
data class DbChatsResponseOk(
|
||||
val data: List<MessengerChat>
|
||||
): IDbChatsResponse
|
||||
@ -0,0 +1,60 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.exceptions.RepoConcurrencyException
|
||||
import ru.otus.messenger.common.exceptions.RepoException
|
||||
import ru.otus.messenger.common.helpers.errorSystem
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
const val ERROR_GROUP_REPO = "repo"
|
||||
|
||||
fun errorNotFound(id: ChatId) = DbChatResponseErr(
|
||||
ChatError(
|
||||
code = "$ERROR_GROUP_REPO-not-found",
|
||||
group = ERROR_GROUP_REPO,
|
||||
field = "id",
|
||||
message = "Object with ID: ${id.asString()} is not Found",
|
||||
)
|
||||
)
|
||||
|
||||
val errorEmptyId = DbChatResponseErr(
|
||||
ChatError(
|
||||
code = "$ERROR_GROUP_REPO-empty-id",
|
||||
group = ERROR_GROUP_REPO,
|
||||
field = "id",
|
||||
message = "Id must not be null or blank"
|
||||
)
|
||||
)
|
||||
|
||||
fun errorRepoConcurrency(
|
||||
oldChat: MessengerChat,
|
||||
exception: Exception = RepoConcurrencyException(
|
||||
id = oldChat.id
|
||||
),
|
||||
) = DbChatResponseErrWithData(
|
||||
chat = oldChat,
|
||||
err = ChatError(
|
||||
code = "${ERROR_GROUP_REPO}-concurrency",
|
||||
group = ERROR_GROUP_REPO,
|
||||
field = "lock",
|
||||
message = "The object with ID ${oldChat.id.asString()} has been changed concurrently by another user or process",
|
||||
exception = exception,
|
||||
)
|
||||
)
|
||||
|
||||
fun errorEmptyLock(id: ChatId) = DbChatResponseErr(
|
||||
ChatError(
|
||||
code = "$ERROR_GROUP_REPO-lock-empty",
|
||||
group = ERROR_GROUP_REPO,
|
||||
field = "lock",
|
||||
message = "Lock for Ad ${id.asString()} is empty that is not admitted"
|
||||
)
|
||||
)
|
||||
|
||||
fun errorDb(e: RepoException) = DbChatResponseErr(
|
||||
errorSystem(
|
||||
violationCode = "dbLockEmpty",
|
||||
e = e
|
||||
)
|
||||
)
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
sealed interface IDbChatResponse: IDbResponse<MessengerChat>
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
sealed interface IDbChatsResponse: IDbResponse<List<MessengerChat>>
|
||||
@ -0,0 +1,3 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
sealed interface IDbResponse<T>
|
||||
@ -0,0 +1,35 @@
|
||||
package ru.otus.messenger.common.repo
|
||||
|
||||
interface IRepoChat {
|
||||
|
||||
suspend fun createChat(rq: DbChatRequest): IDbChatResponse
|
||||
suspend fun readChat(rq: DbChatIdRequest): IDbChatResponse
|
||||
suspend fun updateChat(rq: DbChatRequest): IDbChatResponse
|
||||
suspend fun deleteChat(rq: DbChatIdRequest): IDbChatResponse
|
||||
suspend fun searchChat(rq: DbChatFilterRequest): IDbChatsResponse
|
||||
|
||||
companion object {
|
||||
val NONE = object : IRepoChat {
|
||||
override suspend fun createChat(rq: DbChatRequest): IDbChatResponse {
|
||||
throw NotImplementedError("Must not be used")
|
||||
}
|
||||
|
||||
override suspend fun readChat(rq: DbChatIdRequest): IDbChatResponse {
|
||||
throw NotImplementedError("Must not be used")
|
||||
}
|
||||
|
||||
override suspend fun updateChat(rq: DbChatRequest): IDbChatResponse {
|
||||
throw NotImplementedError("Must not be used")
|
||||
}
|
||||
|
||||
override suspend fun deleteChat(rq: DbChatIdRequest): IDbChatResponse {
|
||||
throw NotImplementedError("Must not be used")
|
||||
}
|
||||
|
||||
override suspend fun searchChat(rq: DbChatFilterRequest): IDbChatsResponse {
|
||||
throw NotImplementedError("Must not be used")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(libs.uuid)
|
||||
implementation(libs.clickhouse.client)
|
||||
implementation(project(":ok-messenger-common"))
|
||||
api(project(":ok-messenger-repo-common"))
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(project(":ok-messenger-repo-tests"))
|
||||
testImplementation(project(":ok-messenger-stubs"))
|
||||
testImplementation(libs.mockk)
|
||||
}
|
||||
@ -0,0 +1,255 @@
|
||||
package ru.otus.messenger.repo.clickhouse
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import com.clickhouse.client.api.Client
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import ru.otus.messenger.common.NONE
|
||||
import ru.otus.messenger.common.models.ChatArchiveFlag
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMetadata
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatSearchFilter
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.ChatRepoBase
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.common.repo.IDbChatResponse
|
||||
import ru.otus.messenger.common.repo.IDbChatsResponse
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.common.repo.errorEmptyId
|
||||
import ru.otus.messenger.common.repo.errorNotFound
|
||||
import ru.otus.messenger.repo.common.IRepoChatInitializable
|
||||
|
||||
class ChatRepoClickHouse(
|
||||
properties: DbProperties,
|
||||
private val randomUuid: () -> String = { uuid4().toString() }
|
||||
) : ChatRepoBase(), IRepoChat, IRepoChatInitializable {
|
||||
|
||||
private val chatTable: String = "chat"
|
||||
|
||||
private val client: Client = Client.Builder()
|
||||
.addEndpoint("https://${properties.host}:${properties.port}/")
|
||||
.setUsername(properties.user)
|
||||
.setPassword(properties.password)
|
||||
.build().also {
|
||||
it.register(ChatTableRecord::class.java, it.getTableSchema(chatTable))
|
||||
}
|
||||
|
||||
override fun save(chats: Collection<MessengerChat>): Collection<MessengerChat> {
|
||||
try {
|
||||
client.insert(
|
||||
chatTable,
|
||||
chats.map { chat -> ChatTableRecord(chat) }
|
||||
)
|
||||
return chats
|
||||
} catch (e: Exception) {
|
||||
return listOf()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun createChat(request: DbChatRequest): IDbChatResponse = tryChatMethod {
|
||||
val key = randomUuid()
|
||||
val chat = request.chat.copy(id = ChatId(key))
|
||||
val record = ChatTableRecord(chat)
|
||||
withContext(Dispatchers.IO) {
|
||||
client.insert(chatTable, listOf(record))
|
||||
}
|
||||
DbChatResponseOk(chat)
|
||||
}
|
||||
|
||||
override suspend fun readChat(request: DbChatIdRequest): IDbChatResponse = tryChatMethod {
|
||||
val key = request.id.takeIf { it != ChatId.NONE }?.asString() ?: return@tryChatMethod errorEmptyId
|
||||
|
||||
val sql = "SELECT * FROM $chatTable WHERE chatId = $key"
|
||||
|
||||
// Default format is RowBinaryWithNamesAndTypesFormatReader so reader have all information about columns
|
||||
withContext(Dispatchers.IO) {
|
||||
client.query(sql)[3, TimeUnit.SECONDS].use { response ->
|
||||
|
||||
// Create a reader to access the data in a convenient way
|
||||
val reader = client.newBinaryFormatReader(response)
|
||||
if (reader.hasNext()) {
|
||||
// Read the next record from stream and parse it
|
||||
reader.next()
|
||||
|
||||
// get values
|
||||
DbChatResponseOk(
|
||||
ChatTableRecord(
|
||||
chatId = reader.getString("chatId"),
|
||||
title = reader.getString("title"),
|
||||
description = reader.getString("description"),
|
||||
type = reader.getString("type"),
|
||||
mode = reader.getString("mode"),
|
||||
ownerId = reader.getString("ownerId"),
|
||||
participants = reader.getList("participants"),
|
||||
createdAt = reader.getInstant("createdAt").toKotlinInstant(),
|
||||
updatedAt = reader.getInstant("updatedAt").toKotlinInstant(),
|
||||
isArchived = reader.getBoolean("isArchived"),
|
||||
metadata = reader.getString("metadata"),
|
||||
).toInternal()
|
||||
)
|
||||
} else {
|
||||
errorNotFound(request.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateChat(request: DbChatRequest): IDbChatResponse = tryChatMethod {
|
||||
val chatId = request.chat.id.takeIf { it != ChatId.NONE } ?: return@tryChatMethod errorEmptyId
|
||||
val chat = request.chat
|
||||
|
||||
val updates = mutableListOf<String>()
|
||||
val params = mutableMapOf<String, Any>()
|
||||
|
||||
if (chat.title.isNotBlank()) {
|
||||
updates += "title = :title"
|
||||
params["title"] = chat.title
|
||||
}
|
||||
if (chat.description.isNotBlank()) {
|
||||
updates += "description = :description"
|
||||
params["description"] = chat.description
|
||||
}
|
||||
if (chat.type != ChatType.NONE) {
|
||||
updates += "chat_type = :chatType"
|
||||
params["chatType"] = chat.type.name
|
||||
}
|
||||
if (chat.mode != ChatMode.NONE) {
|
||||
updates += "chat_mode = :chatMode"
|
||||
params["chatMode"] = chat.mode.name
|
||||
}
|
||||
if (chat.ownerId != ChatOwnerId.NONE) {
|
||||
updates += "owner_id = :ownerId"
|
||||
params["ownerId"] = chat.ownerId.asString()
|
||||
}
|
||||
if (chat.createdAt != Instant.NONE) {
|
||||
updates += "created_at = :createdAt"
|
||||
params["createdAt"] = chat.createdAt
|
||||
}
|
||||
if (chat.updatedAt != Instant.NONE) {
|
||||
updates += "updated_at = :updatedAt"
|
||||
params["updatedAt"] = chat.updatedAt
|
||||
}
|
||||
if (chat.isArchived != ChatArchiveFlag.NONE) {
|
||||
updates += "is_archived = :isArchived"
|
||||
params["isArchived"] = chat.isArchived.asBoolean()
|
||||
}
|
||||
if (chat.metadata != ChatMetadata.NONE) {
|
||||
updates += "metadata = :metadata"
|
||||
params["metadata"] = chat.metadata.asString()
|
||||
}
|
||||
|
||||
if (updates.isEmpty()) {
|
||||
throw IllegalArgumentException("No fields to update")
|
||||
}
|
||||
|
||||
params["chatId"] = chatId.asString()
|
||||
|
||||
val sql = """
|
||||
UPDATE $chatTable
|
||||
SET ${updates.joinToString(", ")}
|
||||
WHERE id = :chatId
|
||||
""".trimIndent()
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
client.query(sql)[3, TimeUnit.SECONDS]
|
||||
}
|
||||
|
||||
readChat(DbChatIdRequest(chatId))
|
||||
}
|
||||
|
||||
override suspend fun deleteChat(request: DbChatIdRequest): IDbChatResponse = tryChatMethod {
|
||||
val chatId = request.id.takeIf { it != ChatId.NONE } ?: return@tryChatMethod errorEmptyId
|
||||
val key = chatId.asString()
|
||||
|
||||
val result = readChat(request)
|
||||
val sql = "DELETE FROM $chatTable WHERE chatId = $key"
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
client.query(sql)[3, TimeUnit.SECONDS]
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
override suspend fun searchChat(request: DbChatFilterRequest): IDbChatsResponse = tryChatsMethod {
|
||||
val result: MutableList<ChatTableRecord> = mutableListOf()
|
||||
|
||||
val whereClauses = mutableListOf<String>()
|
||||
val params = mutableMapOf<String, Any>()
|
||||
|
||||
if (request.ownerId != ChatOwnerId.NONE) {
|
||||
whereClauses += "ownerId = :ownerId"
|
||||
params["ownerId"] = request.ownerId.asString()
|
||||
}
|
||||
|
||||
if (request.chatType != ChatType.NONE) {
|
||||
whereClauses += "chatType = :chatType"
|
||||
params["chatType"] = request.chatType.name
|
||||
}
|
||||
|
||||
if (request.chatMode != ChatMode.NONE) {
|
||||
whereClauses += "chatMode = :chatMode"
|
||||
params["chatMode"] = request.chatMode.name
|
||||
}
|
||||
|
||||
if (request.searchFields.isNotEmpty()) {
|
||||
val searchConditions = request.searchFields.mapIndexed { index, field ->
|
||||
field as ChatSearchFilter.StringSearchField
|
||||
val paramName = "searchField$index"
|
||||
params[paramName] = "%${field.stringValue}%"
|
||||
"${field.fieldName} ILIKE :$paramName"
|
||||
}
|
||||
whereClauses += "(${searchConditions.joinToString(" OR ")})"
|
||||
}
|
||||
|
||||
val wherePart = if (whereClauses.isNotEmpty()) {
|
||||
"WHERE " + whereClauses.joinToString(" AND ")
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
val sql = "SELECT * FROM $chatTable $wherePart"
|
||||
|
||||
// Default format is RowBinaryWithNamesAndTypesFormatReader so reader have all information about columns
|
||||
withContext(Dispatchers.IO) {
|
||||
client.query(sql)[3, TimeUnit.SECONDS].use { response ->
|
||||
|
||||
// Create a reader to access the data in a convenient way
|
||||
val reader = client.newBinaryFormatReader(response)
|
||||
while (reader.hasNext()) {
|
||||
// Read the next record from stream and parse it
|
||||
reader.next()
|
||||
|
||||
// get values
|
||||
result.add(
|
||||
ChatTableRecord(
|
||||
chatId = reader.getString("chatId"),
|
||||
title = reader.getString("title"),
|
||||
description = reader.getString("description"),
|
||||
type = reader.getString("type"),
|
||||
mode = reader.getString("mode"),
|
||||
ownerId = reader.getString("ownerId"),
|
||||
participants = reader.getList("participants"),
|
||||
createdAt = reader.getInstant("createdAt").toKotlinInstant(),
|
||||
updatedAt = reader.getInstant("updatedAt").toKotlinInstant(),
|
||||
isArchived = reader.getBoolean("isArchived"),
|
||||
metadata = reader.getString("metadata"),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DbChatsResponseOk(result.map { it.toInternal() })
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package ru.otus.messenger.repo.clickhouse
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import ru.otus.messenger.common.NONE
|
||||
import ru.otus.messenger.common.models.ChatArchiveFlag
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMetadata
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.ChatUserId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
class ChatTableRecord(
|
||||
val chatId: String?,
|
||||
val title: String?,
|
||||
val description: String?,
|
||||
val type: String?,
|
||||
val mode: String?,
|
||||
val ownerId: String?,
|
||||
val participants: List<String>,
|
||||
val createdAt: Instant?,
|
||||
val updatedAt: Instant?,
|
||||
val isArchived: Boolean?,
|
||||
val metadata: String?,
|
||||
) {
|
||||
constructor(model: MessengerChat): this(
|
||||
chatId = model.id.takeIf { it != ChatId.NONE }?.asString(),
|
||||
title = model.title.takeIf { it.isNotBlank() },
|
||||
description = model.description.takeIf { it.isNotBlank() },
|
||||
type = model.type.takeIf { it != ChatType.NONE }?.name,
|
||||
mode = model.mode.takeIf { it != ChatMode.NONE }?.name,
|
||||
ownerId = model.ownerId.takeIf { it != ChatOwnerId.NONE }?.asString(),
|
||||
participants = model.participants.map { it.asString() },
|
||||
createdAt = model.createdAt.takeIf { it != Instant.NONE },
|
||||
updatedAt = model.updatedAt.takeIf { it != Instant.NONE },
|
||||
isArchived = model.isArchived.takeIf { it != ChatArchiveFlag.NONE }?.asBoolean(),
|
||||
metadata = model.metadata.takeIf { it != ChatMetadata.NONE }?.asString()
|
||||
)
|
||||
|
||||
fun toInternal() = MessengerChat(
|
||||
id = chatId?.let { ChatId(it) } ?: ChatId.NONE,
|
||||
title = title ?: "",
|
||||
description = description ?: "",
|
||||
type = type?.let { ChatType.valueOf(it) } ?: ChatType.NONE,
|
||||
mode = mode?.let { ChatMode.valueOf(it) } ?: ChatMode.NONE,
|
||||
ownerId = ownerId?.let { ChatOwnerId(it) } ?: ChatOwnerId.NONE,
|
||||
participants = participants.map { ChatUserId(it) }.toMutableSet(),
|
||||
createdAt = createdAt ?: Instant.NONE,
|
||||
updatedAt = updatedAt ?: Instant.NONE,
|
||||
isArchived = isArchived?.let { ChatArchiveFlag(it) } ?: ChatArchiveFlag.NONE,
|
||||
metadata = metadata?.let { ChatMetadata(Json.parseToJsonElement(it).jsonObject) } ?: ChatMetadata.NONE,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.messenger.repo.clickhouse
|
||||
|
||||
data class DbProperties(
|
||||
val host: String = "localhost",
|
||||
val port: Int = 8443,
|
||||
val user: String = "default",
|
||||
val password: String = "",
|
||||
)
|
||||
@ -0,0 +1,36 @@
|
||||
package ru.otus.messenger.repo.clickhouse
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import kotlin.test.Test
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
class ChatRepoClickHouseTest {
|
||||
private val repo = mockk<ChatRepoClickHouse>()
|
||||
private val stub = MessengerChatStub.get()
|
||||
|
||||
@Test
|
||||
fun testCreate() {
|
||||
coEvery { repo.createChat(DbChatRequest(stub)) } returns DbChatResponseOk(stub)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRead() {
|
||||
coEvery { repo.readChat(DbChatIdRequest(stub.id)) } returns DbChatResponseOk(stub)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
coEvery { repo.deleteChat(DbChatIdRequest(stub.id)) } returns DbChatResponseOk(stub)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSearch() {
|
||||
coEvery { repo.searchChat(DbChatFilterRequest()) } returns DbChatsResponseOk(listOf(stub))
|
||||
}
|
||||
}
|
||||
0
ok-messenger-be/ok-messenger-repo-common/README.md
Normal file
0
ok-messenger-be/ok-messenger-repo-common/README.md
Normal file
11
ok-messenger-be/ok-messenger-repo-common/build.gradle.kts
Normal file
11
ok-messenger-be/ok-messenger-repo-common/build.gradle.kts
Normal file
@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(project(":ok-messenger-common"))
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.repo.common
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
class ChatRepoInitialized(
|
||||
private val repo: IRepoChatInitializable,
|
||||
initObjects: Collection<MessengerChat> = emptyList(),
|
||||
) : IRepoChatInitializable by repo {
|
||||
@Suppress("unused")
|
||||
val initializedObjects: List<MessengerChat> = save(initObjects).toList()
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.messenger.repo.common
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
|
||||
interface IRepoChatInitializable : IRepoChat {
|
||||
fun save(chats: Collection<MessengerChat>) : Collection<MessengerChat>
|
||||
}
|
||||
8
ok-messenger-be/ok-messenger-repo-inmemory/README.md
Normal file
8
ok-messenger-be/ok-messenger-repo-inmemory/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Модуль `ok-messenger-repo-in-memory`
|
||||
|
||||
Модуль реализует интерфейс репозитария в виде кеша в памяти.
|
||||
|
||||
Используемые зависимости:
|
||||
|
||||
- **io.github.reactivecircus.cache4k:cache4k** - мультиплатформенная библиотека для кеширования [Документация](https://github.com/ReactiveCircus/cache4k)
|
||||
- **com.benasher44:uuid** - реализация UUID для KMP [Документация](https://github.com/benasher44/uuid)
|
||||
17
ok-messenger-be/ok-messenger-repo-inmemory/build.gradle.kts
Normal file
17
ok-messenger-be/ok-messenger-repo-inmemory/build.gradle.kts
Normal file
@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlin.datetime)
|
||||
implementation(libs.db.cache4k)
|
||||
implementation(libs.uuid)
|
||||
implementation(project(":ok-messenger-common"))
|
||||
api(project(":ok-messenger-repo-common"))
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(project(":ok-messenger-repo-tests"))
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import ru.otus.messenger.common.NONE
|
||||
import ru.otus.messenger.common.models.ChatArchiveFlag
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMetadata
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.ChatUserId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
data class ChatEntity(
|
||||
val chatId: String? = null,
|
||||
val title: String? = null,
|
||||
val description: String? = null,
|
||||
val type: String? = null,
|
||||
val mode: String? = null,
|
||||
val ownerId: String? = null,
|
||||
val participants: List<String> = listOf(),
|
||||
val createdAt: Instant? = null,
|
||||
val updatedAt: Instant? = null,
|
||||
val isArchived: Boolean? = null,
|
||||
val metadata: String? = null,
|
||||
) {
|
||||
constructor(model: MessengerChat): this(
|
||||
chatId = model.id.takeIf { it != ChatId.NONE }?.asString(),
|
||||
title = model.title.takeIf { it.isNotBlank() },
|
||||
description = model.description.takeIf { it.isNotBlank() },
|
||||
type = model.type.takeIf { it != ChatType.NONE }?.name,
|
||||
mode = model.mode.takeIf { it != ChatMode.NONE }?.name,
|
||||
ownerId = model.ownerId.takeIf { it != ChatOwnerId.NONE }?.asString(),
|
||||
participants = model.participants.map { it.asString() },
|
||||
createdAt = model.createdAt.takeIf { it != Instant.NONE },
|
||||
updatedAt = model.updatedAt.takeIf { it != Instant.NONE },
|
||||
isArchived = model.isArchived.takeIf { it != ChatArchiveFlag.NONE }?.asBoolean(),
|
||||
metadata = model.metadata.takeIf { it != ChatMetadata.NONE }?.asString()
|
||||
)
|
||||
|
||||
fun toInternal() = MessengerChat(
|
||||
id = chatId?.let { ChatId(it) } ?: ChatId.NONE,
|
||||
title = title ?: "",
|
||||
description = description ?: "",
|
||||
type = type?.let { ChatType.valueOf(it) } ?: ChatType.NONE,
|
||||
mode = mode?.let { ChatMode.valueOf(it) } ?: ChatMode.NONE,
|
||||
ownerId = ownerId?.let { ChatOwnerId(it) } ?: ChatOwnerId.NONE,
|
||||
participants = participants.map { ChatUserId(it) }.toMutableSet(),
|
||||
createdAt = createdAt ?: Instant.NONE,
|
||||
updatedAt = updatedAt ?: Instant.NONE,
|
||||
isArchived = isArchived?.let { ChatArchiveFlag(it) } ?: ChatArchiveFlag.NONE,
|
||||
metadata = metadata?.let { ChatMetadata(Json.parseToJsonElement(it).jsonObject) } ?: ChatMetadata.NONE,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import io.github.reactivecircus.cache4k.Cache
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import ru.otus.messenger.common.exceptions.RepoEmptyLockException
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatSearchFilter
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.ChatRepoBase
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.common.repo.IDbChatResponse
|
||||
import ru.otus.messenger.common.repo.IDbChatsResponse
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.common.repo.errorDb
|
||||
import ru.otus.messenger.common.repo.errorEmptyId
|
||||
import ru.otus.messenger.common.repo.errorNotFound
|
||||
import ru.otus.messenger.common.repo.errorRepoConcurrency
|
||||
import ru.otus.messenger.repo.common.IRepoChatInitializable
|
||||
|
||||
class ChatRepoInMemory(
|
||||
ttl: Duration = 2.minutes,
|
||||
val randomUuid: () -> String = { uuid4().toString() },
|
||||
) : ChatRepoBase(), IRepoChat, IRepoChatInitializable {
|
||||
|
||||
private val mutex: Mutex = Mutex()
|
||||
private val cache = Cache.Builder<String, ChatEntity>()
|
||||
.expireAfterWrite(ttl)
|
||||
.build()
|
||||
|
||||
override fun save(chats: Collection<MessengerChat>) = chats.map { chat ->
|
||||
val entity = ChatEntity(chat)
|
||||
require(entity.chatId != null)
|
||||
cache.put(entity.chatId, entity)
|
||||
chat
|
||||
}
|
||||
|
||||
override suspend fun createChat(request: DbChatRequest): IDbChatResponse = tryChatMethod {
|
||||
val key = randomUuid()
|
||||
val chat = request.chat.copy(id = ChatId(key))
|
||||
val entity = ChatEntity(chat)
|
||||
mutex.withLock {
|
||||
cache.put(key, entity)
|
||||
}
|
||||
DbChatResponseOk(chat)
|
||||
}
|
||||
|
||||
override suspend fun readChat(request: DbChatIdRequest): IDbChatResponse = tryChatMethod {
|
||||
val key = request.id.takeIf { it != ChatId.NONE }?.asString() ?: return@tryChatMethod errorEmptyId
|
||||
mutex.withLock {
|
||||
cache.get(key)
|
||||
?.let {
|
||||
DbChatResponseOk(it.toInternal())
|
||||
} ?: errorNotFound(request.id)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateChat(request: DbChatRequest): IDbChatResponse = tryChatMethod {
|
||||
val requestChat = request.chat
|
||||
val chatId = requestChat.id.takeIf { it != ChatId.NONE } ?: return@tryChatMethod errorEmptyId
|
||||
val key = chatId.asString()
|
||||
|
||||
mutex.withLock {
|
||||
val oldChat = cache.get(key)?.toInternal()
|
||||
when {
|
||||
oldChat == null -> errorNotFound(chatId)
|
||||
oldChat.id == ChatId.NONE -> errorDb(RepoEmptyLockException(chatId))
|
||||
oldChat.id != chatId -> errorRepoConcurrency(oldChat)
|
||||
else -> {
|
||||
val newAd = requestChat.copy(id = ChatId(randomUuid()))
|
||||
val entity = ChatEntity(newAd)
|
||||
cache.put(key, entity)
|
||||
DbChatResponseOk(newAd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteChat(request: DbChatIdRequest): IDbChatResponse = tryChatMethod {
|
||||
val chatId = request.id.takeIf { it != ChatId.NONE } ?: return@tryChatMethod errorEmptyId
|
||||
val key = chatId.asString()
|
||||
|
||||
mutex.withLock {
|
||||
val oldChat = cache.get(key)?.toInternal()
|
||||
when {
|
||||
oldChat == null -> errorNotFound(chatId)
|
||||
oldChat.id == ChatId.NONE -> errorDb(RepoEmptyLockException(chatId))
|
||||
oldChat.id != chatId -> errorRepoConcurrency(oldChat)
|
||||
else -> {
|
||||
cache.invalidate(key)
|
||||
DbChatResponseOk(oldChat)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun searchChat(request: DbChatFilterRequest): IDbChatsResponse = tryChatsMethod {
|
||||
val result: List<MessengerChat> = cache.asMap().asSequence()
|
||||
.filter { entry ->
|
||||
request.chatType.takeIf { it != ChatType.NONE }?.let {
|
||||
it.name == entry.value.type
|
||||
} ?: true
|
||||
}
|
||||
.filter { entry ->
|
||||
request.chatMode.takeIf { it != ChatMode.NONE }?.let {
|
||||
it.name == entry.value.mode
|
||||
} ?: true
|
||||
}
|
||||
.filter { entry ->
|
||||
request.ownerId.takeIf { it != ChatOwnerId.NONE }?.let {
|
||||
it.asString() == entry.value.ownerId
|
||||
} ?: true
|
||||
}
|
||||
.filter { entry ->
|
||||
request.searchFields.takeIf { it.isNotEmpty() }?.let { searchFields ->
|
||||
searchFields.any { searchField ->
|
||||
when (searchField) {
|
||||
is ChatSearchFilter.StringSearchField -> {
|
||||
when (searchField.fieldName.lowercase()) {
|
||||
"chatid" -> assertAction(searchField.stringValue, entry.value.chatId!!, searchField.action)
|
||||
"title" -> assertAction(searchField.stringValue, entry.value.title!!, searchField.action)
|
||||
"ownerid" -> assertAction(searchField.stringValue, entry.value.ownerId!!, searchField.action)
|
||||
"description" -> assertAction(searchField.stringValue, entry.value.description!!, searchField.action)
|
||||
"isarchived" -> assertAction(searchField.stringValue, entry.value.isArchived.toString(), searchField.action)
|
||||
"metadata" -> assertAction(searchField.stringValue, entry.value.metadata!!, searchField.action)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
} ?: true
|
||||
}
|
||||
.map { it.value.toInternal() }
|
||||
.toList()
|
||||
DbChatsResponseOk(result)
|
||||
}
|
||||
|
||||
fun assertAction(expected: String, actual: String, action: ChatSearchFilter.SearchAction) =
|
||||
when (action) {
|
||||
ChatSearchFilter.SearchAction.EQUALS -> expected.toString() == actual
|
||||
ChatSearchFilter.SearchAction.CONTAINS -> expected.toString().contains(actual, ignoreCase = true)
|
||||
ChatSearchFilter.SearchAction.MORE -> expected > actual
|
||||
ChatSearchFilter.SearchAction.LESS -> expected < actual
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.tests.RepoChatCreateTest
|
||||
|
||||
class ChatRepoInMemoryCreateTest : RepoChatCreateTest() {
|
||||
override val repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(randomUuid = { uuidNew.asString() }),
|
||||
initObjects = initObjects,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.tests.RepoChatDeleteTest
|
||||
|
||||
class ChatRepoInMemoryDeleteTest : RepoChatDeleteTest() {
|
||||
override val repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(),
|
||||
initObjects = initObjects,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.tests.RepoChatReadTest
|
||||
|
||||
class ChatRepoInMemoryReadTest : RepoChatReadTest() {
|
||||
override val repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(),
|
||||
initObjects = initObjects,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.tests.RepoChatSearchTest
|
||||
|
||||
class ChatRepoInMemorySearchTest : RepoChatSearchTest() {
|
||||
override val repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(),
|
||||
initObjects = initObjects,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.messenger.repo.inmemory
|
||||
|
||||
import ru.otus.messenger.repo.common.ChatRepoInitialized
|
||||
import ru.otus.messenger.repo.tests.RepoChatDeleteTest
|
||||
import ru.otus.messenger.repo.tests.RepoChatUpdateTest
|
||||
|
||||
class ChatRepoInMemoryUpdateTest : RepoChatUpdateTest() {
|
||||
override val repo = ChatRepoInitialized(
|
||||
ChatRepoInMemory(),
|
||||
initObjects = RepoChatDeleteTest.Companion.initObjects,
|
||||
)
|
||||
}
|
||||
3
ok-messenger-be/ok-messenger-repo-stubs/README.md
Normal file
3
ok-messenger-be/ok-messenger-repo-stubs/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Модуль `ok-messenger-repo-stubs`
|
||||
|
||||
Модуль реализует интерфейс репозитария в виде стабов.
|
||||
13
ok-messenger-be/ok-messenger-repo-stubs/build.gradle.kts
Normal file
13
ok-messenger-be/ok-messenger-repo-stubs/build.gradle.kts
Normal file
@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(project(":ok-messenger-common"))
|
||||
implementation(project(":ok-messenger-stubs"))
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(project(":ok-messenger-repo-tests"))
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ru.otus.messenger.repo.stub
|
||||
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.common.repo.IDbChatResponse
|
||||
import ru.otus.messenger.common.repo.IDbChatsResponse
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
class ChatRepoStub : IRepoChat {
|
||||
override suspend fun createChat(request: DbChatRequest): IDbChatResponse {
|
||||
return DbChatResponseOk(
|
||||
data = MessengerChatStub.get(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun readChat(request: DbChatIdRequest): IDbChatResponse {
|
||||
return DbChatResponseOk(
|
||||
data = MessengerChatStub.get(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun updateChat(request: DbChatRequest): IDbChatResponse {
|
||||
return DbChatResponseOk(
|
||||
data = MessengerChatStub.get(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun deleteChat(request: DbChatIdRequest): IDbChatResponse {
|
||||
return DbChatResponseOk(
|
||||
data = MessengerChatStub.get(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun searchChat(request: DbChatFilterRequest): IDbChatsResponse {
|
||||
return DbChatsResponseOk(
|
||||
data = MessengerChatStub.prepareSearchList(
|
||||
chatTitle = "",
|
||||
chatType = ChatType.NONE,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
0
ok-messenger-be/ok-messenger-repo-tests/README.md
Normal file
0
ok-messenger-be/ok-messenger-repo-tests/README.md
Normal file
16
ok-messenger-be/ok-messenger-repo-tests/build.gradle.kts
Normal file
16
ok-messenger-be/ok-messenger-repo-tests/build.gradle.kts
Normal file
@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlin.datetime)
|
||||
api(libs.kotlin.coroutines)
|
||||
api(libs.kotlin.coroutines.test)
|
||||
implementation(project(":ok-messenger-common"))
|
||||
implementation(project(":ok-messenger-repo-common"))
|
||||
implementation(kotlin("test-junit"))
|
||||
|
||||
testImplementation(project(":ok-messenger-stubs"))
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
|
||||
abstract class BaseInitChats(private val operation: String): IInitObjects<MessengerChat> {
|
||||
fun createInitTestModel(
|
||||
suffix: String,
|
||||
chatOwnerId: ChatOwnerId = ChatOwnerId("TestOwnerId"),
|
||||
chatType: ChatType = ChatType.GROUP,
|
||||
chatMode: ChatMode = ChatMode.WORK,
|
||||
) = MessengerChat(
|
||||
id = ChatId("chat-repo-$operation-$suffix"),
|
||||
ownerId = chatOwnerId,
|
||||
type = chatType,
|
||||
mode = chatMode,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.common.repo.IDbChatResponse
|
||||
import ru.otus.messenger.common.repo.IDbChatsResponse
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
|
||||
class ChatRepositoryMock(
|
||||
private val invokeCreateChat: (DbChatRequest) -> IDbChatResponse = { DEFAULT_CHAT_SUCCESS_EMPTY_MOCK },
|
||||
private val invokeReadChat: (DbChatIdRequest) -> IDbChatResponse = { DEFAULT_CHAT_SUCCESS_EMPTY_MOCK },
|
||||
private val invokeUpdateChat: (DbChatRequest) -> IDbChatResponse = { DEFAULT_CHAT_SUCCESS_EMPTY_MOCK },
|
||||
private val invokeDeleteChat: (DbChatIdRequest) -> IDbChatResponse = { DEFAULT_CHAT_SUCCESS_EMPTY_MOCK },
|
||||
private val invokeSearchChat: (DbChatFilterRequest) -> IDbChatsResponse = { DEFAULT_CHATS_SUCCESS_EMPTY_MOCK },
|
||||
): IRepoChat {
|
||||
override suspend fun createChat(request: DbChatRequest): IDbChatResponse {
|
||||
return invokeCreateChat(request)
|
||||
}
|
||||
|
||||
override suspend fun readChat(request: DbChatIdRequest): IDbChatResponse {
|
||||
return invokeReadChat(request)
|
||||
}
|
||||
|
||||
override suspend fun updateChat(request: DbChatRequest): IDbChatResponse {
|
||||
return invokeUpdateChat(request)
|
||||
}
|
||||
|
||||
override suspend fun deleteChat(request: DbChatIdRequest): IDbChatResponse {
|
||||
return invokeDeleteChat(request)
|
||||
}
|
||||
|
||||
override suspend fun searchChat(request: DbChatFilterRequest): IDbChatsResponse {
|
||||
return invokeSearchChat(request)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DEFAULT_CHAT_SUCCESS_EMPTY_MOCK = DbChatResponseOk(MessengerChat())
|
||||
val DEFAULT_CHATS_SUCCESS_EMPTY_MOCK = DbChatsResponseOk(emptyList())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
internal interface IInitObjects<T> {
|
||||
val initObjects: List<T>
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import ru.otus.messenger.common.models.ChatArchiveFlag
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.ChatMetadata
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.repo.common.IRepoChatInitializable
|
||||
|
||||
abstract class RepoChatCreateTest {
|
||||
abstract val repo: IRepoChatInitializable
|
||||
protected open val uuidNew = ChatId("10000000-0000-0000-0000-000000000001")
|
||||
|
||||
private val createObj = MessengerChat(
|
||||
id = ChatId("Test"),
|
||||
title = "",
|
||||
description = "",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.PERSONAL,
|
||||
ownerId = ChatOwnerId("Test123"),
|
||||
participants = mutableSetOf(),
|
||||
createdAt = Instant.fromEpochMilliseconds(123456),
|
||||
updatedAt = Instant.fromEpochMilliseconds(123456),
|
||||
isArchived = ChatArchiveFlag.NONE,
|
||||
metadata = ChatMetadata(
|
||||
buildJsonObject {
|
||||
put("sampleId", "test")
|
||||
put("case", "create object")
|
||||
put("info", "Why should I repeat this initialization one more time???")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun createSuccess() = runRepoTest {
|
||||
val result = repo.createChat(DbChatRequest(createObj))
|
||||
val expected = createObj
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals(uuidNew, result.data.id)
|
||||
assertEquals(expected.ownerId, result.data.ownerId)
|
||||
assertEquals(expected.type, result.data.type)
|
||||
assertEquals(expected.mode, result.data.mode)
|
||||
assertEquals(expected.createdAt, result.data.createdAt)
|
||||
assertNotEquals(ChatId.NONE, result.data.id)
|
||||
}
|
||||
|
||||
companion object : BaseInitChats("create") {
|
||||
override val initObjects: List<MessengerChat> = emptyList()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
|
||||
abstract class RepoChatDeleteTest {
|
||||
abstract val repo: IRepoChat
|
||||
|
||||
protected open val deleteSuccess = initObjects[0]
|
||||
protected open val notFoundId = ChatId("repo-delete-notFound")
|
||||
|
||||
@Test
|
||||
fun deleteSuccess() = runRepoTest {
|
||||
val result = repo.deleteChat(DbChatIdRequest(deleteSuccess.id))
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals(deleteSuccess.ownerId, result.data.ownerId)
|
||||
assertEquals(deleteSuccess.type, result.data.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteNotFound() = runRepoTest {
|
||||
val result = repo.deleteChat(DbChatIdRequest(notFoundId))
|
||||
|
||||
assertIs<DbChatResponseErr>(result)
|
||||
val error = result.errors.find { it.code == "repo-not-found" }
|
||||
assertNotNull(error)
|
||||
}
|
||||
|
||||
companion object : BaseInitChats("delete") {
|
||||
override val initObjects: List<MessengerChat> = listOf(
|
||||
createInitTestModel("delete"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertEquals
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
|
||||
abstract class RepoChatReadTest {
|
||||
abstract val repo: IRepoChat
|
||||
protected open val readSuccess = initObjects[0]
|
||||
|
||||
@Test
|
||||
fun readSuccess() = runRepoTest {
|
||||
val result = repo.readChat(DbChatIdRequest(readSuccess.id))
|
||||
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals(readSuccess, result.data)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readNotFound() = runRepoTest {
|
||||
val result = repo.readChat(DbChatIdRequest(notFoundId))
|
||||
|
||||
assertIs<DbChatResponseErr>(result)
|
||||
val error = result.errors.find { it.code == "repo-not-found" }
|
||||
assertEquals("id", error?.field)
|
||||
}
|
||||
|
||||
companion object : BaseInitChats("read") {
|
||||
override val initObjects: List<MessengerChat> = listOf(
|
||||
createInitTestModel("read")
|
||||
)
|
||||
|
||||
val notFoundId = ChatId("repo-read-notFound")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatSearchFilter
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
|
||||
abstract class RepoChatSearchTest {
|
||||
abstract val repo: IRepoChat
|
||||
|
||||
protected open val initializedObjects: List<MessengerChat> = initObjects
|
||||
|
||||
@Test
|
||||
fun searchByOwnerId() = runRepoTest {
|
||||
val result = repo.searchChat(
|
||||
DbChatFilterRequest(ownerId = ChatOwnerId("TestOwnerId"))
|
||||
)
|
||||
assertIs<DbChatsResponseOk>(result)
|
||||
val expected = listOf(initializedObjects[1], initializedObjects[3]).sortedBy { it.id.asString() }
|
||||
assertEquals(expected, result.data.sortedBy { it.id.asString() })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByTypeAndMode() = runRepoTest {
|
||||
val result = repo.searchChat(
|
||||
DbChatFilterRequest(
|
||||
chatType = ChatType.GROUP,
|
||||
chatMode = ChatMode.WORK,
|
||||
)
|
||||
)
|
||||
assertIs<DbChatsResponseOk>(result)
|
||||
val expected = listOf(initializedObjects[0]).sortedBy { it.id.asString() }
|
||||
assertEquals(expected, result.data.sortedBy { it.id.asString() })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchContent() = runRepoTest {
|
||||
val result = repo.searchChat(
|
||||
DbChatFilterRequest(
|
||||
searchFields = listOf(
|
||||
ChatSearchFilter.StringSearchField(
|
||||
fieldName = "ownerId",
|
||||
action = ChatSearchFilter.SearchAction.EQUALS,
|
||||
stringValue = "Hmm",
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
assertIs<DbChatsResponseOk>(result)
|
||||
val expected = listOf(initializedObjects[0], initializedObjects[4]).sortedBy { it.id.asString() }
|
||||
assertEquals(expected, result.data.sortedBy { it.id.asString() })
|
||||
}
|
||||
|
||||
companion object: BaseInitChats("search") {
|
||||
private val searchOwnerId = ChatOwnerId("TestOwnerId")
|
||||
private val searchType = ChatType.CHANNEL
|
||||
private val searchMode = ChatMode.PERSONAL
|
||||
override val initObjects: List<MessengerChat> = listOf(
|
||||
createInitTestModel(
|
||||
"test1",
|
||||
chatOwnerId = ChatOwnerId("Hmm"),
|
||||
chatType = ChatType.GROUP,
|
||||
chatMode = ChatMode.WORK,
|
||||
),
|
||||
createInitTestModel(
|
||||
"test2",
|
||||
chatOwnerId = searchOwnerId,
|
||||
chatType = searchType,
|
||||
chatMode = searchMode,
|
||||
),
|
||||
createInitTestModel(
|
||||
"test3",
|
||||
chatOwnerId = ChatOwnerId("Eee!"),
|
||||
chatType = searchType,
|
||||
chatMode = ChatMode.WORK,
|
||||
),
|
||||
createInitTestModel(
|
||||
"test4",
|
||||
chatOwnerId = searchOwnerId,
|
||||
chatType = ChatType.PRIVATE
|
||||
),
|
||||
createInitTestModel(
|
||||
"test5",
|
||||
chatOwnerId = ChatOwnerId("Hmm"),
|
||||
chatType = ChatType.PRIVATE,
|
||||
chatMode = ChatMode.WORK,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import ru.otus.messenger.common.models.ChatId
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErr
|
||||
import ru.otus.messenger.common.repo.DbChatResponseErrWithData
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.IRepoChat
|
||||
|
||||
abstract class RepoChatUpdateTest {
|
||||
abstract val repo: IRepoChat
|
||||
|
||||
protected open val updateSucc = initObjects[0]
|
||||
protected open val updateConc = initObjects[1]
|
||||
protected val updateIdNotFound = ChatId("chat-repo-update-not-found")
|
||||
protected val chatIdBad = ChatId("20000000-0000-0000-0000-000000000009")
|
||||
|
||||
protected open val initializedObjects: List<MessengerChat> = initObjects
|
||||
|
||||
private val reqUpdateSucc by lazy {
|
||||
MessengerChat(
|
||||
id = updateSucc.id,
|
||||
title = "update object",
|
||||
description = "update object description",
|
||||
ownerId = updateSucc.ownerId,
|
||||
type = updateSucc.type,
|
||||
mode = updateSucc.mode,
|
||||
)
|
||||
}
|
||||
private val reqUpdateNotFound = MessengerChat(
|
||||
id = updateIdNotFound,
|
||||
title = "update object not found",
|
||||
description = "update object not found description",
|
||||
ownerId = updateConc.ownerId,
|
||||
type = updateConc.type,
|
||||
mode = updateConc.mode,
|
||||
)
|
||||
private val reqUpdateConc by lazy {
|
||||
MessengerChat(
|
||||
id = chatIdBad,
|
||||
title = "update object not found",
|
||||
description = "update object not found description",
|
||||
ownerId = updateConc.ownerId,
|
||||
type = updateConc.type,
|
||||
mode = updateConc.mode,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateSuccess() = runRepoTest {
|
||||
val result = repo.updateChat(DbChatRequest(reqUpdateSucc))
|
||||
println("ERRORS: ${(result as? DbChatResponseErr)?.errors}")
|
||||
println("ERRORSWD: ${(result as? DbChatResponseErrWithData)?.errors}")
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals(reqUpdateSucc.id, result.data.id)
|
||||
assertEquals(reqUpdateSucc.title, result.data.title)
|
||||
assertEquals(reqUpdateSucc.description, result.data.description)
|
||||
assertEquals(reqUpdateSucc.type, result.data.type)
|
||||
assertEquals(reqUpdateSucc.mode, result.data.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateNotFound() = runRepoTest {
|
||||
val result = repo.updateChat(DbChatRequest(reqUpdateNotFound))
|
||||
assertIs<DbChatResponseErr>(result)
|
||||
val error = result.errors.find { it.code == "repo-not-found" }
|
||||
assertEquals("id", error?.field)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateConcurrencyError() = runRepoTest {
|
||||
val result = repo.updateChat(DbChatRequest(reqUpdateConc))
|
||||
println(result)
|
||||
assertIs<DbChatResponseErrWithData>(result)
|
||||
val error = result.errors.find { it.code == "repo-concurrency" }
|
||||
assertEquals("lock", error?.field)
|
||||
assertEquals(updateConc, result.data)
|
||||
}
|
||||
|
||||
companion object: BaseInitChats("update") {
|
||||
override val initObjects: List<MessengerChat> = listOf(
|
||||
createInitTestModel("update"),
|
||||
createInitTestModel("updateConc"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
fun runRepoTest(testBody: suspend TestScope.() -> Unit) = runTest(timeout = 2.minutes) {
|
||||
withContext(Dispatchers.Default) {
|
||||
testBody()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package ru.otus.messenger.repo.tests
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.repo.DbChatFilterRequest
|
||||
import ru.otus.messenger.common.repo.DbChatIdRequest
|
||||
import ru.otus.messenger.common.repo.DbChatRequest
|
||||
import ru.otus.messenger.common.repo.DbChatResponseOk
|
||||
import ru.otus.messenger.common.repo.DbChatsResponseOk
|
||||
import ru.otus.messenger.stubs.MessengerChatStub
|
||||
|
||||
class ChatRepositoryMockTest {
|
||||
private val repo = ChatRepositoryMock(
|
||||
invokeCreateChat = { DbChatResponseOk(MessengerChatStub.prepareResult { title = "create" }) },
|
||||
invokeReadChat = { DbChatResponseOk(MessengerChatStub.prepareResult { title = "read" }) },
|
||||
invokeUpdateChat = { DbChatResponseOk(MessengerChatStub.prepareResult { title = "update" }) },
|
||||
invokeDeleteChat = { DbChatResponseOk(MessengerChatStub.prepareResult { title = "delete" }) },
|
||||
invokeSearchChat = { DbChatsResponseOk(listOf(MessengerChatStub.prepareResult { title = "search" })) },
|
||||
)
|
||||
|
||||
@Test
|
||||
fun mockCreate() = runTest {
|
||||
val result = repo.createChat(DbChatRequest(MessengerChat()))
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals("create", result.data.title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mockRead() = runTest {
|
||||
val result = repo.readChat(DbChatIdRequest(MessengerChat()))
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals("read", result.data.title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mockUpdate() = runTest {
|
||||
val result = repo.updateChat(DbChatRequest(MessengerChat()))
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals("update", result.data.title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mockDelete() = runTest {
|
||||
val result = repo.deleteChat(DbChatIdRequest(MessengerChat()))
|
||||
assertIs<DbChatResponseOk>(result)
|
||||
assertEquals("delete", result.data.title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mockSearch() = runTest {
|
||||
val result = repo.searchChat(DbChatFilterRequest())
|
||||
assertIs<DbChatsResponseOk>(result)
|
||||
assertEquals("search", result.data.first().title)
|
||||
}
|
||||
}
|
||||
@ -30,3 +30,8 @@ include(":ok-messenger-common")
|
||||
include(":ok-messenger-stubs")
|
||||
include(":ok-messenger-app")
|
||||
include(":ok-messenger-biz")
|
||||
include(":ok-messenger-repo-common")
|
||||
include(":ok-messenger-repo-inmemory")
|
||||
include(":ok-messenger-repo-clickhouse")
|
||||
include(":ok-messenger-repo-stubs")
|
||||
include(":ok-messenger-repo-tests")
|
||||
|
||||
Reference in New Issue
Block a user