From d3a95470457176fbc1d40099023cf6f52cd0e301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=92=D0=B5=D0=B4=D0=B5=D0=BD=D1=91=D0=B2?= Date: Fri, 4 Apr 2025 21:48:48 +0700 Subject: [PATCH] module 6 lesson 1 --- ok-lessons/build.gradle.kts | 2 +- .../src/main/kotlin/MappersV1FromTransport.kt | 16 +- .../src/test/kotlin/MapperCreateTest.kt | 4 +- .../src/test/kotlin/MapperDeleteTest.kt | 4 +- .../src/test/kotlin/MapperReadTest.kt | 4 +- .../src/test/kotlin/MapperSearchTest.kt | 4 +- .../src/test/kotlin/MapperUpdateTest.kt | 4 +- .../src/main/kotlin/ApiV1Mapper.kt | 6 + .../test/kotlin/RequestV1SerializationTest.kt | 2 +- .../kotlin/ResponseV1SerializationTest.kt | 2 +- .../src/test/kotlin/stub/V1StubApiTest.kt | 131 +++++++++++++++ .../ok-messenger-biz/build.gradle.kts | 5 + .../src/main/kotlin/MessengerProcessor.kt | 150 ++++++++++++++++-- .../src/main/kotlin/general/InitStatus.kt | 15 ++ .../src/main/kotlin/general/Operation.kt | 17 ++ .../src/main/kotlin/general/Stubs.kt | 13 ++ .../src/main/kotlin/general/Validation.kt | 12 ++ .../main/kotlin/stubs/StubCreateSuccess.kt | 33 ++++ .../src/main/kotlin/stubs/StubDbError.kt | 26 +++ .../main/kotlin/stubs/StubDeleteSuccess.kt | 28 ++++ .../src/main/kotlin/stubs/StubNoCase.kt | 26 +++ .../src/main/kotlin/stubs/StubNotFound.kt | 28 ++++ .../src/main/kotlin/stubs/StubReadSuccess.kt | 28 ++++ .../main/kotlin/stubs/StubSearchSuccess.kt | 32 ++++ .../main/kotlin/stubs/StubUpdateSuccess.kt | 35 ++++ .../stubs/StubValidationBadDescription.kt | 27 ++++ .../main/kotlin/stubs/StubValidationBadId.kt | 27 ++++ .../kotlin/stubs/StubValidationBadTitle.kt | 27 ++++ .../kotlin/validation/FinishValidation.kt | 23 +++ .../ValidateDescriptionHasContent.kt | 22 +++ .../validation/ValidateDescriptionNotEmpty.kt | 21 +++ .../kotlin/validation/ValidateIdNotEmpty.kt | 21 +++ .../validation/ValidateIdProperFormat.kt | 28 ++++ .../validation/ValidateSearchStringLength.kt | 53 +++++++ .../validation/ValidateTitleHasContent.kt | 26 +++ .../validation/ValidateTitleNotEmpty.kt | 21 +++ .../src/test/kotlin/package.kt | 1 + .../kotlin/stub/MessengerCreateStubTest.kt | 94 +++++++++++ .../kotlin/stub/MessengerDeleteStubTest.kt | 75 +++++++++ .../test/kotlin/stub/MessengerReadStubTest.kt | 76 +++++++++ .../kotlin/stub/MessengerSearchStubTest.kt | 91 +++++++++++ .../kotlin/stub/MessengerUpdateStubTest.kt | 117 ++++++++++++++ .../validation/BaseBizValidationTest.kt | 11 ++ .../validation/BizValidationCreateTest.kt | 18 +++ .../validation/BizValidationDeleteTest.kt | 13 ++ .../validation/BizValidationReadTest.kt | 13 ++ .../validation/BizValidationSearchTest.kt | 36 +++++ .../validation/BizValidationUpdateTest.kt | 23 +++ .../ValidateSearchStringLengthTest.kt | 110 +++++++++++++ .../validation/ValidateTitleHasContentTest.kt | 55 +++++++ .../validation/ValidationBadDescription.kt | 75 +++++++++ .../test/kotlin/validation/ValidationBadId.kt | 73 +++++++++ .../kotlin/validation/ValidationBadTitle.kt | 76 +++++++++ .../src/commonMain/kotlin/MessengerContext.kt | 11 +- .../helpers/MessengerContextErrorHelpers.kt | 12 ++ .../kotlin/helpers/MessengerErrorHelper.kt | 14 ++ .../kotlin/models/ChatSearchFilter.kt | 25 ++- .../commonMain/kotlin/models/MessengerChat.kt | 2 + .../stubs/{Stubs.kt => MessengerStubs.kt} | 7 +- .../src/main/kotlin/MessengerChatStub.kt | 4 +- .../ok-messenger-lib-cor/build.gradle.kts | 11 ++ .../src/main/kotlin/CorDslMarker.kt | 4 + .../src/main/kotlin/ICorExec.kt | 10 ++ .../src/main/kotlin/dsl/CorChainDsl.kt | 22 +++ .../src/main/kotlin/dsl/CorDsl.kt | 51 ++++++ .../src/main/kotlin/dsl/CorExecDsl.kt | 17 ++ .../src/main/kotlin/dsl/CorWorkerDsl.kt | 21 +++ .../src/main/kotlin/dsl/ICorChainDsl.kt | 11 ++ .../src/main/kotlin/dsl/ICorExecDsl.kt | 17 ++ .../src/main/kotlin/dsl/ICorWorkerDsl.kt | 11 ++ .../main/kotlin/handlers/AbstractCorExec.kt | 25 +++ .../src/main/kotlin/handlers/CorChain.kt | 20 +++ .../src/main/kotlin/handlers/CorWorker.kt | 11 ++ .../src/test/kotlin/CorChainTest.kt | 27 ++++ .../src/test/kotlin/CorDslTest.kt | 109 +++++++++++++ .../src/test/kotlin/CorWorkerTest.kt | 44 +++++ .../src/test/kotlin/TestContext.kt | 14 ++ ok-messenger-libs/settings.gradle.kts | 3 +- 78 files changed, 2341 insertions(+), 42 deletions(-) create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/InitStatus.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Operation.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Stubs.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Validation.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubCreateSuccess.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDbError.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDeleteSuccess.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNoCase.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNotFound.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubReadSuccess.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubSearchSuccess.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubUpdateSuccess.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadDescription.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadId.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadTitle.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/FinishValidation.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionHasContent.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionNotEmpty.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdNotEmpty.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdProperFormat.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateSearchStringLength.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleHasContent.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleNotEmpty.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/package.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerCreateStubTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerDeleteStubTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerReadStubTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerSearchStubTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerUpdateStubTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BaseBizValidationTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationCreateTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationDeleteTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationReadTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationSearchTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationUpdateTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateSearchStringLengthTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateTitleHasContentTest.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadDescription.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadId.kt create mode 100644 ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadTitle.kt create mode 100644 ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerContextErrorHelpers.kt rename ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/{Stubs.kt => MessengerStubs.kt} (55%) create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/build.gradle.kts create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/CorDslMarker.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/ICorExec.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorChainDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorExecDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorWorkerDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorChainDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorExecDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorWorkerDsl.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/AbstractCorExec.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorChain.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorWorker.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorChainTest.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorDslTest.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorWorkerTest.kt create mode 100644 ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/TestContext.kt diff --git a/ok-lessons/build.gradle.kts b/ok-lessons/build.gradle.kts index d62429f..64fc973 100644 --- a/ok-lessons/build.gradle.kts +++ b/ok-lessons/build.gradle.kts @@ -3,7 +3,7 @@ plugins { alias(libs.plugins.multiplatform) apply false } -group = "ru.otus.lessons" +group = "ru.otus" version = "0.0.1" subprojects { diff --git a/ok-messenger-be/ok-messenger-api-v1-mappers/src/main/kotlin/MappersV1FromTransport.kt b/ok-messenger-be/ok-messenger-api-v1-mappers/src/main/kotlin/MappersV1FromTransport.kt index e2330ae..2b24c1b 100644 --- a/ok-messenger-be/ok-messenger-api-v1-mappers/src/main/kotlin/MappersV1FromTransport.kt +++ b/ok-messenger-be/ok-messenger-api-v1-mappers/src/main/kotlin/MappersV1FromTransport.kt @@ -6,7 +6,7 @@ import ru.otus.messenger.api.v1.mappers.exceptions.UnknownRequestClass import ru.otus.messenger.api.v1.models.* import ru.otus.messenger.common.models.* import ru.otus.messenger.common.MessengerContext -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs fun MessengerContext.fromTransport(request: IRequest) = when (request) { is ChatCreateRequest -> fromTransport(request) @@ -31,13 +31,13 @@ private fun Debug?.transportToWorkMode(): WorkMode = when (this?.mode) { null -> WorkMode.PROD } -private fun Debug?.transportToStubCase(): Stubs = when (this?.stub) { - DebugStubs.SUCCESS -> Stubs.SUCCESS - DebugStubs.NOT_FOUND -> Stubs.NOT_FOUND - DebugStubs.VALUE_ERROR -> Stubs.VALUE_ERROR - DebugStubs.MISSING_DATA -> Stubs.MISSING_DATA - DebugStubs.CANNOT_DELETE -> Stubs.CANNOT_DELETE - null -> Stubs.NONE +private fun Debug?.transportToStubCase(): MessengerStubs = when (this?.stub) { + DebugStubs.SUCCESS -> MessengerStubs.SUCCESS + DebugStubs.NOT_FOUND -> MessengerStubs.NOT_FOUND + DebugStubs.VALUE_ERROR -> MessengerStubs.VALUE_ERROR + DebugStubs.MISSING_DATA -> MessengerStubs.MISSING_DATA + DebugStubs.CANNOT_DELETE -> MessengerStubs.CANNOT_DELETE + null -> MessengerStubs.NONE } fun MessengerContext.fromTransport(request: ChatCreateRequest) { diff --git a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperCreateTest.kt b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperCreateTest.kt index ff1c06c..d800891 100644 --- a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperCreateTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperCreateTest.kt @@ -20,7 +20,7 @@ import ru.otus.messenger.common.models.ChatType import ru.otus.messenger.common.models.MessengerChat import ru.otus.messenger.common.models.RequestId import ru.otus.messenger.common.models.WorkMode -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs class MapperCreateTest { @Test @@ -42,7 +42,7 @@ class MapperCreateTest { val context = MessengerContext() context.fromTransport(req) - assertEquals(Stubs.SUCCESS, context.stubCase) + assertEquals(MessengerStubs.SUCCESS, context.stubCase) assertEquals(WorkMode.STUB, context.workMode) assertEquals("title", context.chatRequest.title) assertEquals(ChatType.PRIVATE, context.chatRequest.type) diff --git a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperDeleteTest.kt b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperDeleteTest.kt index c2a5beb..4497a5e 100644 --- a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperDeleteTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperDeleteTest.kt @@ -17,7 +17,7 @@ import ru.otus.messenger.common.models.ChatState import ru.otus.messenger.common.models.ChatType import ru.otus.messenger.common.models.RequestId import ru.otus.messenger.common.models.WorkMode -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs class MapperDeleteTest { @Test @@ -33,7 +33,7 @@ class MapperDeleteTest { val context = MessengerContext() context.fromTransport(req) - assertEquals(Stubs.SUCCESS, context.stubCase) + assertEquals(MessengerStubs.SUCCESS, context.stubCase) assertEquals(WorkMode.STUB, context.workMode) assertEquals("", context.chatRequest.title) assertEquals("chat-id", context.chatRequest.id.asString()) diff --git a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperReadTest.kt b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperReadTest.kt index 9ba1b12..480aa3a 100644 --- a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperReadTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperReadTest.kt @@ -19,7 +19,7 @@ import ru.otus.messenger.common.models.ChatType import ru.otus.messenger.common.models.MessengerChat import ru.otus.messenger.common.models.RequestId import ru.otus.messenger.common.models.WorkMode -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs class MapperReadTest { @Test @@ -35,7 +35,7 @@ class MapperReadTest { val context = MessengerContext() context.fromTransport(req) - assertEquals(Stubs.SUCCESS, context.stubCase) + assertEquals(MessengerStubs.SUCCESS, context.stubCase) assertEquals(WorkMode.STUB, context.workMode) assertEquals("", context.chatRequest.title) assertEquals("chat-id", context.chatRequest.id.asString()) diff --git a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperSearchTest.kt b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperSearchTest.kt index f1ef0c5..a5c31f6 100644 --- a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperSearchTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperSearchTest.kt @@ -20,7 +20,7 @@ import ru.otus.messenger.common.models.ChatType import ru.otus.messenger.common.models.MessengerChat import ru.otus.messenger.common.models.RequestId import ru.otus.messenger.common.models.WorkMode -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs class MapperSearchTest { @Test @@ -41,7 +41,7 @@ class MapperSearchTest { val context = MessengerContext() context.fromTransport(req) - assertEquals(Stubs.SUCCESS, context.stubCase) + assertEquals(MessengerStubs.SUCCESS, context.stubCase) assertEquals(WorkMode.STUB, context.workMode) assertEquals("title", context.chatRequest.title) assertEquals(ChatType.PRIVATE, context.chatRequest.type) diff --git a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperUpdateTest.kt b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperUpdateTest.kt index 1ec4671..2ad740a 100644 --- a/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperUpdateTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1-mappers/src/test/kotlin/MapperUpdateTest.kt @@ -20,7 +20,7 @@ import ru.otus.messenger.common.models.ChatType import ru.otus.messenger.common.models.MessengerChat import ru.otus.messenger.common.models.RequestId import ru.otus.messenger.common.models.WorkMode -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs class MapperUpdateTest { @Test @@ -47,7 +47,7 @@ class MapperUpdateTest { val context = MessengerContext() context.fromTransport(req) - assertEquals(Stubs.SUCCESS, context.stubCase) + assertEquals(MessengerStubs.SUCCESS, context.stubCase) assertEquals(WorkMode.STUB, context.workMode) assertEquals("chat-id", context.chatRequest.id.asString()) assertEquals("title", context.chatRequest.title) diff --git a/ok-messenger-be/ok-messenger-api-v1/src/main/kotlin/ApiV1Mapper.kt b/ok-messenger-be/ok-messenger-api-v1/src/main/kotlin/ApiV1Mapper.kt index 285d1ef..62538ee 100644 --- a/ok-messenger-be/ok-messenger-api-v1/src/main/kotlin/ApiV1Mapper.kt +++ b/ok-messenger-be/ok-messenger-api-v1/src/main/kotlin/ApiV1Mapper.kt @@ -1,13 +1,19 @@ package ru.otus.messenger.api.v1 +import com.fasterxml.jackson.annotation.JsonInclude.Include import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.MapperFeature import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.module.kotlin.KotlinModule +import java.text.SimpleDateFormat import ru.otus.messenger.api.v1.models.IRequest import ru.otus.messenger.api.v1.models.IResponse val apiV1Mapper: JsonMapper = JsonMapper.builder().run { + addModule(KotlinModule.Builder().build()) configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + defaultDateFormat(SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")) + serializationInclusion(Include.NON_NULL) enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL) build() } diff --git a/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/RequestV1SerializationTest.kt b/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/RequestV1SerializationTest.kt index 1433b59..1fa8dab 100644 --- a/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/RequestV1SerializationTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/RequestV1SerializationTest.kt @@ -16,7 +16,7 @@ class RequestV1SerializationTest { title = "New chat", type = ChatCreateRequestAllOfChat.Type.GROUP, mode = ChatCreateRequestAllOfChat.Mode.PERSONAL, - participants = emptyList(), + participants = emptySet(), metadata = """ { "isOwner": true, diff --git a/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/ResponseV1SerializationTest.kt b/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/ResponseV1SerializationTest.kt index 90e756c..e23a685 100644 --- a/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/ResponseV1SerializationTest.kt +++ b/ok-messenger-be/ok-messenger-api-v1/src/test/kotlin/ResponseV1SerializationTest.kt @@ -16,7 +16,7 @@ class ResponseV1SerializationTest { title = "Test chat title", type = Chat.Type.CHANNEL, mode = Chat.Mode.WORK, - participants = emptyList(), + participants = emptySet(), createdAt = Instant.now().toString(), isArchived = false, metadata = null diff --git a/ok-messenger-be/ok-messenger-app/src/test/kotlin/stub/V1StubApiTest.kt b/ok-messenger-be/ok-messenger-app/src/test/kotlin/stub/V1StubApiTest.kt index 52457e0..6b772d5 100644 --- a/ok-messenger-be/ok-messenger-app/src/test/kotlin/stub/V1StubApiTest.kt +++ b/ok-messenger-be/ok-messenger-app/src/test/kotlin/stub/V1StubApiTest.kt @@ -1,2 +1,133 @@ package ru.otus.messenger.app.stub +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.SerializationFeature +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 java.util.UUID +import ru.otus.messenger.api.v1.models.ChatCreateRequest +import ru.otus.messenger.api.v1.models.ChatCreateRequestAllOfChat +import ru.otus.messenger.api.v1.models.ChatCreateResponse +import ru.otus.messenger.api.v1.models.ChatDeleteRequest +import ru.otus.messenger.api.v1.models.ChatDeleteResponse +import ru.otus.messenger.api.v1.models.ChatReadRequest +import ru.otus.messenger.api.v1.models.ChatReadResponse +import ru.otus.messenger.api.v1.models.ChatSearchRequest +import ru.otus.messenger.api.v1.models.ChatSearchRequestAllOfCriteria +import ru.otus.messenger.api.v1.models.ChatSearchResponse +import ru.otus.messenger.api.v1.models.Debug +import ru.otus.messenger.api.v1.models.DebugMode +import ru.otus.messenger.api.v1.models.DebugStubs +import ru.otus.messenger.api.v1.models.IRequest +import ru.otus.messenger.api.v1.models.ResponseResult +import ru.otus.messenger.app.common.MessengerAppSettingsData +import ru.otus.messenger.app.module +import ru.otus.messenger.common.MessengerCorSettings +import kotlin.test.Test +import kotlin.test.assertEquals +import ru.otus.messenger.stubs.MessengerChatStubSample + +class V1StubApiTest { + private val chatId = UUID.randomUUID().toString() + private val participants = (MessengerChatStubSample.participants.map { it.asString() } + MessengerChatStubSample.chatOwnerId).toSet() + + @Test + fun create() = v1TestApplication( + func = "create", + request = ChatCreateRequest( + chat = ChatCreateRequestAllOfChat( + title = "New chat", + description = "New chat description", + ), + debug = Debug( + mode = DebugMode.STUB, + stub = DebugStubs.SUCCESS + ) + ), + ) { response -> + val responseObj = response.body() + assertEquals(200, response.status.value) + assertEquals(MessengerChatStubSample.chatOwnerId, responseObj.chat?.ownerId) + assertEquals("New chat", responseObj.chat?.title) + assertEquals(participants, responseObj.chat?.participants) + } + + @Test + fun read() = v1TestApplication( + func = "read", + request = ChatReadRequest( + chatId = MessengerChatStubSample.chatId, + debug = Debug( + mode = DebugMode.STUB, + stub = DebugStubs.SUCCESS + ) + ), + ) { response -> + val responseObj = response.body() + assertEquals(200, response.status.value) + assertEquals(MessengerChatStubSample.chatId, responseObj.chat?.id) + } + + @Test + fun delete() = v1TestApplication( + func = "delete", + request = ChatDeleteRequest( + chatId = chatId, + debug = Debug( + mode = DebugMode.STUB, + stub = DebugStubs.SUCCESS + ) + ), + ) { response -> + val responseObj = response.body() + assertEquals(200, response.status.value) + assertEquals(ResponseResult.SUCCESS, responseObj.result) + } + + @Test + fun search() = v1TestApplication( + func = "search", + request = ChatSearchRequest( + criteria = ChatSearchRequestAllOfCriteria( + title = "Chat search title", + type = ChatSearchRequestAllOfCriteria.Type.GROUP, + ), + debug = Debug( + mode = DebugMode.STUB, + stub = DebugStubs.SUCCESS + ) + ), + ) { response -> + val responseObj = response.body() + assertEquals(200, response.status.value) + assertEquals(2, responseObj.chats?.size) + assertEquals("Chat search title", responseObj.chats?.first()?.title) + } + + private fun v1TestApplication( + func: String, + request: IRequest, + function: suspend (HttpResponse) -> Unit, + ): Unit = testApplication { + application { module(MessengerAppSettingsData(corSettings = MessengerCorSettings())) } + val client = createClient { + install(ContentNegotiation) { + jackson { + disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + enable(SerializationFeature.INDENT_OUTPUT) + writerWithDefaultPrettyPrinter() + } + } + } + val response = client.post("/v1/chat/$func") { + contentType(ContentType.Application.Json) + setBody(request) + } + function(response) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/build.gradle.kts b/ok-messenger-be/ok-messenger-biz/build.gradle.kts index e4e2ef4..3e5af8d 100644 --- a/ok-messenger-be/ok-messenger-biz/build.gradle.kts +++ b/ok-messenger-be/ok-messenger-biz/build.gradle.kts @@ -4,8 +4,13 @@ plugins { dependencies { implementation(kotlin("stdlib")) + implementation(libs.kotlin.coroutines) + implementation(libs.kotlin.datetime) + implementation(libs.kotlinx.serialization.json) implementation(project(":ok-messenger-common")) implementation(project(":ok-messenger-stubs")) + implementation("ru.otus.messenger.libs:ok-messenger-lib-cor") testImplementation(kotlin("test-junit")) + testImplementation(libs.kotlin.coroutines.test) } \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/MessengerProcessor.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/MessengerProcessor.kt index d4e797d..df5459c 100644 --- a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/MessengerProcessor.kt +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/MessengerProcessor.kt @@ -1,17 +1,145 @@ package ru.otus.messenger.biz +import ru.otus.messenger.biz.general.initStatus +import ru.otus.messenger.biz.general.operation +import ru.otus.messenger.biz.general.stubs +import ru.otus.messenger.biz.general.validation +import ru.otus.messenger.biz.stubs.stubCreateSuccess +import ru.otus.messenger.biz.stubs.stubReadSuccess +import ru.otus.messenger.biz.stubs.stubUpdateSuccess +import ru.otus.messenger.biz.stubs.stubDbError +import ru.otus.messenger.biz.stubs.stubDeleteSuccess +import ru.otus.messenger.biz.stubs.stubNoCase +import ru.otus.messenger.biz.stubs.stubSearchSuccess +import ru.otus.messenger.biz.stubs.stubValidationBadDescription +import ru.otus.messenger.biz.stubs.stubValidationBadId +import ru.otus.messenger.biz.stubs.stubValidationBadTitle +import ru.otus.messenger.biz.validation.finishChatFilterValidation +import ru.otus.messenger.biz.validation.finishChatValidation +import ru.otus.messenger.biz.validation.validateDescriptionHasContent +import ru.otus.messenger.biz.validation.validateDescriptionNotEmpty +import ru.otus.messenger.biz.validation.validateIdNotEmpty +import ru.otus.messenger.biz.validation.validateIdProperFormat +import ru.otus.messenger.biz.validation.validateSearchStringLength +import ru.otus.messenger.biz.validation.validateTitleHasContent +import ru.otus.messenger.biz.validation.validateTitleNotEmpty import ru.otus.messenger.common.MessengerContext import ru.otus.messenger.common.MessengerCorSettings -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.stubs.MessengerChatStub +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatId +import ru.otus.messenger.cor.dsl.rootChain +import ru.otus.messenger.cor.dsl.worker -@Suppress("unused", "RedundantSuspendModifier") -class MessengerProcessor(val corSettings: MessengerCorSettings) { - suspend fun exec(ctx: MessengerContext) { - ctx.chatResponse = MessengerChatStub.get() - ctx.chatsResponse = MessengerChatStub.prepareSearchList("New chat", ChatType.GROUP, ChatMode.PERSONAL).toMutableList() - ctx.state = ChatState.RUNNING - } +class MessengerProcessor( + private val corSettings: MessengerCorSettings = MessengerCorSettings.NONE +) { + suspend fun exec(ctx: MessengerContext) = businessChain.exec( + ctx.also { it.corSettings = corSettings } + ) + + private val businessChain = rootChain { + initStatus("Инициализация статуса") + + operation("Создание чата", ChatCommand.CREATE) { + stubs("Обработка стабов") { + stubCreateSuccess("Имитация успешной обработки", corSettings) + stubValidationBadTitle("Имитация ошибки валидации заголовка") + stubValidationBadDescription("Имитация ошибки валидации описания") + stubDbError("Имитация ошибки работы с БД") + stubNoCase("Ошибка: запрошенный стаб недопустим") + } + + validation { + worker("Копируем поля в chatValidating") { chatValidating = chatRequest.deepCopy() } + worker("Очистка id") { chatValidating.id = ChatId.NONE } + worker("Очистка заголовка") { chatValidating.title = chatValidating.title.trim() } + worker("Очистка описания") { chatValidating.description = chatValidating.description.trim() } + validateTitleNotEmpty("Проверка, что заголовок не пуст") + validateTitleHasContent("Проверка символов") + validateDescriptionNotEmpty("Проверка, что описание не пусто") + validateDescriptionHasContent("Проверка символов") + + finishChatValidation("Завершение проверок") + } + } + + operation("Получить чат", ChatCommand.READ) { + stubs("Обработка стабов") { + stubReadSuccess("Имитация успешной обработки", corSettings) + stubValidationBadId("Имитация ошибки валидации id") + stubDbError("Имитация ошибки работы с БД") + stubNoCase("Ошибка: запрошенный стаб недопустим") + } + + validation { + worker("Копируем поля в chatValidating") { chatValidating = chatRequest.deepCopy() } + worker("Очистка id") { chatValidating.id = ChatId(chatValidating.id.asString().trim()) } + validateIdNotEmpty("Проверка на непустой id") + validateIdProperFormat("Проверка формата id") + + finishChatValidation("Успешное завершение процедуры валидации") + } + } + + operation("Изменить чат", ChatCommand.UPDATE) { + stubs("Обработка стабов") { + stubUpdateSuccess("Имитация успешной обработки", corSettings) + stubValidationBadId("Имитация ошибки валидации id") + stubValidationBadTitle("Имитация ошибки валидации заголовка") + stubValidationBadDescription("Имитация ошибки валидации описания") + stubDbError("Имитация ошибки работы с БД") + stubNoCase("Ошибка: запрошенный стаб недопустим") + } + + validation { + worker("Копируем поля в chatValidating") { chatValidating = chatRequest.deepCopy() } + worker("Очистка id") { chatValidating.id = ChatId(chatValidating.id.asString().trim()) } + worker("Очистка заголовка") { chatValidating.title = chatValidating.title.trim() } + worker("Очистка описания") { chatValidating.description = chatValidating.description.trim() } + validateIdNotEmpty("Проверка на непустой id") + validateIdProperFormat("Проверка формата id") + validateTitleNotEmpty("Проверка на непустой заголовок") + validateTitleHasContent("Проверка на наличие содержания в заголовке") + validateDescriptionNotEmpty("Проверка на непустое описание") + validateDescriptionHasContent("Проверка на наличие содержания в описании") + + finishChatValidation("Успешное завершение процедуры валидации") + } + } + + operation("Удалить чат", ChatCommand.DELETE) { + stubs("Обработка стабов") { + stubDeleteSuccess("Имитация успешной обработки", corSettings) + stubValidationBadId("Имитация ошибки валидации id") + stubDbError("Имитация ошибки работы с БД") + stubNoCase("Ошибка: запрошенный стаб недопустим") + } + + validation { + worker("Копируем поля в chatValidating") { + chatValidating = chatRequest.deepCopy() + } + worker("Очистка id") { chatValidating.id = ChatId(chatValidating.id.asString().trim()) } + validateIdNotEmpty("Проверка на непустой id") + validateIdProperFormat("Проверка формата id") + finishChatValidation("Успешное завершение процедуры валидации") + } + } + + operation("Поиск чата", ChatCommand.SEARCH) { + stubs("Обработка стабов") { + stubSearchSuccess("Имитация успешной обработки", corSettings) + stubValidationBadId("Имитация ошибки валидации id") + stubDbError("Имитация ошибки работы с БД") + stubNoCase("Ошибка: запрошенный стаб недопустим") + } + + validation { + worker("Копируем поля в chatFilterValidating") { chatFilterValidating = chatFilterRequest.deepCopy() } + validateSearchStringLength("Валидация длины строки поиска в фильтре") + + finishChatFilterValidation("Успешное завершение процедуры валидации") + } + } + }.build() } \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/InitStatus.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/InitStatus.kt new file mode 100644 index 0000000..6648bef --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/InitStatus.kt @@ -0,0 +1,15 @@ +package ru.otus.messenger.biz.general + +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.initStatus(title: String) = worker { + this.title = title + this.description = """ + Этот обработчик устанавливает стартовый статус обработки. Запускается только в случае не заданного статуса. + """.trimIndent() + on { state == ChatState.NONE } + handle { state = ChatState.RUNNING } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Operation.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Operation.kt new file mode 100644 index 0000000..ce722d2 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Operation.kt @@ -0,0 +1,17 @@ +package ru.otus.messenger.biz.general + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.chain + +fun ICorChainDsl.operation( + title: String, + command: ChatCommand, + block: ICorChainDsl.() -> Unit +) = chain { + block() + this.title = title + on { this.command == command && state == ChatState.RUNNING } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Stubs.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Stubs.kt new file mode 100644 index 0000000..e254f03 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Stubs.kt @@ -0,0 +1,13 @@ +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.chain + +fun ICorChainDsl.stubs(title: String = "Обработка стабов", block: ICorChainDsl.() -> Unit) = chain { + block() + this.title = title + on { workMode == WorkMode.STUB && state == ChatState.RUNNING } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Validation.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Validation.kt new file mode 100644 index 0000000..8ba5d3a --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/general/Validation.kt @@ -0,0 +1,12 @@ +package ru.otus.messenger.biz.general + +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.chain + +fun ICorChainDsl.validation(block: ICorChainDsl.() -> Unit) = chain { + block() + title = "Валидация" + on { state == ChatState.RUNNING } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubCreateSuccess.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubCreateSuccess.kt new file mode 100644 index 0000000..213a9e1 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubCreateSuccess.kt @@ -0,0 +1,33 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.MessengerCorSettings +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.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker +import ru.otus.messenger.logging.common.LogLevel +import ru.otus.messenger.stubs.MessengerChatStub + +fun ICorChainDsl.stubCreateSuccess(title: String, corSettings: MessengerCorSettings) = worker { + this.title = title + this.description = """ + Кейс успеха для создания чата + """.trimIndent() + on { stubCase == MessengerStubs.SUCCESS && state == ChatState.RUNNING } + val logger = corSettings.loggerProvider.logger("stubCreateSuccess") + handle { + logger.doWithLogging(id = this.requestId.asString(), LogLevel.DEBUG) { + state = ChatState.FINISHING + val stub = MessengerChatStub.prepareResult { + chatRequest.title.takeIf { it.isNotBlank() }?.also { this.title = it } + chatRequest.description.takeIf { it.isNotBlank() }?.also { this.description = it } + chatRequest.type.takeIf { it != ChatType.NONE }?.also { this.type = it } + chatRequest.mode.takeIf { it != ChatMode.NONE }?.also { this.mode = it } + } + chatResponse = stub + } + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDbError.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDbError.kt new file mode 100644 index 0000000..ceac76c --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDbError.kt @@ -0,0 +1,26 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.stubDbError(title: String) = worker { + this.title = title + this.description = """ + Кейс ошибки базы данных + """.trimIndent() + on { stubCase == MessengerStubs.DB_ERROR && state == ChatState.RUNNING } + handle { + fail( + ChatError( + group = "internal", + code = "internal-db", + message = "Internal error" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDeleteSuccess.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDeleteSuccess.kt new file mode 100644 index 0000000..19cc744 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubDeleteSuccess.kt @@ -0,0 +1,28 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.MessengerCorSettings +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker +import ru.otus.messenger.logging.common.LogLevel +import ru.otus.messenger.stubs.MessengerChatStub + +fun ICorChainDsl.stubDeleteSuccess(title: String, corSettings: MessengerCorSettings) = worker { + this.title = title + this.description = """ + Кейс успеха для удаления объявления + """.trimIndent() + on { stubCase == MessengerStubs.SUCCESS && state == ChatState.RUNNING } + val logger = corSettings.loggerProvider.logger("stubDeleteSuccess") + handle { + logger.doWithLogging(id = this.requestId.asString(), LogLevel.DEBUG) { + state = ChatState.FINISHING + val stub = MessengerChatStub.prepareResult { + chatRequest.title.takeIf { it.isNotBlank() }?.also { this.title = it } + } + chatResponse = stub + } + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNoCase.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNoCase.kt new file mode 100644 index 0000000..6192bea --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNoCase.kt @@ -0,0 +1,26 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.stubNoCase(title: String) = worker { + this.title = title + this.description = """ + Валидируем ситуацию, когда запрошен кейс, который не поддерживается в стабах + """.trimIndent() + on { state == ChatState.RUNNING } + handle { + fail( + ChatError( + code = "validation", + field = "stub", + group = "validation", + message = "Wrong stub case is requested: ${stubCase.name}" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNotFound.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNotFound.kt new file mode 100644 index 0000000..386f01b --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubNotFound.kt @@ -0,0 +1,28 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.stubNotFound(title: String) = worker { + this.title = title + this.description = """ + Entity of interest were not found + """.trimIndent() + + on { stubCase == MessengerStubs.NOT_FOUND && state == ChatState.RUNNING } + + handle { + fail( + ChatError( + group = "internal", + code = "internal-db", + message = "Entity not found error" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubReadSuccess.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubReadSuccess.kt new file mode 100644 index 0000000..2bea27d --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubReadSuccess.kt @@ -0,0 +1,28 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.MessengerCorSettings +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker +import ru.otus.messenger.logging.common.LogLevel +import ru.otus.messenger.stubs.MessengerChatStub + +fun ICorChainDsl.stubReadSuccess(title: String, corSettings: MessengerCorSettings) = worker { + this.title = title + this.description = """ + Кейс успеха для чтения чата + """.trimIndent() + on { stubCase == MessengerStubs.SUCCESS && state == ChatState.RUNNING } + val logger = corSettings.loggerProvider.logger("stubReadSuccess") + handle { + logger.doWithLogging(id = this.requestId.asString(), LogLevel.DEBUG) { + state = ChatState.FINISHING + val stub = MessengerChatStub.prepareResult { + chatRequest.title.takeIf { it.isNotBlank() }?.also { this.title = it } + } + chatResponse = stub + } + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubSearchSuccess.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubSearchSuccess.kt new file mode 100644 index 0000000..9fc1dab --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubSearchSuccess.kt @@ -0,0 +1,32 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.MessengerCorSettings +import ru.otus.messenger.common.models.ChatSearchFilter.StringSearchField +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.ChatType +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker +import ru.otus.messenger.logging.common.LogLevel +import ru.otus.messenger.stubs.MessengerChatStub + +fun ICorChainDsl.stubSearchSuccess(title: String, corSettings: MessengerCorSettings) = worker { + this.title = title + this.description = """ + Кейс успеха для поиска объявлений + """.trimIndent() + on { stubCase == MessengerStubs.SUCCESS && state == ChatState.RUNNING } + val logger = corSettings.loggerProvider.logger("stubSearchSuccess") + handle { + logger.doWithLogging(id = this.requestId.asString(), LogLevel.DEBUG) { + state = ChatState.FINISHING + chatsResponse.addAll( + MessengerChatStub.prepareSearchList( + (chatFilterRequest.searchFields.first() as StringSearchField).stringValue, + ChatType.GROUP + ) + ) + } + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubUpdateSuccess.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubUpdateSuccess.kt new file mode 100644 index 0000000..4472103 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubUpdateSuccess.kt @@ -0,0 +1,35 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.MessengerCorSettings +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.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker +import ru.otus.messenger.logging.common.LogLevel +import ru.otus.messenger.stubs.MessengerChatStub + +fun ICorChainDsl.stubUpdateSuccess(title: String, corSettings: MessengerCorSettings) = worker { + this.title = title + this.description = """ + Кейс успеха для изменения объявления + """.trimIndent() + on { stubCase == MessengerStubs.SUCCESS && state == ChatState.RUNNING } + val logger = corSettings.loggerProvider.logger("stubUpdateSuccess") + handle { + logger.doWithLogging(id = this.requestId.asString(), LogLevel.DEBUG) { + state = ChatState.FINISHING + val stub = MessengerChatStub.prepareResult { + chatRequest.id.takeIf { it != ChatId.NONE }?.also { this.id = it } + chatRequest.title.takeIf { it.isNotBlank() }?.also { this.title = it } + chatRequest.description.takeIf { it.isNotBlank() }?.also { this.description = it } + chatRequest.type.takeIf { it != ChatType.NONE }?.also { this.type = it } + chatRequest.mode.takeIf { it != ChatMode.NONE }?.also { this.mode = it } + } + chatResponse = stub + } + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadDescription.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadDescription.kt new file mode 100644 index 0000000..a2e4740 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadDescription.kt @@ -0,0 +1,27 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.stubValidationBadDescription(title: String) = worker { + this.title = title + this.description = """ + Кейс ошибки валидации для описания чата + """.trimIndent() + on { stubCase == MessengerStubs.BAD_DESCRIPTION && state == ChatState.RUNNING } + handle { + fail( + ChatError( + group = "validation", + code = "validation-description", + field = "description", + message = "Wrong description field" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadId.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadId.kt new file mode 100644 index 0000000..5591d4b --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadId.kt @@ -0,0 +1,27 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.stubValidationBadId(title: String) = worker { + this.title = title + this.description = """ + Кейс ошибки валидации для идентификатора объявления + """.trimIndent() + on { stubCase == MessengerStubs.BAD_ID && state == ChatState.RUNNING } + handle { + fail( + ChatError( + group = "validation", + code = "validation-id", + field = "id", + message = "Wrong id field" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadTitle.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadTitle.kt new file mode 100644 index 0000000..94112fc --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/stubs/StubValidationBadTitle.kt @@ -0,0 +1,27 @@ +package ru.otus.messenger.biz.stubs + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.stubValidationBadTitle(title: String) = worker { + this.title = title + this.description = """ + Кейс ошибки валидации для заголовка чата + """.trimIndent() + on { stubCase == MessengerStubs.BAD_TITLE && state == ChatState.RUNNING } + handle { + fail( + ChatError( + group = "validation", + code = "validation-title", + field = "title", + message = "Wrong title field" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/FinishValidation.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/FinishValidation.kt new file mode 100644 index 0000000..df58bdb --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/FinishValidation.kt @@ -0,0 +1,23 @@ +package ru.otus.messenger.biz.validation + +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.finishChatValidation(title: String) = worker { + this.title = title + on { state == ChatState.RUNNING } + handle { + chatValidated = chatValidating + } +} + +fun ICorChainDsl.finishChatFilterValidation(title: String) = worker { + this.title = title + on { state == ChatState.RUNNING } + handle { + chatFilterValidated = chatFilterValidating + } +} + diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionHasContent.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionHasContent.kt new file mode 100644 index 0000000..12648cb --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionHasContent.kt @@ -0,0 +1,22 @@ +package ru.otus.messenger.biz.validation + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateDescriptionHasContent(title: String) = worker { + this.title = title + val regExp = Regex("\\p{L}") + on { chatValidating.description.isNotEmpty() && !chatValidating.description.contains(regExp) } + handle { + fail( + errorValidation( + field = "description", + violationCode = "noContent", + description = "field must contain letters" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionNotEmpty.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionNotEmpty.kt new file mode 100644 index 0000000..741f43e --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateDescriptionNotEmpty.kt @@ -0,0 +1,21 @@ +package ru.otus.messenger.biz.validation + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateDescriptionNotEmpty(title: String) = worker { + this.title = title + on { chatValidating.description.isEmpty() } + handle { + fail( + errorValidation( + field = "description", + violationCode = "empty", + description = "field must not be empty" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdNotEmpty.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdNotEmpty.kt new file mode 100644 index 0000000..7785c46 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdNotEmpty.kt @@ -0,0 +1,21 @@ +package ru.otus.messenger.biz.validation + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateIdNotEmpty(title: String) = worker { + this.title = title + on { chatValidating.id.asString().isEmpty() } + handle { + fail( + errorValidation( + field = "id", + violationCode = "empty", + description = "field must not be empty" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdProperFormat.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdProperFormat.kt new file mode 100644 index 0000000..f8567a4 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateIdProperFormat.kt @@ -0,0 +1,28 @@ +package ru.otus.messenger.biz.validation + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatId +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateIdProperFormat(title: String) = worker { + this.title = title + + // Может быть вынесен в ChatId для реализации различных форматов + val regExp = Regex("^[0-9a-zA-Z#:-]+$") + on { chatValidating.id != ChatId.NONE && ! chatValidating.id.asString().matches(regExp) } + handle { + val encodedId = chatValidating.id.asString() + .replace("<", "<") + .replace(">", ">") + fail( + errorValidation( + field = "id", + violationCode = "badFormat", + description = "value $encodedId must contain only letters and numbers" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateSearchStringLength.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateSearchStringLength.kt new file mode 100644 index 0000000..13f2442 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateSearchStringLength.kt @@ -0,0 +1,53 @@ +package ru.otus.messenger.biz.validation + +import kotlin.collections.first +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.common.models.ChatSearchFilter.StringSearchField +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.chain +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateSearchStringLength(title: String) = chain { + this.title = title + this.description = """ + Валидация длины строки поиска в поисковых фильтрах. Допустимые значения: + - null - не выполняем поиск по строке + - 3-100 - допустимая длина + - больше 100 - слишком длинная строка + """.trimIndent() + on { state == ChatState.RUNNING } + worker("Обрезка пустых символов") { + (chatFilterValidating.searchFields.first() as StringSearchField).stringValue = + (chatFilterValidating.searchFields.first() as StringSearchField).stringValue.trim() } + worker { + this.title = "Проверка кейса длины на 0-2 символа" + this.description = this.title + on { state == ChatState.RUNNING && (chatFilterValidating.searchFields.first() as StringSearchField).stringValue.length in (1..2) } + handle { + fail( + errorValidation( + field = "searchString", + violationCode = "tooShort", + description = "Search string must contain at least 3 symbols" + ) + ) + } + } + worker { + this.title = "Проверка кейса длины на более 100 символов" + this.description = this.title + on { state == ChatState.RUNNING && (chatFilterValidating.searchFields.first() as StringSearchField).stringValue.length > 100 } + handle { + fail( + errorValidation( + field = "searchString", + violationCode = "tooLong", + description = "Search string must be no more than 100 symbols long" + ) + ) + } + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleHasContent.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleHasContent.kt new file mode 100644 index 0000000..5930d9f --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleHasContent.kt @@ -0,0 +1,26 @@ +package ru.otus.messenger.biz.validation + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateTitleHasContent(title: String) = worker { + this.title = title + this.description = """ + Проверяем, что у нас есть какие-то слова в заголовке. + Отказываем в публикации заголовков, в которых только бессмысленные символы типа %^&^$^%#^))&^*&%^^& + """.trimIndent() + val regExp = Regex("\\p{L}") + on { chatValidating.title.isNotEmpty() && ! chatValidating.title.contains(regExp) } + handle { + fail( + errorValidation( + field = "title", + violationCode = "noContent", + description = "field must contain letters" + ) + ) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleNotEmpty.kt b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleNotEmpty.kt new file mode 100644 index 0000000..0c2016f --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/main/kotlin/validation/ValidateTitleNotEmpty.kt @@ -0,0 +1,21 @@ +package ru.otus.messenger.biz.validation + +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.helpers.errorValidation +import ru.otus.messenger.common.helpers.fail +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.worker + +fun ICorChainDsl.validateTitleNotEmpty(title: String) = worker { + this.title = title + on { chatValidating.title.isEmpty() } + handle { + fail( + errorValidation( + field = "title", + violationCode = "empty", + description = "field must not be empty" + ) + ) + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/package.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/package.kt new file mode 100644 index 0000000..7bcbbe4 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/package.kt @@ -0,0 +1 @@ +package ru.otus.messenger.biz \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerCreateStubTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerCreateStubTest.kt new file mode 100644 index 0000000..d10adc6 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerCreateStubTest.kt @@ -0,0 +1,94 @@ +package ru.otus.messenger.biz.stub + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put +import ru.otus.messenger.biz.MessengerProcessor +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.MessengerChat +import ru.otus.messenger.common.models.WorkMode +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.stubs.MessengerChatStub + +class MessengerCreateStubTest { + private val processor = MessengerProcessor() + private val timestamp = Instant.parse("2025-01-31T06:30:00Z") + private val content = buildJsonObject { + put("sampleId", "uuid4") + put("testParam", "test") + } + + @Test + fun create() = runTest { + val context = MessengerContext( + command = ChatCommand.CREATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.SUCCESS, + ) + processor.exec(context) + assertEquals(MessengerChatStub.get().title, context.chatResponse.title) + assertEquals(timestamp, context.chatResponse.createdAt) + assertEquals(content.toString(), context.chatResponse.metadata.asString()) + } + + @Test + fun badTitle() = runTest { + val context = MessengerContext( + command = ChatCommand.CREATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_TITLE, + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("title", context.errors.firstOrNull()?.field) + assertEquals("validation", context.errors.firstOrNull()?.group) + } + + @Test + fun badDescription() = runTest { + val context = MessengerContext( + command = ChatCommand.CREATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_DESCRIPTION, + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("description", context.errors.firstOrNull()?.field) + assertEquals("validation", context.errors.firstOrNull()?.group) + } + + @Test + fun databaseError() = runTest { + val context = MessengerContext( + command = ChatCommand.CREATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.DB_ERROR, + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("internal", context.errors.firstOrNull()?.group) + } + + @Test + fun badNoCase() = runTest { + val context = MessengerContext( + command = ChatCommand.CREATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.NOT_FOUND, + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("stub", context.errors.firstOrNull()?.field) + assertEquals("validation", context.errors.firstOrNull()?.group) + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerDeleteStubTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerDeleteStubTest.kt new file mode 100644 index 0000000..7e19eb3 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerDeleteStubTest.kt @@ -0,0 +1,75 @@ +package ru.otus.messenger.biz.stub + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import ru.otus.messenger.biz.MessengerProcessor +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.MessengerChat +import ru.otus.messenger.common.models.WorkMode +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.stubs.MessengerChatStub + +class MessengerDeleteStubTest { + private val processor = MessengerProcessor() + + @Test + fun delete() = runTest { + val context = MessengerContext( + command = ChatCommand.DELETE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.SUCCESS, + ) + processor.exec(context) + + val stub = MessengerChatStub.get() + assertEquals(stub.id, context.chatResponse.id) + assertEquals(stub.title, context.chatResponse.title) + assertEquals(stub.type, context.chatResponse.type) + assertEquals(stub.mode, context.chatResponse.mode) + } + + @Test + fun badNoCase() = runTest { + val context = MessengerContext( + command = ChatCommand.DELETE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.NOT_FOUND, + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("stub", context.errors.firstOrNull()?.field) + assertEquals("validation", context.errors.firstOrNull()?.group) + } + + @Test + fun badId() = runTest { + val context = MessengerContext( + command = ChatCommand.DELETE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_ID + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("id", context.errors.firstOrNull()?.field) + assertEquals("validation", context.errors.firstOrNull()?.group) + } + + @Test + fun databaseError() = runTest { + val context = MessengerContext( + command = ChatCommand.DELETE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.DB_ERROR, + ) + processor.exec(context) + assertEquals(MessengerChat(), context.chatResponse) + assertEquals("internal", context.errors.firstOrNull()?.group) + } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerReadStubTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerReadStubTest.kt new file mode 100644 index 0000000..dc85538 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerReadStubTest.kt @@ -0,0 +1,76 @@ +package ru.otus.messenger.biz.stub + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import ru.otus.messenger.biz.MessengerProcessor +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.MessengerChat +import ru.otus.messenger.common.models.WorkMode +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.stubs.MessengerChatStub + +class MessengerReadStubTest { + private val processor = MessengerProcessor() + + @Test + fun read() = runTest { + + val ctx = MessengerContext( + command = ChatCommand.READ, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.SUCCESS, + ) + processor.exec(ctx) + with (MessengerChatStub.get()) { + assertEquals(id, ctx.chatResponse.id) + assertEquals(title, ctx.chatResponse.title) + assertEquals(description, ctx.chatResponse.description) + assertEquals(type, ctx.chatResponse.type) + assertEquals(mode, ctx.chatResponse.mode) + } + } + + @Test + fun badId() = runTest { + val ctx = MessengerContext( + command = ChatCommand.READ, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_ID, + ) + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("id", ctx.errors.firstOrNull()?.field) + assertEquals("validation", ctx.errors.firstOrNull()?.group) + } + + @Test + fun databaseError() = runTest { + val ctx = MessengerContext( + command = ChatCommand.READ, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.DB_ERROR, + ) + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("internal", ctx.errors.firstOrNull()?.group) + } + + @Test + fun badNoCase() = runTest { + val ctx = MessengerContext( + command = ChatCommand.READ, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_TITLE, + ) + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("stub", ctx.errors.firstOrNull()?.field) + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerSearchStubTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerSearchStubTest.kt new file mode 100644 index 0000000..582666d --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerSearchStubTest.kt @@ -0,0 +1,91 @@ +package ru.otus.messenger.biz.stub + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.fail +import kotlinx.coroutines.test.runTest +import ru.otus.messenger.biz.MessengerProcessor +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatSearchFilter +import ru.otus.messenger.common.models.ChatSearchFilter.StringSearchField +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.MessengerChat +import ru.otus.messenger.common.models.WorkMode +import ru.otus.messenger.common.stubs.MessengerStubs +import ru.otus.messenger.stubs.MessengerChatStub + +class MessengerSearchStubTest { + private val processor = MessengerProcessor() + val filter = ChatSearchFilter( + searchFields = listOf( + StringSearchField(fieldName = "title", stringValue = "New chat" ), + StringSearchField(fieldName = "description", stringValue = "New chat description" ) + ) + ) + + @Test + fun read() = runTest { + + val ctx = MessengerContext( + command = ChatCommand.SEARCH, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.SUCCESS, + chatFilterRequest = filter, + ) + processor.exec(ctx) + assertTrue(ctx.chatsResponse.size > 1) + val first = ctx.chatsResponse.firstOrNull() ?: fail("Empty response list") + assertTrue(first.title.contains((filter.searchFields[0] as StringSearchField).stringValue)) + assertTrue(first.description.contains((filter.searchFields[1] as StringSearchField).stringValue)) + with(MessengerChatStub.get()) { + assertEquals(type, first.type) + assertEquals(mode, first.mode) + } + } + + @Test + fun badId() = runTest { + val ctx = MessengerContext( + command = ChatCommand.SEARCH, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_ID, + chatFilterRequest = filter, + ) + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("id", ctx.errors.firstOrNull()?.field) + assertEquals("validation", ctx.errors.firstOrNull()?.group) + } + + @Test + fun databaseError() = runTest { + val ctx = MessengerContext( + command = ChatCommand.SEARCH, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.DB_ERROR, + chatFilterRequest = filter, + ) + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("internal", ctx.errors.firstOrNull()?.group) + } + + @Test + fun badNoCase() = runTest { + val ctx = MessengerContext( + command = ChatCommand.SEARCH, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_TITLE, + chatFilterRequest = filter, + ) + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("stub", ctx.errors.firstOrNull()?.field) + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerUpdateStubTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerUpdateStubTest.kt new file mode 100644 index 0000000..d01c928 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/stub/MessengerUpdateStubTest.kt @@ -0,0 +1,117 @@ +package ru.otus.messenger.biz.stub + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import ru.otus.messenger.biz.MessengerProcessor +import ru.otus.messenger.common.MessengerContext +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.stubs.MessengerStubs +import ru.otus.messenger.stubs.MessengerChatStubSample + +class MessengerUpdateStubTest { + private val processor = MessengerProcessor() + val id = ChatId(MessengerChatStubSample.chatId) + val title = "New chat" + val description = "New chat description" + val type = ChatType.GROUP + val mode = ChatMode.PERSONAL + + @Test + fun create() = runTest { + + val ctx = MessengerContext( + command = ChatCommand.UPDATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.SUCCESS + ) + + processor.exec(ctx) + assertEquals(id, ctx.chatResponse.id) + assertEquals(title, ctx.chatResponse.title) + assertEquals(description, ctx.chatResponse.description) + assertEquals(type, ctx.chatResponse.type) + assertEquals(mode, ctx.chatResponse.mode) + } + + @Test + fun badId() = runTest { + val ctx = MessengerContext( + command = ChatCommand.UPDATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_ID, + chatRequest = MessengerChat(), + ) + + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("id", ctx.errors.firstOrNull()?.field) + assertEquals("validation", ctx.errors.firstOrNull()?.group) + } + + @Test + fun badTitle() = runTest { + val ctx = MessengerContext( + command = ChatCommand.UPDATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_TITLE, + ) + + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("title", ctx.errors.firstOrNull()?.field) + assertEquals("validation", ctx.errors.firstOrNull()?.group) + } + @Test + fun badDescription() = runTest { + val ctx = MessengerContext( + command = ChatCommand.UPDATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_DESCRIPTION, + ) + + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("description", ctx.errors.firstOrNull()?.field) + assertEquals("validation", ctx.errors.firstOrNull()?.group) + } + + @Test + fun databaseError() = runTest { + val ctx = MessengerContext( + command = ChatCommand.UPDATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.DB_ERROR, + ) + + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("internal", ctx.errors.firstOrNull()?.group) + } + + @Test + fun badNoCase() = runTest { + val ctx = MessengerContext( + command = ChatCommand.UPDATE, + state = ChatState.NONE, + workMode = WorkMode.STUB, + stubCase = MessengerStubs.BAD_SEARCH_STRING, + ) + + processor.exec(ctx) + assertEquals(MessengerChat(), ctx.chatResponse) + assertEquals("stub", ctx.errors.firstOrNull()?.field) + assertEquals("validation", ctx.errors.firstOrNull()?.group) + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BaseBizValidationTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BaseBizValidationTest.kt new file mode 100644 index 0000000..653f6a5 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BaseBizValidationTest.kt @@ -0,0 +1,11 @@ +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 + +abstract class BaseBizValidationTest { + protected abstract val command: ChatCommand + private val settings by lazy { MessengerCorSettings() } + protected val processor by lazy { MessengerProcessor(settings) } +} diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationCreateTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationCreateTest.kt new file mode 100644 index 0000000..8e562f2 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationCreateTest.kt @@ -0,0 +1,18 @@ +package ru.otus.messenger.biz.validation + +import kotlin.test.Test +import ru.otus.messenger.common.models.ChatCommand + +class BizValidationCreateTest : BaseBizValidationTest() { + override val command: ChatCommand = ChatCommand.CREATE + + @Test fun correctTitle() = validationTitleCorrect(command, processor) + @Test fun trimTitle() = validationTitleTrim(command, processor) + @Test fun emptyTitle() = validationTitleEmpty(command, processor) + @Test fun badSymbolsTitle() = validationTitleSymbols(command, processor) + + @Test fun correctDescription() = validationDescriptionCorrect(command, processor) + @Test fun trimDescription() = validationDescriptionTrim(command, processor) + @Test fun emptyDescription() = validationDescriptionEmpty(command, processor) + @Test fun badSymbolsDescription() = validationDescriptionSymbols(command, processor) +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationDeleteTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationDeleteTest.kt new file mode 100644 index 0000000..af6cd8d --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationDeleteTest.kt @@ -0,0 +1,13 @@ +package ru.otus.messenger.biz.validation + +import org.junit.Test +import ru.otus.messenger.common.models.ChatCommand + +class BizValidationDeleteTest : BaseBizValidationTest() { + override val command: ChatCommand = ChatCommand.DELETE + + @Test fun correctId() = validationIdCorrect(command, processor) + @Test fun trimId() = validationIdTrim(command, processor) + @Test fun emptyId() = validationIdEmpty(command, processor) + @Test fun badFormatId() = validationIdFormat(command, processor) +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationReadTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationReadTest.kt new file mode 100644 index 0000000..20f7631 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationReadTest.kt @@ -0,0 +1,13 @@ +package ru.otus.messenger.biz.validation + +import org.junit.Test +import ru.otus.messenger.common.models.ChatCommand + +class BizValidationReadTest : BaseBizValidationTest() { + override val command: ChatCommand = ChatCommand.READ + + @Test fun correctId() = validationIdCorrect(command, processor) + @Test fun trimId() = validationIdTrim(command, processor) + @Test fun emptyId() = validationIdEmpty(command, processor) + @Test fun badFormatId() = validationIdFormat(command, processor) +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationSearchTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationSearchTest.kt new file mode 100644 index 0000000..2fea33e --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationSearchTest.kt @@ -0,0 +1,36 @@ +package ru.otus.messenger.biz.validation + +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlinx.coroutines.test.runTest +import org.junit.Test +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatCommand +import ru.otus.messenger.common.models.ChatSearchFilter +import ru.otus.messenger.common.models.ChatSearchFilter.StringSearchField +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.WorkMode + +class BizValidationSearchTest : BaseBizValidationTest() { + override val command: ChatCommand = ChatCommand.SEARCH + + @Test + fun correctEmpty() = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatFilterRequest = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "", + stringValue = "" + ) + ), + ) + ) + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationUpdateTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationUpdateTest.kt new file mode 100644 index 0000000..0574cac --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/BizValidationUpdateTest.kt @@ -0,0 +1,23 @@ +package ru.otus.messenger.biz.validation + +import org.junit.Test +import ru.otus.messenger.common.models.ChatCommand + +class BizValidationUpdateTest : BaseBizValidationTest() { + override val command: ChatCommand = ChatCommand.UPDATE + + @Test fun correctTitle() = validationTitleCorrect(command, processor) + @Test fun trimTitle() = validationTitleTrim(command, processor) + @Test fun emptyTitle() = validationTitleEmpty(command, processor) + @Test fun badSymbolsTitle() = validationTitleSymbols(command, processor) + + @Test fun correctDescription() = validationDescriptionCorrect(command, processor) + @Test fun trimDescription() = validationDescriptionTrim(command, processor) + @Test fun emptyDescription() = validationDescriptionEmpty(command, processor) + @Test fun badSymbolsDescription() = validationDescriptionSymbols(command, processor) + + @Test fun correctId() = validationIdCorrect(command, processor) + @Test fun trimId() = validationIdTrim(command, processor) + @Test fun emptyId() = validationIdEmpty(command, processor) + @Test fun badFormatId() = validationIdFormat(command, processor) +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateSearchStringLengthTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateSearchStringLengthTest.kt new file mode 100644 index 0000000..29719ea --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateSearchStringLengthTest.kt @@ -0,0 +1,110 @@ +package ru.otus.messenger.biz.validation + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatSearchFilter +import ru.otus.messenger.common.models.ChatSearchFilter.StringSearchField +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.cor.dsl.rootChain + +class ValidateSearchStringLengthTest { + @Test + fun emptyString() = runTest { + val ctx = MessengerContext( + state = ChatState.RUNNING, + chatFilterValidating = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "title", + stringValue = "", + ) + ) + ) + ) + chain.exec(ctx) + assertEquals(ChatState.RUNNING, ctx.state) + assertEquals(0, ctx.errors.size) + } + + @Test + fun blankString() = runTest { + val ctx = MessengerContext( + state = ChatState.RUNNING, + chatFilterValidating = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "title", + stringValue = " ", + ) + ) + ) + ) + chain.exec(ctx) + assertEquals(ChatState.RUNNING, ctx.state) + assertEquals(0, ctx.errors.size) + } + + @Test + fun shortString() = runTest { + val ctx = MessengerContext( + state = ChatState.RUNNING, + chatFilterValidating = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "title", + stringValue = "12", + ) + ) + ) + ) + chain.exec(ctx) + assertEquals(ChatState.FAILING, ctx.state) + assertEquals(1, ctx.errors.size) + assertEquals("validation-searchString-tooShort", ctx.errors.first().code) + } + + @Test + fun normalString() = runTest { + val ctx = MessengerContext( + state = ChatState.RUNNING, + chatFilterValidating = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "title", + stringValue = "123", + ) + ) + ) + ) + chain.exec(ctx) + assertEquals(ChatState.RUNNING, ctx.state) + assertEquals(0, ctx.errors.size) + } + + @Test + fun longString() = runTest { + val ctx = MessengerContext( + state = ChatState.RUNNING, + chatFilterValidating = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "title", + stringValue = "12".repeat(51), + ) + ) + ) + ) + chain.exec(ctx) + assertEquals(ChatState.FAILING, ctx.state) + assertEquals(1, ctx.errors.size) + assertEquals("validation-searchString-tooLong", ctx.errors.first().code) + } + + companion object { + val chain = rootChain { + validateSearchStringLength("") + }.build() + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateTitleHasContentTest.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateTitleHasContentTest.kt new file mode 100644 index 0000000..04b232b --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidateTitleHasContentTest.kt @@ -0,0 +1,55 @@ +package ru.otus.messenger.biz.validation + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import ru.otus.messenger.common.MessengerContext +import ru.otus.messenger.common.models.ChatSearchFilter +import ru.otus.messenger.common.models.ChatSearchFilter.StringSearchField +import ru.otus.messenger.common.models.ChatState +import ru.otus.messenger.common.models.MessengerChat +import ru.otus.messenger.cor.dsl.rootChain + +class ValidateTitleHasContentTest { + @Test + fun emptyString() = runTest { + val ctx = MessengerContext(state = ChatState.RUNNING, chatValidating = MessengerChat(title = "")) + chain.exec(ctx) + assertEquals(ChatState.RUNNING, ctx.state) + assertEquals(0, ctx.errors.size) + } + + @Test + fun noContent() = runTest { + val ctx = + MessengerContext(state = ChatState.RUNNING, chatValidating = MessengerChat(title = "12!@#$%^&*()_+-=")) + chain.exec(ctx) + assertEquals(ChatState.FAILING, ctx.state) + assertEquals(1, ctx.errors.size) + assertEquals("validation-title-noContent", ctx.errors.first().code) + } + + @Test + fun normalString() = runTest { + val ctx = MessengerContext( + state = ChatState.RUNNING, + chatFilterValidating = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "title", + stringValue = "Ж" + ) + ) + ) + ) + chain.exec(ctx) + assertEquals(ChatState.RUNNING, ctx.state) + assertEquals(0, ctx.errors.size) + } + + companion object { + val chain = rootChain { + validateTitleHasContent("") + }.build() + } +} \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadDescription.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadDescription.kt new file mode 100644 index 0000000..f610dec --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadDescription.kt @@ -0,0 +1,75 @@ +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 +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 { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.get(), + ) + + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) + assertContains(ctx.chatValidated.description, "description") +} + +fun validationDescriptionTrim(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + description = " \n\tabc \n\t" + }, + ) + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) + assertEquals("abc", ctx.chatValidated.description) +} + +fun validationDescriptionEmpty(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + description = "" + }, + ) + processor.exec(ctx) + assertEquals(1, ctx.errors.size) + assertEquals(ChatState.FAILING, ctx.state) + val error = ctx.errors.firstOrNull() + assertEquals("description", error?.field) + assertContains(error?.message ?: "", "description") +} + +fun validationDescriptionSymbols(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + description = "!@#$%^&*(),.{}" + }, + ) + processor.exec(ctx) + assertEquals(1, ctx.errors.size) + assertEquals(ChatState.FAILING, ctx.state) + val error = ctx.errors.firstOrNull() + assertEquals("description", error?.field) + assertContains(error?.message ?: "", "description") +} diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadId.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadId.kt new file mode 100644 index 0000000..8248132 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadId.kt @@ -0,0 +1,73 @@ +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 +import ru.otus.messenger.common.models.ChatId +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 { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.get(), + ) + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) +} + +fun validationIdTrim(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + id = ChatId(" \n\t ${id.asString()} \n\t ") + }, + ) + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) +} + +fun validationIdEmpty(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + id = ChatId("") + }, + ) + processor.exec(ctx) + assertEquals(1, ctx.errors.size) + assertEquals(ChatState.FAILING, ctx.state) + val error = ctx.errors.firstOrNull() + assertEquals("id", error?.field) + assertContains(error?.message ?: "", "id") +} + +fun validationIdFormat(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + id = ChatId("!@#\$%^&*(),.{}") + }, + ) + processor.exec(ctx) + assertEquals(1, ctx.errors.size) + assertEquals(ChatState.FAILING, ctx.state) + val error = ctx.errors.firstOrNull() + assertEquals("id", error?.field) + assertContains(error?.message ?: "", "id") +} diff --git a/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadTitle.kt b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadTitle.kt new file mode 100644 index 0000000..d9ad043 --- /dev/null +++ b/ok-messenger-be/ok-messenger-biz/src/test/kotlin/validation/ValidationBadTitle.kt @@ -0,0 +1,76 @@ +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 +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 { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + title = "abc" + }, + ) + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) + assertEquals("abc", ctx.chatValidated.title) +} + +fun validationTitleTrim(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + title = " \n\t abc \t\n " + }, + ) + processor.exec(ctx) + assertEquals(0, ctx.errors.size) + assertNotEquals(ChatState.FAILING, ctx.state) + assertEquals("abc", ctx.chatValidated.title) +} + +fun validationTitleEmpty(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + title = "" + }, + ) + processor.exec(ctx) + assertEquals(1, ctx.errors.size) + assertEquals(ChatState.FAILING, ctx.state) + val error = ctx.errors.firstOrNull() + assertEquals("title", error?.field) + assertContains(error?.message ?: "", "title") +} + +fun validationTitleSymbols(command: ChatCommand, processor: MessengerProcessor) = runTest { + val ctx = MessengerContext( + command = command, + state = ChatState.NONE, + workMode = WorkMode.TEST, + chatRequest = MessengerChatStub.prepareResult { + title = "!@#$%^&*(),.{}" + }, + ) + processor.exec(ctx) + assertEquals(1, ctx.errors.size) + assertEquals(ChatState.FAILING, ctx.state) + val error = ctx.errors.firstOrNull() + assertEquals("title", error?.field) + assertContains(error?.message ?: "", "title") +} diff --git a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/MessengerContext.kt b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/MessengerContext.kt index a780d93..c79e758 100644 --- a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/MessengerContext.kt +++ b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/MessengerContext.kt @@ -2,7 +2,7 @@ package ru.otus.messenger.common import kotlinx.datetime.Instant import ru.otus.messenger.common.models.* -import ru.otus.messenger.common.stubs.Stubs +import ru.otus.messenger.common.stubs.MessengerStubs import ru.otus.messenger.common.ws.IMessengerWsSession data class MessengerContext( @@ -10,8 +10,9 @@ data class MessengerContext( var state: ChatState = ChatState.NONE, val errors: MutableList = mutableListOf(), + var corSettings: MessengerCorSettings = MessengerCorSettings(), var workMode: WorkMode = WorkMode.PROD, - var stubCase: Stubs = Stubs.NONE, + var stubCase: MessengerStubs = MessengerStubs.NONE, var wsSession: IMessengerWsSession = IMessengerWsSession.NONE, var requestId: RequestId = RequestId.NONE, @@ -19,6 +20,12 @@ data class MessengerContext( var chatRequest: MessengerChat = MessengerChat(), var chatFilterRequest: ChatSearchFilter = ChatSearchFilter.NONE, + var chatValidating: MessengerChat = MessengerChat(), + var chatFilterValidating: ChatSearchFilter = ChatSearchFilter.NONE, + + var chatValidated: MessengerChat = MessengerChat(), + var chatFilterValidated: ChatSearchFilter = ChatSearchFilter.NONE, + var chatResponse: MessengerChat = MessengerChat(), var chatsResponse: MutableList = mutableListOf(), ) \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerContextErrorHelpers.kt b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerContextErrorHelpers.kt new file mode 100644 index 0000000..8b7961e --- /dev/null +++ b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerContextErrorHelpers.kt @@ -0,0 +1,12 @@ +package ru.otus.messenger.common.helpers + +import ru.otus.messenger.common.MessengerContext +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.fail(error: ChatError) { + addError(error) + state = ChatState.FAILING +} diff --git a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerErrorHelper.kt b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerErrorHelper.kt index 857e76e..02df4a5 100644 --- a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerErrorHelper.kt +++ b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/helpers/MessengerErrorHelper.kt @@ -1,6 +1,7 @@ package ru.otus.messenger.common.helpers import ru.otus.messenger.common.models.ChatError +import ru.otus.messenger.logging.common.LogLevel fun Throwable.asMessengerError( code: String = "unknown", @@ -12,4 +13,17 @@ fun Throwable.asMessengerError( field = "", message = message, exception = this, +) + +fun errorValidation( + field: String, + violationCode: String, + description: String, + level: LogLevel = LogLevel.ERROR, +) = ChatError( + code = "validation-$field-$violationCode", + field = field, + group = "validation", + message = "Validation error for field $field: $description", + level = level, ) \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/ChatSearchFilter.kt b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/ChatSearchFilter.kt index baa7eab..3fead51 100644 --- a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/ChatSearchFilter.kt +++ b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/ChatSearchFilter.kt @@ -6,9 +6,14 @@ data class ChatSearchFilter( var type: ChatType = ChatType.NONE, var mode: ChatMode = ChatMode.NONE, ) { + fun deepCopy(): ChatSearchFilter = copy( + searchFields = searchFields.toMutableList().toList() + ) + interface SearchField { val fieldName: String val action: SearchAction + fun deepCopy(fieldName: String? = null): SearchField } enum class SearchAction { @@ -21,10 +26,24 @@ data class ChatSearchFilter( data class StringSearchField( override val fieldName: String, override val action: SearchAction = SearchAction.CONTAINS, - val stringValue: String, - ) : SearchField + var stringValue: String, + ) : SearchField { + override fun deepCopy(fieldName: String?): SearchField { + fieldName?.let { + return this.copy(fieldName = it) + } + return this.copy() + } + } companion object { - val NONE = ChatSearchFilter() + val NONE = ChatSearchFilter( + searchFields = listOf( + StringSearchField( + fieldName = "", + stringValue = "" + ) + ) + ) } } \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/MessengerChat.kt b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/MessengerChat.kt index bce87d8..5641b87 100644 --- a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/MessengerChat.kt +++ b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/models/MessengerChat.kt @@ -18,6 +18,8 @@ data class MessengerChat( ) { fun isEmpty() = this == NONE + fun deepCopy(): MessengerChat = copy() + companion object { private val NONE = MessengerChat() } diff --git a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/Stubs.kt b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/MessengerStubs.kt similarity index 55% rename from ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/Stubs.kt rename to ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/MessengerStubs.kt index 85597d3..bf11d19 100644 --- a/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/Stubs.kt +++ b/ok-messenger-be/ok-messenger-common/src/commonMain/kotlin/stubs/MessengerStubs.kt @@ -1,11 +1,16 @@ package ru.otus.messenger.common.stubs -enum class Stubs { +enum class MessengerStubs { NONE, SUCCESS, NOT_FOUND, + BAD_ID, + BAD_TITLE, + BAD_DESCRIPTION, + BAD_ARCHIVED, CANNOT_DELETE, MISSING_DATA, VALUE_ERROR, + BAD_SEARCH_STRING, DB_ERROR, } \ No newline at end of file diff --git a/ok-messenger-be/ok-messenger-stubs/src/main/kotlin/MessengerChatStub.kt b/ok-messenger-be/ok-messenger-stubs/src/main/kotlin/MessengerChatStub.kt index 7c2b738..077fe79 100644 --- a/ok-messenger-be/ok-messenger-stubs/src/main/kotlin/MessengerChatStub.kt +++ b/ok-messenger-be/ok-messenger-stubs/src/main/kotlin/MessengerChatStub.kt @@ -1,6 +1,5 @@ package ru.otus.messenger.stubs -import ru.otus.messenger.common.models.ChatMode import ru.otus.messenger.common.models.ChatType import ru.otus.messenger.common.models.MessengerChat import ru.otus.messenger.stubs.MessengerChatStubSample.CHAT_SAMPLE_1 @@ -14,9 +13,8 @@ object MessengerChatStub { fun prepareSearchList( chatTitle: String, chatType: ChatType, - chatMode: ChatMode, ) = listOf( CHAT_SAMPLE_1, CHAT_SAMPLE_2 - ).filter { it.title == chatTitle && it.type == chatType && it.mode == chatMode } + ).filter { it.title == chatTitle && it.type == chatType } } \ No newline at end of file diff --git a/ok-messenger-libs/ok-messenger-lib-cor/build.gradle.kts b/ok-messenger-libs/ok-messenger-lib-cor/build.gradle.kts new file mode 100644 index 0000000..0ce9f78 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("build-jvm") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(libs.kotlin.coroutines) + + testImplementation(kotlin("test-junit")) + testImplementation(libs.kotlin.coroutines.test) +} \ No newline at end of file diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/CorDslMarker.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/CorDslMarker.kt new file mode 100644 index 0000000..bab4b3d --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/CorDslMarker.kt @@ -0,0 +1,4 @@ +package ru.otus.messenger.cor + +@DslMarker +annotation class CorDslMarker \ No newline at end of file diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/ICorExec.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/ICorExec.kt new file mode 100644 index 0000000..eb9ef78 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/ICorExec.kt @@ -0,0 +1,10 @@ +package ru.otus.messenger.cor + +/** + * Блок кода, который обрабатывает контекст. Имеет имя и описание + */ +interface ICorExec { + val title: String + val description: String + suspend fun exec(context: T) +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorChainDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorChainDsl.kt new file mode 100644 index 0000000..7cc95b9 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorChainDsl.kt @@ -0,0 +1,22 @@ +package ru.otus.messenger.cor.dsl + +import ru.otus.messenger.cor.CorDslMarker +import ru.otus.messenger.cor.ICorExec +import ru.otus.messenger.cor.handlers.CorChain + +@CorDslMarker +class CorChainDsl( +) : CorExecDsl(), ICorChainDsl { + private val workers: MutableList> = mutableListOf() + override fun add(worker: ICorExecDsl) { + workers.add(worker) + } + + override fun build(): ICorExec = CorChain( + title = title, + description = description, + execs = workers.map { it.build() }, + blockOn = blockOn, + blockExcept = blockExcept + ) +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorDsl.kt new file mode 100644 index 0000000..0b9948f --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorDsl.kt @@ -0,0 +1,51 @@ +package ru.otus.messenger.cor.dsl + +/** + * Точка входа в dsl построения цепочек. + * Элементы исполняются последовательно. + * + * Пример: + * ``` + * rootChain { + * worker { + * } + * chain { + * worker(...) { + * } + * worker(...) { + * } + * } + * } + * ``` + */ +fun rootChain(function: ICorChainDsl.() -> Unit): ICorChainDsl = CorChainDsl().apply(function) + + +/** + * Создает цепочку, элементы которой исполняются последовательно. + */ +fun ICorChainDsl.chain(function: ICorChainDsl.() -> Unit) { + add(CorChainDsl().apply(function)) +} + +/** + * Создает рабочего + */ +fun ICorChainDsl.worker(function: ICorWorkerDsl.() -> Unit) { + add(CorWorkerDsl().apply(function)) +} + +/** + * Создает рабочего с on и except по умолчанию + */ +fun ICorChainDsl.worker( + title: String, + description: String = "", + blockHandle: T.() -> Unit +) { + add(CorWorkerDsl().also { + it.title = title + it.description = description + it.handle(blockHandle) + }) +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorExecDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorExecDsl.kt new file mode 100644 index 0000000..a192861 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorExecDsl.kt @@ -0,0 +1,17 @@ +package ru.otus.messenger.cor.dsl + +abstract class CorExecDsl : ICorExecDsl { + protected var blockOn: suspend T.() -> Boolean = { true } + protected var blockExcept: suspend T.(e: Throwable) -> Unit = { e: Throwable -> throw e } + + override var title: String = "" + override var description: String = "" + + override fun on(function: suspend T.() -> Boolean) { + blockOn = function + } + + override fun except(function: suspend T.(e: Throwable) -> Unit) { + blockExcept = function + } +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorWorkerDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorWorkerDsl.kt new file mode 100644 index 0000000..ab26903 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/CorWorkerDsl.kt @@ -0,0 +1,21 @@ +package ru.otus.messenger.cor.dsl + +import ru.otus.messenger.cor.CorDslMarker +import ru.otus.messenger.cor.ICorExec +import ru.otus.messenger.cor.handlers.CorWorker + +@CorDslMarker +class CorWorkerDsl : CorExecDsl(), ICorWorkerDsl { + private var blockHandle: suspend T.() -> Unit = {} + override fun handle(function: suspend T.() -> Unit) { + blockHandle = function + } + + override fun build(): ICorExec = CorWorker( + title = title, + description = description, + blockOn = blockOn, + blockHandle = blockHandle, + blockExcept = blockExcept + ) +} \ No newline at end of file diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorChainDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorChainDsl.kt new file mode 100644 index 0000000..a51d4dc --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorChainDsl.kt @@ -0,0 +1,11 @@ +package ru.otus.messenger.cor.dsl + +import ru.otus.messenger.cor.CorDslMarker + +/** + * Билдер (dsl) для цепочек (chain) + */ +@CorDslMarker +interface ICorChainDsl : ICorExecDsl { + fun add(worker: ICorExecDsl) +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorExecDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorExecDsl.kt new file mode 100644 index 0000000..7745bb9 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorExecDsl.kt @@ -0,0 +1,17 @@ +package ru.otus.messenger.cor.dsl + +import ru.otus.messenger.cor.CorDslMarker +import ru.otus.messenger.cor.ICorExec + +/** + * Базовый билдер (dsl) + */ +@CorDslMarker +interface ICorExecDsl { + var title: String + var description: String + fun on(function: suspend T.() -> Boolean) + fun except(function: suspend T.(e: Throwable) -> Unit) + + fun build(): ICorExec +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorWorkerDsl.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorWorkerDsl.kt new file mode 100644 index 0000000..a428b48 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/dsl/ICorWorkerDsl.kt @@ -0,0 +1,11 @@ +package ru.otus.messenger.cor.dsl + +import ru.otus.messenger.cor.CorDslMarker + +/** + * Билдер (dsl) для рабочих (worker) + */ +@CorDslMarker +interface ICorWorkerDsl : ICorExecDsl { + fun handle(function: suspend T.() -> Unit) +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/AbstractCorExec.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/AbstractCorExec.kt new file mode 100644 index 0000000..d7bcb69 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/AbstractCorExec.kt @@ -0,0 +1,25 @@ +package ru.otus.messenger.cor.handlers + +import ru.otus.messenger.cor.ICorExec + +abstract class AbstractCorExec( + override val title: String, + override val description: String = "", + private val blockOn: suspend T.() -> Boolean = { true }, + private val blockExcept: suspend T.(Throwable) -> Unit = {}, +): ICorExec { + protected abstract suspend fun handle(context: T) + + private suspend fun on(context: T): Boolean = context.blockOn() + private suspend fun except(context: T, e: Throwable) = context.blockExcept(e) + + override suspend fun exec(context: T) { + if (on(context)) { + try { + handle(context) + } catch (e: Throwable) { + except(context, e) + } + } + } +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorChain.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorChain.kt new file mode 100644 index 0000000..404f916 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorChain.kt @@ -0,0 +1,20 @@ +package ru.otus.messenger.cor.handlers + +import ru.otus.messenger.cor.ICorExec + +/** + * Реализация цепочки (chain), которая исполняет свои вложенные цепочки и рабочие + */ +class CorChain( + private val execs: List>, + title: String, + description: String = "", + blockOn: suspend T.() -> Boolean = { true }, + blockExcept: suspend T.(Throwable) -> Unit = {}, +) : AbstractCorExec(title, description, blockOn, blockExcept) { + override suspend fun handle(context: T) { + execs.forEach { + it.exec(context) + } + } +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorWorker.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorWorker.kt new file mode 100644 index 0000000..a8b297d --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/main/kotlin/handlers/CorWorker.kt @@ -0,0 +1,11 @@ +package ru.otus.messenger.cor.handlers + +class CorWorker( + title: String, + description: String = "", + blockOn: suspend T.() -> Boolean = { true }, + private val blockHandle: suspend T.() -> Unit = {}, + blockExcept: suspend T.(Throwable) -> Unit = {}, +) : AbstractCorExec(title, description, blockOn, blockExcept) { + override suspend fun handle(context: T) = blockHandle(context) +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorChainTest.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorChainTest.kt new file mode 100644 index 0000000..7fabb95 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorChainTest.kt @@ -0,0 +1,27 @@ +package ru.otus.messenger.cor + +import junit.framework.Assert.assertEquals +import kotlinx.coroutines.test.runTest +import org.junit.Test +import ru.otus.messenger.cor.handlers.CorChain +import ru.otus.messenger.cor.handlers.CorWorker + +class CorChainTest { + @Test + fun `chain should execute workers`() = runTest { + val createWorker = { title: String -> + CorWorker( + title = title, + blockOn = { status == TestContext.CorStatus.NONE }, + blockHandle = { history += "$title; " } + ) + } + val chain = CorChain( + execs = listOf(createWorker("w1"), createWorker("w2")), + title = "chain", + ) + val ctx = TestContext() + chain.exec(ctx) + assertEquals("w1; w2; ", ctx.history) + } +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorDslTest.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorDslTest.kt new file mode 100644 index 0000000..e8e9a6a --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorDslTest.kt @@ -0,0 +1,109 @@ +package ru.otus.messenger.cor + +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlinx.coroutines.test.runTest +import org.junit.Test +import ru.otus.messenger.cor.dsl.ICorChainDsl +import ru.otus.messenger.cor.dsl.ICorExecDsl +import ru.otus.messenger.cor.dsl.chain +import ru.otus.messenger.cor.dsl.rootChain +import ru.otus.messenger.cor.dsl.worker + +class CorDslTest { + private suspend fun execute(dsl: ICorExecDsl): TestContext { + val ctx = TestContext() + dsl.build().exec(ctx) + return ctx + } + + @Test + fun `handle should execute`() = runTest { + val chain = rootChain { + worker { + handle { history += "w1; " } + } + } + val ctx = execute(chain) + assertEquals("w1; ", ctx.history) + } + + @Test + fun `on should check condition`() = runTest { + val chain = rootChain { + worker { + on { status == TestContext.CorStatus.ERROR } + handle { history += "w1; " } + } + worker { + on { status == TestContext.CorStatus.NONE } + handle { + history += "w2; " + status = TestContext.CorStatus.FAILING + } + } + worker { + on { status == TestContext.CorStatus.FAILING } + handle { history += "w3; " } + } + } + assertEquals("w2; w3; ", execute(chain).history) + } + + @Test + fun `except should execute when exception`() = runTest { + val chain = rootChain { + worker { + handle { throw RuntimeException("some error") } + except { history += it.message } + } + } + assertEquals("some error", execute(chain).history) + } + + @Test + fun `should throw when exception and no except`() = runTest { + val chain = rootChain { + worker("throw") { throw RuntimeException("some error") } + } + assertFails { + execute(chain) + } + } + + @Test + fun `complex chain example`() = runTest { + val chain = rootChain { + worker { + title = "Инициализация статуса" + description = "При старте обработки цепочки, статус еще не установлен. Проверяем его" + + on { status == TestContext.CorStatus.NONE } + handle { status = TestContext.CorStatus.RUNNING } + except { status = TestContext.CorStatus.ERROR } + } + + chain { + on { status == TestContext.CorStatus.RUNNING } + + worker( + title = "Лямбда обработчик", + description = "Пример использования обработчика в виде лямбды" + ) { + some += 4 + } + } + + printResult() + + }.build() + + val ctx = TestContext() + chain.exec(ctx) + println("Complete: $ctx") + } + + private fun ICorChainDsl.printResult() = worker(title = "Print example") { + println("some = $some") + } +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorWorkerTest.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorWorkerTest.kt new file mode 100644 index 0000000..9228d64 --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/CorWorkerTest.kt @@ -0,0 +1,44 @@ +package ru.otus.messenger.cor + +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import ru.otus.messenger.cor.handlers.CorWorker + +class CorWorkerTest { + @Test + fun `worker should execute handle`() = runTest { + val worker = CorWorker( + title = "w1", + blockHandle = { history += "w1; " } + ) + val ctx = TestContext() + worker.exec(ctx) + assertEquals("w1; ", ctx.history) + } + + @Test + fun `worker should not execute when off`() = runTest { + val worker = CorWorker( + title = "w1", + blockOn = { status == TestContext.CorStatus.ERROR }, + blockHandle = { history += "w1; " } + ) + val ctx = TestContext() + worker.exec(ctx) + assertEquals("", ctx.history) + } + + @Test + fun `worker should handle exception`() = runTest { + val worker = CorWorker( + title = "w1", + blockHandle = { throw RuntimeException("some error") }, + blockExcept = { e -> history += e.message } + ) + val ctx = TestContext() + worker.exec(ctx) + assertEquals("some error", ctx.history) + } + +} diff --git a/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/TestContext.kt b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/TestContext.kt new file mode 100644 index 0000000..e8b737c --- /dev/null +++ b/ok-messenger-libs/ok-messenger-lib-cor/src/test/kotlin/TestContext.kt @@ -0,0 +1,14 @@ +package ru.otus.messenger.cor + +data class TestContext( + var status: CorStatus = CorStatus.NONE, + var some: Int = 0, + var history: String = "", +) { + enum class CorStatus { + NONE, + RUNNING, + FAILING, + ERROR + } +} diff --git a/ok-messenger-libs/settings.gradle.kts b/ok-messenger-libs/settings.gradle.kts index 8a204bf..e4e632d 100644 --- a/ok-messenger-libs/settings.gradle.kts +++ b/ok-messenger-libs/settings.gradle.kts @@ -23,4 +23,5 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" } -include(":ok-messenger-lib-logging") \ No newline at end of file +include(":ok-messenger-lib-logging") +include(":ok-messenger-lib-cor") \ No newline at end of file