diff --git a/.editorconfig b/.editorconfig index 4e58bba..8db783a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,7 @@ ktlint_standard_function-signature = disabled [{*.kt,*.kts}] indent_style = space max_line_length = 140 +indent_size = 2 ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL ij_continuation_indent_size = 2 ij_kotlin_allow_trailing_comma = false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90a7ad2..e11609d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: Build on: push: - branches: [main] + branches: [ main ] pull_request: - branches: [main] + branches: [ main ] jobs: build-and-test: @@ -19,15 +19,18 @@ jobs: - name: Check out Git repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - name: Gradle Build and Test - uses: gradle/gradle-build-action@v3.5.0 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 with: - arguments: --build-cache build test jacocoTestReport + gradle-version: current + + - name: Gradle Build and Test + run: gradle --build-cache --configuration-cache build test koverXmlReport - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: - files: ./build/reports/kediatr-core/jacoco/test/jacocoTestReport.xml,./build/reports/kediatr-koin-starter/jacoco/test/jacocoTestReport.xml,./build/reports/kediatr-quarkus-starter/jacoco/test/jacocoTestReport.xml,./build/reports/kediatr-spring-boot-2x-starter/jacoco/test/jacocoTestReport.xml,./build/reports/kediatr-spring-boot-3x-starter/jacoco/test/jacocoTestReport.xml + files: '**/build/reports/kover/report.xml' flags: unittests name: codecov-umbrella fail_ci_if_error: false diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index 8a0a161..f31fcf1 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -1,9 +1,9 @@ name: Publish to Maven on: - release: - types: [created] - + release: + types: [ created ] + jobs: publish: runs-on: ubuntu-latest @@ -12,21 +12,21 @@ jobs: packages: write steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - server-id: github - settings-path: ${{ github.workspace }} + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + server-id: github + settings-path: ${{ github.workspace }} - - name: Publish to Maven Repository - uses: gradle/gradle-build-action@v3.5.0 - with: - arguments: --build-cache publish - env: - gpg_private_key: ${{ secrets.gpg_private_key }} - gpg_passphrase: ${{ secrets.gpg_passphrase }} - nexus_username: ${{ secrets.nexus_username }} - nexus_password: ${{ secrets.nexus_password }} + - name: Publish to Maven Repository + uses: gradle/gradle-build-action@v3.5.0 + with: + arguments: --build-cache publish + env: + gpg_private_key: ${{ secrets.gpg_private_key }} + gpg_passphrase: ${{ secrets.gpg_passphrase }} + nexus_username: ${{ secrets.nexus_username }} + nexus_password: ${{ secrets.nexus_password }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 555cdd7..8285a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,14 +11,19 @@ # What's Changed ## New + * Support for Spring-Boot 3.1.0 #116 by @osoykan in https://github.com/Trendyol/kediatR/pull/117 ## Package bumps + * Update dependency io.quarkus:quarkus-bom to v3.0.3.Final by @renovate in https://github.com/Trendyol/kediatR/pull/110 * Update plugin io.quarkus to v3.0.3.Final by @renovate in https://github.com/Trendyol/kediatR/pull/111 -* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.7.1 by @renovate in https://github.com/Trendyol/kediatR/pull/114 -* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.7.1 by @renovate in https://github.com/Trendyol/kediatR/pull/113 -* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.7.1 by @renovate in https://github.com/Trendyol/kediatR/pull/112 +* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.7.1 by @renovate + in https://github.com/Trendyol/kediatR/pull/114 +* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.7.1 by @renovate + in https://github.com/Trendyol/kediatR/pull/113 +* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.7.1 by @renovate + in https://github.com/Trendyol/kediatR/pull/112 **Full Changelog**: https://github.com/Trendyol/kediatR/compare/v2.1.0...3.0.0-SNAPSHOT @@ -27,10 +32,14 @@ ## What's Changed ### Enhancements -* Add possibility to invoke publish method with custom publishStrategy by @awaniak in https://github.com/Trendyol/kediatR/pull/92 -* Make the quarkus starter work again with quarkus 3.x.x by @lucas-dclrcq in https://github.com/Trendyol/kediatR/pull/109 + +* Add possibility to invoke publish method with custom publishStrategy by @awaniak + in https://github.com/Trendyol/kediatR/pull/92 +* Make the quarkus starter work again with quarkus 3.x.x by @lucas-dclrcq + in https://github.com/Trendyol/kediatR/pull/109 ### New Contributors + * @renovate made their first contribution in https://github.com/Trendyol/kediatR/pull/44 * @awaniak made their first contribution in https://github.com/Trendyol/kediatR/pull/92 * @lucas-dclrcq made their first contribution in https://github.com/Trendyol/kediatR/pull/109 @@ -93,9 +102,12 @@ * Update plugin io.quarkus to v3.0.1.Final by @renovate in https://github.com/Trendyol/kediatR/pull/102 * Update plugin io.quarkus to v3.0.2.Final by @renovate in https://github.com/Trendyol/kediatR/pull/104 * Update dependency io.quarkus:quarkus-bom to v2.16.7.Final by @renovate in https://github.com/Trendyol/kediatR/pull/105 -* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.7.0 by @renovate in https://github.com/Trendyol/kediatR/pull/107 -* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.7.0 by @renovate in https://github.com/Trendyol/kediatR/pull/108 -* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.7.0 by @renovate in https://github.com/Trendyol/kediatR/pull/106 +* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.7.0 by @renovate + in https://github.com/Trendyol/kediatR/pull/107 +* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.7.0 by @renovate + in https://github.com/Trendyol/kediatR/pull/108 +* Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.7.0 by @renovate + in https://github.com/Trendyol/kediatR/pull/106 **Full Changelog**: https://github.com/Trendyol/kediatR/compare/v2.0.0...v2.1.0 @@ -104,32 +116,37 @@ This is the announcement release for 2.0.0; in other words, SNAPSHOT is promoted to release. # Breaking Changes -- CommandBus renamed to Mediator #25 + +- CommandBus renamed to Mediator #25 - ` executeCommand.* executeQuery.*, publishNotification.*` renamed to `send` -- Pipeline behavior refactored with decorator pattern #31 -- Sync code is deleted; now it supports only async invocation #26 +- Pipeline behavior refactored with decorator pattern #31 +- Sync code is deleted; now it supports only async invocation #26 ### Bug Fixes - general: - - fix name for pom while publishing ([4cf70ac](https://github.com/Trendyol/kediatR/commit/4cf70ac439ddf694decf779b601693bd902c150c)) + - fix name for pom while + publishing ([4cf70ac](https://github.com/Trendyol/kediatR/commit/4cf70ac439ddf694decf779b601693bd902c150c)) ## [v2.0-SNAPSHOT](https://github.com/Trendyol/kediatR/releases/tag/v2.0-SNAPSHOT) - 2022-10-19 09:10:36 # Breaking Changes -- CommandBus renamed to Mediator #25 -- Pipeline behavior refactored with decorator pattern #31 -- Sync code is deleted; now it supports only async invocation #26 + +- CommandBus renamed to Mediator #25 +- Pipeline behavior refactored with decorator pattern #31 +- Sync code is deleted; now it supports only async invocation #26 ### Bug Fixes - general: - - fix the code snippet for sample kediatr-core usage ([e7f09ba](https://github.com/Trendyol/kediatR/commit/e7f09ba81cb07bd873dd22a9eca683b8d9b461eb)) ([#23](https://github.com/Trendyol/kediatR/pull/23)) + - fix the code snippet for sample kediatr-core + usage ([e7f09ba](https://github.com/Trendyol/kediatR/commit/e7f09ba81cb07bd873dd22a9eca683b8d9b461eb)) ([#23](https://github.com/Trendyol/kediatR/pull/23)) ### Refactor - general: - - refactor gradle ([3005893](https://github.com/Trendyol/kediatR/commit/3005893ece9745af2e78875c5334e10dc8373296)) - - refactor gradle ([023651c](https://github.com/Trendyol/kediatR/commit/023651c1b316029afdaa0bf4e3c199ab80515025)) + - refactor gradle ([3005893](https://github.com/Trendyol/kediatR/commit/3005893ece9745af2e78875c5334e10dc8373296)) + - refactor gradle ([023651c](https://github.com/Trendyol/kediatR/commit/023651c1b316029afdaa0bf4e3c199ab80515025)) -\* *This CHANGELOG was automatically generated by [auto-generate-changelog](https://github.com/BobAnkh/auto-generate-changelog)* +\* *This CHANGELOG was automatically generated +by [auto-generate-changelog](https://github.com/BobAnkh/auto-generate-changelog)* diff --git a/README.md b/README.md index 61c8a9a..aa849d7 100644 --- a/README.md +++ b/README.md @@ -2,48 +2,49 @@ Humus! The kediatr mascot -Mediator implementation in kotlin with native coroutine support. Supports Spring-Boot, Quarkus and Koin dependency providers. +Mediator implementation in kotlin with native coroutine support. Supports Spring-Boot, Quarkus and Koin dependency +providers. Documentation is available at [https://trendyol.github.io/kediatR/](https://trendyol.github.io/kediatR/) ## Show me the code ```kotlin -class PingCommand: Command // or -class PingQuery: Query // or -class PingNotification: Notification -class PingCommandHandler: CommandHandler { - override suspend fun handle(command: PingCommand) { - println("Pong!") - } +class PingCommand : Command // or +class PingQuery : Query // or +class PingNotification : Notification +class PingCommandHandler : CommandHandler { + override suspend fun handle(command: PingCommand) { + println("Pong!") + } } -class PingQueryHandler: QueryHandler { - override suspend fun handle(query: PingQuery): String { - return "Pong!" - } +class PingQueryHandler : QueryHandler { + override suspend fun handle(query: PingQuery): String { + return "Pong!" + } } -class PingNotificationHandler: NotificationHandler { - override suspend fun handle(notification: PingNotification) { - println("Pong!") - } +class PingNotificationHandler : NotificationHandler { + override suspend fun handle(notification: PingNotification) { + println("Pong!") + } } -class MeasurePipelineBehaviour: PipelineBehaviour { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - val start = System.currentTimeMillis() - val response = next(request) - val end = System.currentTimeMillis() - println("Request ${request::class.simpleName} took ${end - start} ms") - return response - } -} +class MeasurePipelineBehaviour : PipelineBehaviour { + override suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse { + val start = System.currentTimeMillis() + val response = next(request) + val end = System.currentTimeMillis() + println("Request ${request::class.simpleName} took ${end - start} ms") + return response + } +} val mediator = // create mediator instance in-memory or with dependency injection, take a look at the documentation -mediator.send(PingCommand()) // 1..1 + mediator.send(PingCommand()) // 1..1 mediator.send(PingQuery()) // 1..1 mediator.send(PingNotification()) // 0..N ``` diff --git a/build.gradle.kts b/build.gradle.kts index 369316d..c5ac9ae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,25 +1,31 @@ group = "com.trendyol" plugins { - kotlin("jvm") version libs.versions.kotlin.get() - id("kediatr-publishing") apply false - id("com.palantir.git-version") version "3.1.0" - java + kotlin("jvm") version libs.versions.kotlin.get() + java + id("kediatr-publishing") apply false + alias(libs.plugins.spotless) } -val versionDetails: groovy.lang.Closure by extra -val details = versionDetails() -version = details.lastTag +version = properties["version"].toString() subprojectsOf("projects") { - apply { - plugin("kotlin") - plugin("kediatr-publishing") - plugin("java") - } + apply { + plugin("kotlin") + plugin("kediatr-publishing") + plugin("java") + plugin(rootProject.libs.plugins.spotless.pluginId) + } - java { - withSourcesJar() - withJavadocJar() + spotless { + kotlin { + ktlint() + .setEditorConfigPath(rootProject.layout.projectDirectory.file(".editorconfig")) } + } + + java { + withSourcesJar() + withJavadocJar() + } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 597a658..40636e1 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,9 +1,9 @@ plugins { - `kotlin-dsl` + `kotlin-dsl` } repositories { - mavenCentral() - google() - gradlePluginPortal() + mavenCentral() + google() + gradlePluginPortal() } diff --git a/buildSrc/src/main/kotlin/BuildConstants.kt b/buildSrc/src/main/kotlin/BuildConstants.kt deleted file mode 100644 index 284889f..0000000 --- a/buildSrc/src/main/kotlin/BuildConstants.kt +++ /dev/null @@ -1,4 +0,0 @@ -object TaskNames { - const val testAggregateReports = "testAggregateReports" - const val codeCoverageReport = "codeCoverageReport" -} diff --git a/buildSrc/src/main/kotlin/Helpers.kt b/buildSrc/src/main/kotlin/Helpers.kt index 2fda818..ef57687 100644 --- a/buildSrc/src/main/kotlin/Helpers.kt +++ b/buildSrc/src/main/kotlin/Helpers.kt @@ -1,78 +1,27 @@ -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.Task +import org.gradle.api.* import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.invoke fun Project.subprojectsOf( - vararg parentProjects: String, - action: Action, + vararg parentProjects: String, + action: Action, ): Unit = subprojects.filter { parentProjects.contains(it.parent?.name) }.forEach { action(it) } fun Collection.of( - vararg parentProjects: String, - except: List = emptyList(), - action: Action, + vararg parentProjects: String, + except: List = emptyList(), + action: Action, ): Unit = this.filter { - parentProjects.contains(it.parent?.name) && !except.contains(it.name) + parentProjects.contains(it.parent?.name) && !except.contains(it.name) }.forEach { action(it) } fun Collection.of( - vararg parentProjects: String, - except: List = emptyList(), + vararg parentProjects: String, + except: List = emptyList(), ): List = this.filter { - parentProjects.contains(it.parent?.name) && !except.contains(it.name) + parentProjects.contains(it.parent?.name) && !except.contains(it.name) } infix fun Property.by(value: T) { - set(value) + set(value) } - -fun Project.testProjects(): List = listOfNotNull( - this.tasks.firstOrNull { it.name.contains("test") }, - tasks.firstOrNull { it.name.contains("e2eTest") }, - tasks.firstOrNull { it.name.contains("integrationTest") } -) - -fun Project.compilationTasks(): List = this.tasks.filter { it.name.contains("compile") } - -fun Project.findTestProjectsRecursively(): List { - val tasks = this.testProjects() - if (tasks.isNotEmpty()) return tasks - return this.subprojects.flatMap { it.findTestProjectsRecursively() } -} - -fun Project.recursivelyContainsTask(name: String): Task? { - val task = this.tasks.firstOrNull { it.name.contains(name) } - if (task != null) return task - return this.subprojects.firstNotNullOfOrNull { it.recursivelyContainsTask(name) } -} - -fun Project.whenService(action: (Project) -> Unit): Unit = if (this.parent != null && this.parent!!.name == "projects") { - action(this) -} else Unit - -fun Project.goToService(): Project { - if (this.parent != null && this.parent!!.name != "projects") { - return this.parent!!.goToService() - } - - if (this.parent != null && this.parent!!.name == "projects") { - return this - } - - throw IllegalStateException("Project is not a service") -} - -fun Project.goToProjectLevel(of: String): Project { - if (this.parent != null && this.parent!!.name != of) { - return this.parent!!.goToProjectLevel(of) - } - - if (this.parent != null && this.parent!!.name == of) { - return this - } - - throw IllegalStateException("Project is not a library") -} - diff --git a/buildSrc/src/main/kotlin/kediatr-publishing.gradle.kts b/buildSrc/src/main/kotlin/kediatr-publishing.gradle.kts index dafb86c..655d08f 100644 --- a/buildSrc/src/main/kotlin/kediatr-publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/kediatr-publishing.gradle.kts @@ -1,89 +1,89 @@ plugins { - `maven-publish` - signing - java + `maven-publish` + signing + java } fun getProperty( - projectKey: String, - environmentKey: String, + projectKey: String, + environmentKey: String, ): String? { - return if (project.hasProperty(projectKey)) { - project.property(projectKey) as? String? - } else { - System.getenv(environmentKey) - } + return if (project.hasProperty(projectKey)) { + project.property(projectKey) as? String? + } else { + System.getenv(environmentKey) + } } afterEvaluate { - publishing { - publications { - create("publish-${project.name}") { - groupId = rootProject.group.toString() - version = rootProject.version.toString() - artifactId = project.name - from(components["java"]) - pom { - name.set(project.name) - description.set(project.properties["projectDescription"].toString()) - url.set(project.properties["projectUrl"].toString()) - packaging = "jar" - licenses { - license { - name.set(project.properties["licence"].toString()) - url.set(project.properties["licenceUrl"].toString()) - } - } - developers { - developer { - id.set("osoykan") - name.set("Oguzhan Soykan") - email.set("oguzhan.soykan@trendyol.com") - } - developer { - id.set("canerpatir") - name.set("Caner Patir") - email.set("caner.patir@trendyol.com") - } - developer { - id.set("bilal-kilic") - name.set("Bilal Kilic") - email.set("bilal.kilic@trendyol.com") - } - } - scm { - connection.set("scm:git@github.com:Trendyol/kediatR.git") - developerConnection.set("scm:git:ssh://github.com:Trendyol/kediatR.git") - url.set(project.properties["projectUrl"].toString()) - } - } + publishing { + publications { + create("publish-${project.name}") { + groupId = rootProject.group.toString() + version = rootProject.version.toString() + artifactId = project.name + from(components["java"]) + pom { + name.set(project.name) + description.set(project.properties["projectDescription"].toString()) + url.set(project.properties["projectUrl"].toString()) + packaging = "jar" + licenses { + license { + name.set(project.properties["licence"].toString()) + url.set(project.properties["licenceUrl"].toString()) } - } - - repositories { - maven { - val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/") - url = if (rootProject.version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl - // url = rootProject.buildDir.resolve("publications").toURI() - credentials { - username = getProperty("nexus_username", "nexus_username") - password = getProperty("nexus_password", "nexus_password") - } + } + developers { + developer { + id.set("osoykan") + name.set("Oguzhan Soykan") + email.set("oguzhan.soykan@trendyol.com") + } + developer { + id.set("canerpatir") + name.set("Caner Patir") + email.set("caner.patir@trendyol.com") } + developer { + id.set("bilal-kilic") + name.set("Bilal Kilic") + email.set("bilal.kilic@trendyol.com") + } + } + scm { + connection.set("scm:git@github.com:Trendyol/kediatR.git") + developerConnection.set("scm:git:ssh://github.com:Trendyol/kediatR.git") + url.set(project.properties["projectUrl"].toString()) + } } + } } - val signingKey = getProperty(projectKey = "gpg.key", environmentKey = "gpg_private_key") - val passPhrase = getProperty(projectKey = "gpg.passphrase", environmentKey = "gpg_passphrase") - signing { - if (passPhrase == null) { - logger.warn( - "The passphrase for the signing key was not found. " + - "Either provide it as env variable 'gpg_passphrase' or as project property 'gpg_passphrase'. " + - "Otherwise the signing might fail!" - ) + repositories { + maven { + val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") + val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/") + url = if (rootProject.version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl + // url = rootProject.buildDir.resolve("publications").toURI() + credentials { + username = getProperty("nexus_username", "nexus_username") + password = getProperty("nexus_password", "nexus_password") } - useInMemoryPgpKeys(signingKey, passPhrase) - sign(publishing.publications) + } + } + } + + val signingKey = getProperty(projectKey = "gpg.key", environmentKey = "gpg_private_key") + val passPhrase = getProperty(projectKey = "gpg.passphrase", environmentKey = "gpg_passphrase") + signing { + if (passPhrase == null) { + logger.warn( + "The passphrase for the signing key was not found. " + + "Either provide it as env variable 'gpg_passphrase' or as project property 'gpg_passphrase'. " + + "Otherwise the signing might fail!" + ) } + useInMemoryPgpKeys(signingKey, passPhrase) + sign(publishing.publications) + } } diff --git a/buildSrc/src/main/kotlin/predef.kt b/buildSrc/src/main/kotlin/predef.kt new file mode 100644 index 0000000..2489e55 --- /dev/null +++ b/buildSrc/src/main/kotlin/predef.kt @@ -0,0 +1,5 @@ +import org.gradle.api.provider.Provider +import org.gradle.plugin.use.PluginDependency + +val Provider.pluginId: String + get() = get().pluginId diff --git a/gradle.properties b/gradle.properties index 52042b0..6017c05 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,9 @@ org.gradle.parallel=false org.gradle.caching=true +org.gradle.configuration-cache=true projectDescription=Mediator implementation in Kotlin with native coroutine support projectUrl=https://github.com/Trendyol/kediatR licenceUrl=https://github.com/Trendyol/kediatR/blob/master/LICENCE licence=MIT Licence +org.gradle.jvmargs=-XX:+UseParallelGC +version="3.1.0" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4678650..251c696 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,35 @@ [versions] kotlin = "1.9.22" +kotlinx = "1.9.0" +kotest = "5.9.1" +detekt = "1.23.7" +junit = "5.11.3" +koin = "4.0.0" +quarkus = "3.16.0" +jakarta = "4.1.0" [libraries] +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx" } +org-reflections = { module = "org.reflections:reflections", version = "0.10.2" } +koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } +junitBom = { module = "org.junit:junit-bom", version.ref = "junit" } +quarkusBom = { module = "io.quarkus:quarkus-bom", version.ref = "quarkus" } +quarkus-arc = { module = "io.quarkus:quarkus-arc", version.ref = "quarkus" } +jakarta-enterpise-cdi-api = { module = "jakarta.enterprise:jakarta.enterprise.cdi-api", version.ref = "jakarta" } + +# test +kotest-property-jvm = { module = "io.kotest:kotest-property-jvm", version.ref = "kotest" } +kotest-datatests = { module = "io.kotest:kotest-framework-datatest-jvm", version.ref = "kotest" } +kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx" } +koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" } +koin-test-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin" } +quarkus-junit5 = { module = "io.quarkus:quarkus-junit5", version.ref = "quarkus" } +detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } [plugins] +kover = { id = "org.jetbrains.kotlinx.kover", version = "0.8.3" } +testLogger = { id = "com.adarshr.test-logger", version = "4.0.0" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +spotless = { id = "com.diffplug.spotless", version = "6.25.0" } +quarkus = { id = "io.quarkus", version.ref = "quarkus" } diff --git a/projects/build.gradle.kts b/projects/build.gradle.kts index 4b90b55..8374534 100644 --- a/projects/build.gradle.kts +++ b/projects/build.gradle.kts @@ -1,76 +1,49 @@ plugins { - kotlin("jvm") version libs.versions.kotlin.get() - jacoco - `jacoco-report-aggregation` - `test-report-aggregation` - id("com.adarshr.test-logger") version "4.0.0" - id("org.jlleitschuh.gradle.ktlint") version "12.1.1" + kotlin("jvm") version libs.versions.kotlin.get() + alias(libs.plugins.testLogger) + alias(libs.plugins.kover) } +kover { + reports { + filters { + excludes { + androidGeneratedClasses() + this.packages("**generated**") + this.classes("**generated**") + } + } + } +} subprojects { - apply { - plugin("com.adarshr.test-logger") - plugin("org.jlleitschuh.gradle.ktlint") - plugin("jacoco") - } - - kotlin { - jvmToolchain(17) - } + apply { + plugin(rootProject.libs.plugins.kover.pluginId) + plugin(rootProject.libs.plugins.testLogger.pluginId) + } - dependencies { - implementation(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") - } + kotlin { + jvmToolchain(17) + } - dependencies { - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0") - testImplementation(platform("org.junit:junit-bom:5.11.3")) - testImplementation("org.junit.jupiter:junit-jupiter") - } + dependencies { + implementation(rootProject.libs.kotlinx.coroutines.core) + } - tasks.test { - useJUnitPlatform() - testlogger { - setTheme("mocha") - showStandardStreams = true - } - reports { - junitXml.required.set(true) - html.required.set(true) - } - } - jacoco { - reportsDirectory.set(rootProject.layout.buildDirectory.dir("reports/${project.name}/jacoco")) - } - tasks.jacocoTestReport { - dependsOn(tasks.test) - reports { - xml.required.set(true) - html.required.set(false) - csv.required.set(false) - } - executionData(this) - } -} + dependencies { + testImplementation(rootProject.libs.kotlinx.coroutines.test) + testImplementation(rootProject.libs.kotest.property.jvm) + testImplementation(rootProject.libs.kotest.datatests) + } -tasks.create("jacocoRootReport") { - enabled = false - this.group = "Reporting" - subprojects.forEach { dependsOn(it.tasks.test) } - subprojects.forEach { - sourceSets(it.sourceSets.getByName("main")) - executionData.from(it.layout.buildDirectory.file("jacoco/test.exec")) + tasks.test { + useJUnitPlatform() + testlogger { + setTheme("mocha") + showStandardStreams = true } reports { - html.required.set(false) - xml.required.set(true) - csv.required.set(false) + junitXml.required.set(true) + html.required.set(true) } -} - -tasks.testAggregateTestReport { - subprojects.forEach { dependsOn(it.tasks.test) } - subprojects.map { it.tasks.test }.forEach { testResults.from(it) } - destinationDirectory.set(rootProject.layout.buildDirectory.dir("reports/tests")) + } } diff --git a/projects/kediatr-core/build.gradle.kts b/projects/kediatr-core/build.gradle.kts index e96cd0a..79df1d5 100644 --- a/projects/kediatr-core/build.gradle.kts +++ b/projects/kediatr-core/build.gradle.kts @@ -1,7 +1,18 @@ +plugins { + `java-test-fixtures` +} + +val testFixturesImplementation: Configuration by configurations.getting { + extendsFrom(configurations.implementation.get()) +} + dependencies { - testImplementation(kotlin("test")) + testFixturesImplementation(libs.kotest.assertions.core) + testFixturesImplementation(junitLibs.junitJupiterApi) + testFixturesImplementation(libs.kotlinx.coroutines.test) + testFixturesRuntimeOnly(junitLibs.junitJupiterEngine) } kotlin { - jvmToolchain(11) + jvmToolchain(11) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/AggregateException.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/AggregateException.kt index c20d68a..b48c519 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/AggregateException.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/AggregateException.kt @@ -6,5 +6,5 @@ package com.trendyol.kediatr * @since 1.0.9 */ class AggregateException(val exceptions: Collection) : RuntimeException() { - constructor(exceptions: Array) : this(exceptions.toList()) + constructor(exceptions: Array) : this(exceptions.toList()) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandHandler.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandHandler.kt index 579c2d0..aa982a4 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandHandler.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandHandler.kt @@ -8,10 +8,10 @@ package com.trendyol.kediatr * @see Command */ interface CommandHandler { - /** - * Handles a command - * - * @param command the command to handle - */ - suspend fun handle(command: TCommand) + /** + * Handles a command + * + * @param command the command to handle + */ + suspend fun handle(command: TCommand) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandProvider.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandProvider.kt index 7618646..19730ec 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandProvider.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandProvider.kt @@ -6,12 +6,12 @@ package com.trendyol.kediatr * @param type of handler */ internal class CommandProvider>( - private val dependencyProvider: DependencyProvider, - private val type: Class + private val dependencyProvider: DependencyProvider, + private val type: Class ) { - fun get(): H { - return dependencyProvider.getSingleInstanceOf(type) - } + fun get(): H { + return dependencyProvider.getSingleInstanceOf(type) + } } /** @@ -21,10 +21,10 @@ internal class CommandProvider>( * @param type of handler */ internal class CommandWithResultProvider>( - private val dependencyProvider: DependencyProvider, - private val type: Class + private val dependencyProvider: DependencyProvider, + private val type: Class ) { - fun get(): H { - return dependencyProvider.getSingleInstanceOf(type) - } + fun get(): H { + return dependencyProvider.getSingleInstanceOf(type) + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandWithResultHandler.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandWithResultHandler.kt index 828b79f..a316bce 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandWithResultHandler.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandWithResultHandler.kt @@ -8,10 +8,10 @@ package com.trendyol.kediatr * @see Command */ interface CommandWithResultHandler, TResult> { - /** - * Handles a command - * - * @param command the command to handle - */ - suspend fun handle(command: TCommand): TResult + /** + * Handles a command + * + * @param command the command to handle + */ + suspend fun handle(command: TCommand): TResult } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Container.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Container.kt index ea60054..f998d1e 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Container.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Container.kt @@ -2,37 +2,37 @@ package com.trendyol.kediatr @Suppress("UNCHECKED_CAST") internal class Container(dependencyProvider: DependencyProvider) : Registrar() { - val commandMap = HashMap, CommandProvider>>() - val queryMap = HashMap, QueryProvider>>() - val notificationMap = HashMap, MutableList>>>() - val pipelineSet = HashSet>() - val commandWithResultMap = HashMap, CommandWithResultProvider<*>>() + val commandMap = HashMap, CommandProvider>>() + val queryMap = HashMap, QueryProvider>>() + val notificationMap = HashMap, MutableList>>>() + val pipelineSet = HashSet>() + val commandWithResultMap = HashMap, CommandWithResultProvider<*>>() - init { + init { - registerFor, *>, Query<*>>(dependencyProvider) { key, value -> - queryMap[key] = QueryProvider(dependencyProvider, value as Class>) - } + registerFor, *>, Query<*>>(dependencyProvider) { key, value -> + queryMap[key] = QueryProvider(dependencyProvider, value as Class>) + } - registerFor, Command>(dependencyProvider) { key, value -> - commandMap[key] = CommandProvider(dependencyProvider, value) - } + registerFor, Command>(dependencyProvider) { key, value -> + commandMap[key] = CommandProvider(dependencyProvider, value) + } - registerFor, *>, CommandWithResult<*>>(dependencyProvider) { key, value -> - commandWithResultMap[key] = - CommandWithResultProvider( - dependencyProvider, - value as Class> - ) - } + registerFor, *>, CommandWithResult<*>>(dependencyProvider) { key, value -> + commandWithResultMap[key] = + CommandWithResultProvider( + dependencyProvider, + value as Class> + ) + } - registerFor, Notification>(dependencyProvider) { key, value -> - notificationMap.getOrPut(key) { mutableListOf() } - .add(NotificationProvider(dependencyProvider, value as Class>)) - } + registerFor, Notification>(dependencyProvider) { key, value -> + notificationMap.getOrPut(key) { mutableListOf() } + .add(NotificationProvider(dependencyProvider, value as Class>)) + } - registerFor(dependencyProvider) { handler -> - pipelineSet.add(PipelineProvider(dependencyProvider, handler)) - } + registerFor(dependencyProvider) { handler -> + pipelineSet.add(PipelineProvider(dependencyProvider, handler)) } + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/DependencyProvider.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/DependencyProvider.kt index 220c95b..9514ef1 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/DependencyProvider.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/DependencyProvider.kt @@ -1,7 +1,7 @@ package com.trendyol.kediatr interface DependencyProvider { - fun getSingleInstanceOf(clazz: Class): T + fun getSingleInstanceOf(clazz: Class): T - fun getSubTypesOf(clazz: Class): Collection> + fun getSubTypesOf(clazz: Class): Collection> } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Mediator.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Mediator.kt index 06ddb56..fdf0bed 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Mediator.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Mediator.kt @@ -1,22 +1,22 @@ package com.trendyol.kediatr interface Mediator { - suspend fun , TResponse> send(query: TQuery): TResponse + suspend fun , TResponse> send(query: TQuery): TResponse - suspend fun send(command: TCommand) + suspend fun send(command: TCommand) - suspend fun , TResult> send(command: TCommand): TResult + suspend fun , TResult> send(command: TCommand): TResult - /** - * Publishes the given notification to appropriate notification handlers - * - * @since 1.0.9 - * @param T any [Notification] subclass to publish - */ - suspend fun publish(notification: T) + /** + * Publishes the given notification to appropriate notification handlers + * + * @since 1.0.9 + * @param T any [Notification] subclass to publish + */ + suspend fun publish(notification: T) - suspend fun publish( - notification: T, - publishStrategy: PublishStrategy - ) + suspend fun publish( + notification: T, + publishStrategy: PublishStrategy + ) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorBuilder.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorBuilder.kt index 1fc56f2..8e10350 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorBuilder.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorBuilder.kt @@ -1,28 +1,28 @@ package com.trendyol.kediatr class MediatorBuilder( - private val dependencyProvider: DependencyProvider + private val dependencyProvider: DependencyProvider ) { - internal var defaultPublishStrategy: PublishStrategy = StopOnExceptionPublishStrategy() - private set + internal var defaultPublishStrategy: PublishStrategy = StopOnExceptionPublishStrategy() + private set - /** - * Overrides default notification publishing strategy. - * Default strategy is [StopOnExceptionPublishStrategy] - * - * @since 1.0.9 - * @see [PublishStrategy] - * @see [ContinueOnExceptionPublishStrategy] - * @see [StopOnExceptionPublishStrategy] - * @see [ParallelNoWaitPublishStrategy] - * @see [ParallelWhenAllPublishStrategy] - */ - fun withPublishStrategy(publishStrategy: PublishStrategy): MediatorBuilder { - this.defaultPublishStrategy = publishStrategy - return this - } + /** + * Overrides default notification publishing strategy. + * Default strategy is [StopOnExceptionPublishStrategy] + * + * @since 1.0.9 + * @see [PublishStrategy] + * @see [ContinueOnExceptionPublishStrategy] + * @see [StopOnExceptionPublishStrategy] + * @see [ParallelNoWaitPublishStrategy] + * @see [ParallelWhenAllPublishStrategy] + */ + fun withPublishStrategy(publishStrategy: PublishStrategy): MediatorBuilder { + this.defaultPublishStrategy = publishStrategy + return this + } - fun build(registry: Registry = RegistryImpl(dependencyProvider)): Mediator { - return MediatorImpl(registry, defaultPublishStrategy) - } + fun build(registry: Registry = RegistryImpl(dependencyProvider)): Mediator { + return MediatorImpl(registry, defaultPublishStrategy) + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorImpl.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorImpl.kt index bdc2d09..35eb824 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorImpl.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/MediatorImpl.kt @@ -1,53 +1,53 @@ package com.trendyol.kediatr class MediatorImpl( - private val registry: Registry, - private val defaultPublishStrategy: PublishStrategy = StopOnExceptionPublishStrategy() + private val registry: Registry, + private val defaultPublishStrategy: PublishStrategy = StopOnExceptionPublishStrategy() ) : Mediator { - override suspend fun , TResponse> send(query: TQuery): TResponse = - processPipeline( - registry.getPipelineBehaviors(), - query - ) { - registry.resolveQueryHandler(query.javaClass).handle(query) - } - - override suspend fun send(command: TCommand) = - processPipeline( - registry.getPipelineBehaviors(), - command - ) { - registry.resolveCommandHandler(command.javaClass).handle(command) - } - - override suspend fun , TResult> send(command: TCommand): TResult = - processPipeline( - registry.getPipelineBehaviors(), - command - ) { - registry.resolveCommandWithResultHandler(command.javaClass).handle(command) - } + override suspend fun , TResponse> send(query: TQuery): TResponse = + processPipeline( + registry.getPipelineBehaviors(), + query + ) { + registry.resolveQueryHandler(query.javaClass).handle(query) + } - override suspend fun publish(notification: T) = publish(notification, defaultPublishStrategy) + override suspend fun send(command: TCommand) = + processPipeline( + registry.getPipelineBehaviors(), + command + ) { + registry.resolveCommandHandler(command.javaClass).handle(command) + } - override suspend fun publish( - notification: T, - publishStrategy: PublishStrategy - ) = processPipeline( - registry.getPipelineBehaviors(), - notification + override suspend fun , TResult> send(command: TCommand): TResult = + processPipeline( + registry.getPipelineBehaviors(), + command ) { - publishStrategy.publish(notification, registry.resolveNotificationHandlers(notification.javaClass)) + registry.resolveCommandWithResultHandler(command.javaClass).handle(command) } - private suspend fun processPipeline( - pipelineBehaviors: Collection, - request: TRequest, - handler: RequestHandlerDelegate - ): TResponse = - pipelineBehaviors - .reversed() - .fold(handler) { next, pipeline -> - { pipeline.handle(request) { next(it) } } - }(request) + override suspend fun publish(notification: T) = publish(notification, defaultPublishStrategy) + + override suspend fun publish( + notification: T, + publishStrategy: PublishStrategy + ) = processPipeline( + registry.getPipelineBehaviors(), + notification + ) { + publishStrategy.publish(notification, registry.resolveNotificationHandlers(notification.javaClass)) + } + + private suspend fun processPipeline( + pipelineBehaviors: Collection, + request: TRequest, + handler: RequestHandlerDelegate + ): TResponse = + pipelineBehaviors + .reversed() + .fold(handler) { next, pipeline -> + { pipeline.handle(request) { next(it) } } + }(request) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationHandler.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationHandler.kt index 181a259..1a4c675 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationHandler.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationHandler.kt @@ -8,10 +8,10 @@ package com.trendyol.kediatr * @see Notification */ interface NotificationHandler where T : Notification { - /** - * Handles a notification - * - * @param notification the notification to handle - */ - suspend fun handle(notification: T) + /** + * Handles a notification + * + * @param notification the notification to handle + */ + suspend fun handle(notification: T) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationProvider.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationProvider.kt index 7c0ff9a..1b31e54 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationProvider.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/NotificationProvider.kt @@ -1,10 +1,10 @@ package com.trendyol.kediatr internal class NotificationProvider>( - private val dependencyProvider: DependencyProvider, - private val type: Class + private val dependencyProvider: DependencyProvider, + private val type: Class ) { - fun get(): H { - return dependencyProvider.getSingleInstanceOf(type) - } + fun get(): H { + return dependencyProvider.getSingleInstanceOf(type) + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineBehavior.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineBehavior.kt index 41564d3..9ee863b 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineBehavior.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineBehavior.kt @@ -6,14 +6,14 @@ package com.trendyol.kediatr * @since 1.0.12 */ interface PipelineBehavior { - /** - * Process to invoke before handling any query, command or notification - * - * @param request the request to handle - * @param next the represents the CommandHandler handle function - */ - suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse + /** + * Process to invoke before handling any query, command or notification + * + * @param request the request to handle + * @param next the represents the CommandHandler handle function + */ + suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineProvider.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineProvider.kt index 5cf019a..efc1444 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineProvider.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PipelineProvider.kt @@ -6,10 +6,10 @@ package com.trendyol.kediatr * @param type of pipeline behavior */ internal class PipelineProvider( - private val dependencyProvider: DependencyProvider, - private val type: Class + private val dependencyProvider: DependencyProvider, + private val type: Class ) { - fun get(): H { - return dependencyProvider.getSingleInstanceOf(type) - } + fun get(): H { + return dependencyProvider.getSingleInstanceOf(type) + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategies.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategies.kt index aec757a..8daa366 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategies.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategies.kt @@ -3,68 +3,68 @@ package com.trendyol.kediatr import kotlinx.coroutines.* class ContinueOnExceptionPublishStrategy : PublishStrategy { - override suspend fun publish( - notification: T, - notificationHandlers: Collection>, - dispatcher: CoroutineDispatcher - ) { - coroutineScope { - withContext(dispatcher) { - val exceptions = mutableListOf() - notificationHandlers.forEach { - try { - it.handle(notification) - } catch (e: Exception) { - exceptions.add(e) - } - } - if (exceptions.isNotEmpty()) { - throw AggregateException(exceptions) - } - } + override suspend fun publish( + notification: T, + notificationHandlers: Collection>, + dispatcher: CoroutineDispatcher + ) { + coroutineScope { + withContext(dispatcher) { + val exceptions = mutableListOf() + notificationHandlers.forEach { + try { + it.handle(notification) + } catch (e: Exception) { + exceptions.add(e) + } } + if (exceptions.isNotEmpty()) { + throw AggregateException(exceptions) + } + } } + } } class StopOnExceptionPublishStrategy : PublishStrategy { - override suspend fun publish( - notification: T, - notificationHandlers: Collection>, - dispatcher: CoroutineDispatcher - ) { - coroutineScope { - withContext(dispatcher) { - notificationHandlers.forEach { it.handle(notification) } - } - } + override suspend fun publish( + notification: T, + notificationHandlers: Collection>, + dispatcher: CoroutineDispatcher + ) { + coroutineScope { + withContext(dispatcher) { + notificationHandlers.forEach { it.handle(notification) } + } } + } } class ParallelNoWaitPublishStrategy : PublishStrategy { - override suspend fun publish( - notification: T, - notificationHandlers: Collection>, - dispatcher: CoroutineDispatcher - ) { - coroutineScope { - withContext(dispatcher) { - notificationHandlers.forEach { launch { it.handle(notification) } } - } - } + override suspend fun publish( + notification: T, + notificationHandlers: Collection>, + dispatcher: CoroutineDispatcher + ) { + coroutineScope { + withContext(dispatcher) { + notificationHandlers.forEach { launch { it.handle(notification) } } + } } + } } class ParallelWhenAllPublishStrategy : PublishStrategy { - override suspend fun publish( - notification: T, - notificationHandlers: Collection>, - dispatcher: CoroutineDispatcher - ) { - coroutineScope { - withContext(dispatcher) { - val deferredResults = notificationHandlers.map { async { it.handle(notification) } } - deferredResults.awaitAll() - } - } + override suspend fun publish( + notification: T, + notificationHandlers: Collection>, + dispatcher: CoroutineDispatcher + ) { + coroutineScope { + withContext(dispatcher) { + val deferredResults = notificationHandlers.map { async { it.handle(notification) } } + deferredResults.awaitAll() + } } + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategy.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategy.kt index d49b78b..7d1dbbd 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategy.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/PublishStrategy.kt @@ -4,9 +4,9 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers interface PublishStrategy { - suspend fun publish( - notification: T, - notificationHandlers: Collection>, - dispatcher: CoroutineDispatcher = Dispatchers.IO - ) + suspend fun publish( + notification: T, + notificationHandlers: Collection>, + dispatcher: CoroutineDispatcher = Dispatchers.IO + ) } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryHandler.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryHandler.kt index 0e546d4..b91f943 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryHandler.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryHandler.kt @@ -8,10 +8,10 @@ package com.trendyol.kediatr * @see Query */ interface QueryHandler, TResponse> { - /** - * Handles a query - * - * @param query the query to handle - */ - suspend fun handle(query: TQuery): TResponse + /** + * Handles a query + * + * @param query the query to handle + */ + suspend fun handle(query: TQuery): TResponse } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryProvider.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryProvider.kt index 45db5d7..b8ff79a 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryProvider.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/QueryProvider.kt @@ -6,10 +6,10 @@ package com.trendyol.kediatr * @param type of handler */ internal class QueryProvider>( - private val dependencyProvider: DependencyProvider, - private val type: Class + private val dependencyProvider: DependencyProvider, + private val type: Class ) { - fun get(): H { - return dependencyProvider.getSingleInstanceOf(type) - } + fun get(): H { + return dependencyProvider.getSingleInstanceOf(type) + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registrar.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registrar.kt index 2215180..1be01f7 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registrar.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registrar.kt @@ -1,72 +1,71 @@ package com.trendyol.kediatr -import java.lang.reflect.ParameterizedType -import java.lang.reflect.TypeVariable +import java.lang.reflect.* @Suppress("UNCHECKED_CAST") abstract class Registrar { - protected inline fun registerFor( - dependencyProvider: DependencyProvider, - registrar: (key: Class, value: Class) -> Unit - ) = dependencyProvider.getSubTypesOf(THandler::class.java).forEach { - registerFor(it) { key, value -> - registrar(key as Class, value as Class) - } + protected inline fun registerFor( + dependencyProvider: DependencyProvider, + registrar: (key: Class, value: Class) -> Unit + ) = dependencyProvider.getSubTypesOf(THandler::class.java).forEach { + registerFor(it) { key, value -> + registrar(key as Class, value as Class) } + } + + protected inline fun registerFor( + handler: Class<*>, + registrar: (key: Class<*>, value: Class<*>) -> Unit + ) { + val interfaceOrBaseClass = THandler::class.java + if (!interfaceOrBaseClass.isAssignableFrom(handler)) return + + handler.genericInterfaces + .filterIsInstance() + .map { extractParameter(it) } + .forEach { registrar(it, handler) } - protected inline fun registerFor( - handler: Class<*>, - registrar: (key: Class<*>, value: Class<*>) -> Unit - ) { - val interfaceOrBaseClass = THandler::class.java - if (!interfaceOrBaseClass.isAssignableFrom(handler)) return + when (handler.genericSuperclass) { + is ParameterizedType -> { + val inheritedHandler = (handler.genericSuperclass as ParameterizedType).rawType as Class<*> + inheritedHandler.genericInterfaces + .filterIsInstance() + .map { extractParameter(handler.genericSuperclass as ParameterizedType) } + .forEach { registrar(it, handler) } + } - handler.genericInterfaces + is Class<*> -> { + val inheritedHandler = handler.genericSuperclass as Class<*> + if (interfaceOrBaseClass.isAssignableFrom(inheritedHandler)) { + inheritedHandler.genericInterfaces .filterIsInstance() .map { extractParameter(it) } .forEach { registrar(it, handler) } - - when (handler.genericSuperclass) { - is ParameterizedType -> { - val inheritedHandler = (handler.genericSuperclass as ParameterizedType).rawType as Class<*> - inheritedHandler.genericInterfaces - .filterIsInstance() - .map { extractParameter(handler.genericSuperclass as ParameterizedType) } - .forEach { registrar(it, handler) } - } - - is Class<*> -> { - val inheritedHandler = handler.genericSuperclass as Class<*> - if (interfaceOrBaseClass.isAssignableFrom(inheritedHandler)) { - inheritedHandler.genericInterfaces - .filterIsInstance() - .map { extractParameter(it) } - .forEach { registrar(it, handler) } - } - } } + } } + } - protected inline fun registerFor( - dependencyProvider: DependencyProvider, - registrar: (value: Class) -> Unit - ) = dependencyProvider.getSubTypesOf(T::class.java).forEach { handler -> - registerFor(handler) { value -> registrar(value as Class) } - } + protected inline fun registerFor( + dependencyProvider: DependencyProvider, + registrar: (value: Class) -> Unit + ) = dependencyProvider.getSubTypesOf(T::class.java).forEach { handler -> + registerFor(handler) { value -> registrar(value as Class) } + } - protected inline fun registerFor( - handler: Class<*>, - registrar: (value: Class<*>) -> Unit - ) { - val interfaceOrBaseClass = T::class.java - if (!interfaceOrBaseClass.isAssignableFrom(handler)) return - registrar(handler) - } + protected inline fun registerFor( + handler: Class<*>, + registrar: (value: Class<*>) -> Unit + ) { + val interfaceOrBaseClass = T::class.java + if (!interfaceOrBaseClass.isAssignableFrom(handler)) return + registrar(handler) + } - protected fun extractParameter(genericInterface: ParameterizedType): Class<*> = - when (val typeArgument = genericInterface.actualTypeArguments[0]) { - is ParameterizedType -> typeArgument.rawType as Class<*> - is TypeVariable<*> -> extractParameter((genericInterface.rawType as Class<*>).genericInterfaces[0] as ParameterizedType) - else -> typeArgument as Class<*> - } + protected fun extractParameter(genericInterface: ParameterizedType): Class<*> = + when (val typeArgument = genericInterface.actualTypeArguments[0]) { + is ParameterizedType -> typeArgument.rawType as Class<*> + is TypeVariable<*> -> extractParameter((genericInterface.rawType as Class<*>).genericInterfaces[0] as ParameterizedType) + else -> typeArgument as Class<*> + } } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registry.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registry.kt index 61ea062..6eff2d1 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registry.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Registry.kt @@ -1,17 +1,17 @@ package com.trendyol.kediatr interface Registry { - fun resolveCommandHandler(classOfCommand: Class): CommandHandler + fun resolveCommandHandler(classOfCommand: Class): CommandHandler - fun , TResult> resolveCommandWithResultHandler( - classOfCommand: Class - ): CommandWithResultHandler + fun , TResult> resolveCommandWithResultHandler( + classOfCommand: Class + ): CommandWithResultHandler - fun , TResult> resolveQueryHandler(classOfQuery: Class): QueryHandler + fun , TResult> resolveQueryHandler(classOfQuery: Class): QueryHandler - fun resolveNotificationHandlers( - classOfNotification: Class - ): Collection> + fun resolveNotificationHandlers( + classOfNotification: Class + ): Collection> - fun getPipelineBehaviors(): Collection + fun getPipelineBehaviors(): Collection } diff --git a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/RegistryImpl.kt b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/RegistryImpl.kt index 4f01f6f..cc7451c 100644 --- a/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/RegistryImpl.kt +++ b/projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/RegistryImpl.kt @@ -3,38 +3,38 @@ package com.trendyol.kediatr class RegistryImpl( - dependencyProvider: DependencyProvider + dependencyProvider: DependencyProvider ) : Registry { - private val registry = Container(dependencyProvider) + private val registry = Container(dependencyProvider) - override fun resolveCommandHandler(classOfCommand: Class): CommandHandler { - val handler = - registry.commandMap[classOfCommand]?.get() - ?: throw HandlerNotFoundException("handler could not be found for ${classOfCommand.name}") - return handler as CommandHandler - } + override fun resolveCommandHandler(classOfCommand: Class): CommandHandler { + val handler = + registry.commandMap[classOfCommand]?.get() + ?: throw HandlerNotFoundException("handler could not be found for ${classOfCommand.name}") + return handler as CommandHandler + } - override fun , TResult> resolveCommandWithResultHandler( - classOfCommand: Class - ): CommandWithResultHandler { - val handler = - registry.commandWithResultMap[classOfCommand]?.get() - ?: throw HandlerNotFoundException("handler could not be found for ${classOfCommand.name}") - return handler as CommandWithResultHandler - } + override fun , TResult> resolveCommandWithResultHandler( + classOfCommand: Class + ): CommandWithResultHandler { + val handler = + registry.commandWithResultMap[classOfCommand]?.get() + ?: throw HandlerNotFoundException("handler could not be found for ${classOfCommand.name}") + return handler as CommandWithResultHandler + } - override fun resolveNotificationHandlers( - classOfNotification: Class - ): Collection> = - registry.notificationMap.filter { (k, _) -> k.isAssignableFrom(classOfNotification) } - .flatMap { (_, v) -> v.map { it.get() as NotificationHandler } } + override fun resolveNotificationHandlers( + classOfNotification: Class + ): Collection> = + registry.notificationMap.filter { (k, _) -> k.isAssignableFrom(classOfNotification) } + .flatMap { (_, v) -> v.map { it.get() as NotificationHandler } } - override fun , TResult> resolveQueryHandler(classOfQuery: Class): QueryHandler { - val handler = - registry.queryMap[classOfQuery]?.get() - ?: throw HandlerNotFoundException("handler could not be found for ${classOfQuery.name}") - return handler as QueryHandler - } + override fun , TResult> resolveQueryHandler(classOfQuery: Class): QueryHandler { + val handler = + registry.queryMap[classOfQuery]?.get() + ?: throw HandlerNotFoundException("handler could not be found for ${classOfQuery.name}") + return handler as QueryHandler + } - override fun getPipelineBehaviors(): Collection = registry.pipelineSet.map { it.get() } + override fun getPipelineBehaviors(): Collection = registry.pipelineSet.map { it.get() } } diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandHandlerTest.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandHandlerTest.kt index 92392fb..e66f746 100644 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandHandlerTest.kt +++ b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandHandlerTest.kt @@ -1,159 +1,5 @@ package com.trendyol.kediatr -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull +import com.trendyol.kediatr.coreUseCases.CommandHandlerUseCases -class CommandHandlerTest { - @Test - fun async_commandHandler_should_be_fired() = - runBlocking { - class MyAsyncCommand : Command - - class MyCommandHandler : CommandHandler { - var invocationCount = 0 - - override suspend fun handle(command: MyAsyncCommand) { - invocationCount++ - } - } - - val handler = MyCommandHandler() - val handlers: HashMap, Any> = hashMapOf(Pair(MyCommandHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.send(MyAsyncCommand()) - - assertEquals(1, handler.invocationCount) - } - - @Test - fun `should throw exception if given async command has not been registered before`() { - class NonExistCommand : Command - - val handlers: HashMap, Any> = hashMapOf() - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - bus.send(NonExistCommand()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for ${NonExistCommand::class.java.typeName}", exception.message) - } - - @Test - fun inheritance_should_work() = - runBlocking { - class MyCommandForInheritance : Command - - abstract class MyCommandHandlerFor : CommandHandler - - class MyInheritedCommandHandler : MyCommandHandlerFor() { - var invocationCount = 0 - - override suspend fun handle(command: MyCommandForInheritance) { - invocationCount++ - } - } - - val handler = MyInheritedCommandHandler() - val handlers: HashMap, Any> = hashMapOf(Pair(MyInheritedCommandHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.send(MyCommandForInheritance()) - - assertEquals(1, handler.invocationCount) - } - - @Test - fun inheritance_but_not_parameterized_should_work() = - runBlocking { - class MyCommandForInheritance : Command - - abstract class MyCommandHandlerBaseForSpecificCommand : CommandHandler - - class MyInheritedCommandHandlerForSpecificCommand : MyCommandHandlerBaseForSpecificCommand() { - var invocationCount = 0 - - override suspend fun handle(command: MyCommandForInheritance) { - invocationCount++ - } - } - - val handler = MyInheritedCommandHandlerForSpecificCommand() - val handlers: HashMap, Any> = - hashMapOf(Pair(MyInheritedCommandHandlerForSpecificCommand::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.send(MyCommandForInheritance()) - - assertEquals(1, handler.invocationCount) - } - - @Nested - inner class ParameterizedTests { - @Test - fun async_command_should_be_fired() = - runBlocking { - class ParameterizedCommand(val param: T) : Command - - class ParameterizedCommandHandler : CommandHandler> { - var invocationCount = 0 - - override suspend fun handle(command: ParameterizedCommand) { - invocationCount++ - } - } - - // given - val handler = ParameterizedCommandHandler>() - val handlers: HashMap, Any> = - hashMapOf(Pair(ParameterizedCommandHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - // when - bus.send(ParameterizedCommand("MyParam")) - - // then - assertEquals(1, handler.invocationCount) - } - - @Test - fun async_commandHandler_with_inheritance_should_be_fired() = - runBlocking { - class ParameterizedCommand(val param: T) : Command - - abstract class ParameterizedCommandHandlerBase : CommandHandler> - - class ParameterizedCommandHandler : ParameterizedCommandHandlerBase() { - var invocationCount = 0 - - override suspend fun handle(command: ParameterizedCommand) { - invocationCount++ - } - } - - // given - val handler = ParameterizedCommandHandler>() - val handlers: HashMap, Any> = - hashMapOf(Pair(ParameterizedCommandHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - // when - bus.send(ParameterizedCommand("MyParam")) - - // then - assertEquals(1, handler.invocationCount) - } - } -} +class CommandHandlerTest : CommandHandlerUseCases() diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandWithResultHandlerTest.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandWithResultHandlerTest.kt index edc985e..12e2687 100644 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandWithResultHandlerTest.kt +++ b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/CommandWithResultHandlerTest.kt @@ -1,154 +1,5 @@ package com.trendyol.kediatr -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import com.trendyol.kediatr.coreUseCases.CommandWithResultHandlerUseCases -private var counter = 0 -private var asyncTestCounter = 0 - -class CommandWithResultHandlerTest { - init { - counter = 0 - asyncTestCounter = 0 - } - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - val handler = AsyncMyCommandRHandler() - val handlers: HashMap, Any> = hashMapOf(Pair(AsyncMyCommandRHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.send(MyAsyncCommandR()) - - assertTrue { - asyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command has not been registered before`() { - val provider = ManualDependencyProvider(hashMapOf()) - val bus: Mediator = MediatorBuilder(provider).build() - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - bus.send(NonExistCommandR()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.NonExistCommandR", exception.message) - } - - @Test - fun inheritance_should_work() = - runBlocking { - var invocationCount = 0 - - class MyAsyncCommand : CommandWithResult - - class AsyncMyCommandHandler : CommandWithResultHandler { - override suspend fun handle(command: MyAsyncCommand): Result { - invocationCount++ - return Result() - } - } - - val handler = AsyncMyCommandHandler() - val handlers: HashMap, Any> = hashMapOf(Pair(AsyncMyCommandHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.send(MyAsyncCommand()) - - assertEquals(1, invocationCount) - } - - @Nested - inner class ParamaterizedTests { - init { - counter = 0 - asyncTestCounter = 0 - } - - inner class ParameterizedCommandWithResult(val param: TParam) : CommandWithResult - - inner class ParatemerizedAsyncCommandWithResultHandler : - CommandWithResultHandler, String> { - override suspend fun handle(command: ParameterizedCommandWithResult): String { - counter++ - return command.param.toString() - } - } - - @Test - fun `async commandWithResult should be fired and return result`() = - runBlocking { - // given - val handler = ParatemerizedAsyncCommandWithResultHandler>() - val handlers: HashMap, Any> = hashMapOf(Pair(ParatemerizedAsyncCommandWithResultHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - // when - val result = bus.send(ParameterizedCommandWithResult(61L)) - - // then - assertTrue { counter == 1 } - assertEquals(result, "61") - } - - @Test - fun inheritance_should_work() = - runBlocking { - var invocationCount = 0 - - class ParameterizedCommandWithResult(val param: TParam) : CommandWithResult - - abstract class ParameterizedCommandWithResultHandlerBase> : - CommandWithResultHandler - - class Handler : ParameterizedCommandWithResultHandlerBase>() { - override suspend fun handle(command: ParameterizedCommandWithResult): String { - invocationCount++ - return command.param.toString() - } - } - - val handler = Handler>() - val handlers: HashMap, Any> = hashMapOf(Pair(Handler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - // when - val result = bus.send(ParameterizedCommandWithResult("invoked")) - - // then - assertEquals(1, invocationCount) - assertEquals(result, "invoked") - } - } -} - -private class Result - -private class NonExistCommandR : Command - -private class MyCommandR : CommandWithResult - -private class MyAsyncCommandR : CommandWithResult - -private class AsyncMyCommandRHandler : CommandWithResultHandler { - override suspend fun handle(command: MyAsyncCommandR): Result { - delay(500) - asyncTestCounter++ - - return Result() - } -} +class CommandWithResultHandlerTest : CommandWithResultHandlerUseCases() diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/ManualDependencyProvider.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/ManualDependencyProvider.kt deleted file mode 100644 index f917cc8..0000000 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/ManualDependencyProvider.kt +++ /dev/null @@ -1,51 +0,0 @@ -@file:Suppress("UNCHECKED_CAST") - -package com.trendyol.kediatr - -import java.lang.reflect.ParameterizedType - -class ManualDependencyProvider( - private val handlerMap: HashMap, Any> -) : DependencyProvider { - override fun getSingleInstanceOf(clazz: Class): T { - return handlerMap[clazz] as T - } - - override fun getSubTypesOf(clazz: Class): Collection> { - return handlerMap - .filter { filterInternal(it.key, clazz) } - .map { - it.key as Class - } - } - - private fun filterInternal( - handler: Class<*>, - interfaceOrBaseClass: Class - ): Boolean { - if (interfaceOrBaseClass.isAssignableFrom(handler)) return true - - if (handler.genericInterfaces - .filterIsInstance() - .any { it.rawType == interfaceOrBaseClass } - ) { - return true - } - - return when (handler.genericSuperclass) { - is ParameterizedType -> { - val inheritedHandler = (handler.genericSuperclass as ParameterizedType).rawType as Class<*> - inheritedHandler.genericInterfaces - .filterIsInstance() - .any { it.rawType == interfaceOrBaseClass } - } - - is Class<*> -> { - val inheritedHandler = (handler.genericSuperclass as Class<*>) - interfaceOrBaseClass.isAssignableFrom(inheritedHandler) - } - - else -> false - } - } -} diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/MediatorBuilderTest.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/MediatorBuilderTest.kt deleted file mode 100644 index 955be61..0000000 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/MediatorBuilderTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.trendyol.kediatr - -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.MethodSource -import kotlin.test.assertEquals - -internal class MediatorBuilderTest { - @ParameterizedTest - @MethodSource("strategies") - fun `When a publish strategy is defined it should be set`(expectedStrategy: PublishStrategy) { - // Arrange/Act - val builder = - MediatorBuilder(ManualDependencyProvider(hashMapOf())) - .withPublishStrategy(expectedStrategy) - - // Assert - assertEquals(expectedStrategy, builder.defaultPublishStrategy) - } - - companion object { - @JvmStatic - fun strategies() = - listOf( - Arguments.of(ContinueOnExceptionPublishStrategy()), - Arguments.of(ParallelNoWaitPublishStrategy()), - Arguments.of(ParallelWhenAllPublishStrategy()), - Arguments.of(StopOnExceptionPublishStrategy()) - ) - } -} diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/NotificationHandlerTest.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/NotificationHandlerTest.kt index 23a18d0..d4dc5d8 100644 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/NotificationHandlerTest.kt +++ b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/NotificationHandlerTest.kt @@ -1,160 +1,5 @@ package com.trendyol.kediatr -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import java.util.concurrent.CountDownLatch -import kotlin.test.assertEquals -import kotlin.test.assertTrue +import com.trendyol.kediatr.coreUseCases.NotificationHandlerUseCases -private var asyncCountDownLatch = CountDownLatch(1) - -private open class Ping : Notification - -private class ExtendedPing : Ping() - -private class AnPingHandler : NotificationHandler { - override suspend fun handle(notification: ExtendedPing) { - asyncCountDownLatch.countDown() - } -} - -private class AnotherPingHandler : NotificationHandler { - override suspend fun handle(notification: Ping) { - asyncCountDownLatch.countDown() - } -} - -class NotificationHandlerTest { - init { - asyncCountDownLatch = CountDownLatch(1) - } - - @Test - fun `async notification handler should be called`() = - runBlocking { - val pingHandler = AnPingHandler() - val anotherPingHandler = AnotherPingHandler() - val handlers: HashMap, Any> = - hashMapOf( - Pair(AnPingHandler::class.java, pingHandler), - Pair(AnotherPingHandler::class.java, anotherPingHandler) - ) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.publish(ExtendedPing()) - - assertTrue { - asyncCountDownLatch.count == 0L - } - } - - @Test - fun multiple_handlers_for_a_notification_should_be_dispatched() = - runBlocking { - var invocationCount = 0 - - class MyNotification : Notification - - class Handler1 : NotificationHandler { - override suspend fun handle(notification: MyNotification) { - invocationCount++ - } - } - - class Handler2 : NotificationHandler { - override suspend fun handle(notification: MyNotification) { - invocationCount++ - } - } - - val handlers: HashMap, Any> = - hashMapOf( - Pair(Handler1::class.java, Handler1()), - Pair(Handler2::class.java, Handler2()) - ) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.publish(MyNotification()) - assertEquals(2, invocationCount) - } - - @Test - fun inherited_notification_handler_should_be_called() = - runBlocking { - class PingForInherited : Notification - - abstract class NotificationHandlerBase : NotificationHandler - - class InheritedNotificationHandler : NotificationHandlerBase() { - override suspend fun handle(notification: PingForInherited) { - asyncCountDownLatch.countDown() - } - } - - val nHandler = InheritedNotificationHandler() - val handlers: HashMap, Any> = hashMapOf(Pair(InheritedNotificationHandler::class.java, nHandler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.publish(PingForInherited()) - - assertEquals(0, asyncCountDownLatch.count) - } - - @Nested - inner class ParamaterizedTests { - inner class ParameterizedNotification(val param: T) : Notification - - inner class ParameterizedNotificationHandler : NotificationHandler> { - override suspend fun handle(notification: ParameterizedNotification) { - asyncCountDownLatch.countDown() - } - } - - @Test - fun `async notification should be fired`() = - runBlocking { - // given - val handler = ParameterizedNotificationHandler>() - val handlers: HashMap, Any> = - hashMapOf(Pair(ParameterizedNotificationHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - // when - bus.publish(ParameterizedNotification("MyParam")) - - // then - assertTrue { - asyncCountDownLatch.count == 0L - } - } - - @Test - fun inherited_notification_handler_should_be_called() = - runBlocking { - var invocationCount = 0 - var parameter = "" - - class ParameterizedNotification(val param: T) : Notification - - abstract class NotificationHandlerBase : NotificationHandler - - class ParameterizedNotificationHandler : NotificationHandlerBase>() { - override suspend fun handle(notification: ParameterizedNotification) { - parameter = notification.param.toString() - invocationCount++ - } - } - - val nHandler = ParameterizedNotificationHandler>() - val handlers: HashMap, Any> = hashMapOf(Pair(ParameterizedNotificationHandler::class.java, nHandler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.publish(ParameterizedNotification("invoked")) - - assertEquals(1, invocationCount) - assertEquals("invoked", parameter) - } - } -} +class NotificationHandlerTest : NotificationHandlerUseCases() diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/PipelineBehaviorTest.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/PipelineBehaviorTest.kt index bcd869e..a9cc2e6 100644 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/PipelineBehaviorTest.kt +++ b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/PipelineBehaviorTest.kt @@ -1,172 +1,5 @@ package com.trendyol.kediatr -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import kotlin.test.assertEquals -import kotlin.test.assertTrue +import com.trendyol.kediatr.coreUseCases.PipelineBehaviorUseCases -var exceptionPipelineBehaviorHandleCounter = 0 -var exceptionPipelineBehaviorHandleCatchCounter = 0 -var loggingPipelineBehaviorHandleBeforeNextCounter = 0 -var loggingPipelineBehaviorHandleAfterNextCounter = 0 -var inheritedPipelineBehaviourHandleCounter = 0 -var commandTestCounter = 0 - -class PipelineBehaviorTest { - init { - exceptionPipelineBehaviorHandleCounter = 0 - exceptionPipelineBehaviorHandleCatchCounter = 0 - loggingPipelineBehaviorHandleBeforeNextCounter = 0 - loggingPipelineBehaviorHandleAfterNextCounter = 0 - inheritedPipelineBehaviourHandleCounter = 0 - commandTestCounter = 0 - } - - private class MyCommand : Command - - private class MyCommandHandler : CommandHandler { - override suspend fun handle(command: MyCommand) { - commandTestCounter++ - delay(500) - } - } - - @Test - fun `should process command without async pipeline`() { - val handler = MyCommandHandler() - val handlers: HashMap, Any> = - hashMapOf( - Pair(MyCommandHandler::class.java, handler) - ) - - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - runBlocking { - bus.send(MyCommand()) - } - - assertTrue { commandTestCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 0 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 0 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 0 } - } - - @Test - fun `should process command with async pipeline`() { - val handler = MyCommandHandler() - val exceptionPipeline = ExceptionPipelineBehavior() - val loggingPipeline = LoggingPipelineBehavior() - val handlers: HashMap, Any> = - hashMapOf( - Pair(MyCommandHandler::class.java, handler), - Pair(ExceptionPipelineBehavior::class.java, exceptionPipeline), - Pair(LoggingPipelineBehavior::class.java, loggingPipeline) - ) - - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - runBlocking { - bus.send(MyCommand()) - } - - assertTrue { commandTestCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 1 } - } - - @Test - fun `should process exception in async handler`() { - val handler = MyBrokenHandler() - val exceptionPipeline = ExceptionPipelineBehavior() - val loggingPipeline = LoggingPipelineBehavior() - val handlers: HashMap, Any> = - hashMapOf( - Pair(MyBrokenHandler::class.java, handler), - Pair(ExceptionPipelineBehavior::class.java, exceptionPipeline), - Pair(LoggingPipelineBehavior::class.java, loggingPipeline) - ) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - val act = suspend { bus.send(MyBrokenCommand()) } - - assertThrows { runBlocking { act() } } - assertTrue { commandTestCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 0 } - } - - @Test - fun `should process command with inherited pipeline`() = - runBlocking { - val handler = MyCommandHandler() - val pipeline = InheritedPipelineBehaviour() - val handlers: HashMap, Any> = - hashMapOf( - Pair(MyCommandHandler::class.java, handler), - Pair(InheritedPipelineBehaviour::class.java, pipeline) - ) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - bus.send(MyCommand()) - - assertEquals(1, inheritedPipelineBehaviourHandleCounter) - } -} - -private abstract class MyBasePipelineBehaviour : PipelineBehavior - -private class InheritedPipelineBehaviour : MyBasePipelineBehaviour() { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - inheritedPipelineBehaviourHandleCounter++ - return next(request) - } -} - -private class MyCommand : Command - -private class MyBrokenCommand : Command - -private class MyBrokenHandler : CommandHandler { - override suspend fun handle(command: MyBrokenCommand) { - throw Exception() - } -} - -private class ExceptionPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - try { - exceptionPipelineBehaviorHandleCounter++ - return next(request) - } catch (ex: Exception) { - exceptionPipelineBehaviorHandleCatchCounter++ - throw ex - } - } -} - -private class LoggingPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - loggingPipelineBehaviorHandleBeforeNextCounter++ - val result = next(request) - loggingPipelineBehaviorHandleAfterNextCounter++ - return result - } -} +class PipelineBehaviorTest : PipelineBehaviorUseCases() diff --git a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/QueryHandlerTest.kt b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/QueryHandlerTest.kt index 2910f18..11f0b48 100644 --- a/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/QueryHandlerTest.kt +++ b/projects/kediatr-core/src/test/kotlin/com/trendyol/kediatr/QueryHandlerTest.kt @@ -1,79 +1,5 @@ package com.trendyol.kediatr -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import com.trendyol.kediatr.coreUseCases.QueryHandlerUseCases -class QueryHandlerTest { - @Test - fun async_queryHandler_should_retrieve_result() = - runBlocking { - class TestQuery(val id: Int) : Query - - class TestQueryHandler : QueryHandler { - override suspend fun handle(query: TestQuery): String { - return "hello " + query.id - } - } - - val handler = TestQueryHandler() - val handlers: HashMap, Any> = hashMapOf(Pair(TestQueryHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - val result = bus.send(TestQuery(1)) - - assertTrue { - result == "hello 1" - } - } - - @Test - fun should_throw_exception_if_given_async_query_has_not_been_registered_before() { - class NonExistQuery : Query - - val handlers: HashMap, Any> = hashMapOf() - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - bus.send(NonExistQuery()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for ${NonExistQuery::class.java.typeName}", exception.message) - } - - @Nested - inner class ParamaterizedTests { - inner class ParameterizedQuery(val param: TParam) : Query - - inner class ParameterizedQueryHandler : QueryHandler, String> { - override suspend fun handle(query: ParameterizedQuery): String { - return query.param.toString() - } - } - - @Test - fun async_query_should_be_fired_and_return_result() = - runBlocking { - // given - val handler = ParameterizedQueryHandler>() - val handlers: HashMap, Any> = hashMapOf(Pair(ParameterizedQueryHandler::class.java, handler)) - val provider = ManualDependencyProvider(handlers) - val bus: Mediator = MediatorBuilder(provider).build() - - // when - val result = bus.send(ParameterizedQuery(61L)) - - // then - assertEquals("61", result) - } - } -} +class QueryHandlerTest : QueryHandlerUseCases() diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/CommandHandlerUseCases.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/CommandHandlerUseCases.kt new file mode 100644 index 0000000..3cbee3c --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/CommandHandlerUseCases.kt @@ -0,0 +1,135 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.Command +import com.trendyol.kediatr.CommandHandler +import com.trendyol.kediatr.HandlerNotFoundException +import com.trendyol.kediatr.Mediator +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test + +abstract class CommandHandlerUseCases : MediatorTestConvention() { + @Test + fun async_commandHandler_should_be_fired() = runTest { + var invocationCount = 0 + + class MyAsyncCommand : Command + + class MyCommandHandler : CommandHandler { + override suspend fun handle(command: MyAsyncCommand) { + invocationCount++ + } + } + + val bus: Mediator = newMediator(handlers = listOf(MyCommandHandler())) + bus.send(MyAsyncCommand()) + + invocationCount shouldBe 1 + } + + @Test + fun `should throw exception if given async command has not been registered before`() = runTest { + class NonExistCommand : Command + + val bus: Mediator = newMediator() + + val exception = shouldThrow { + bus.send(NonExistCommand()) + } + + exception.message shouldBe "handler could not be found for ${NonExistCommand::class.java.typeName}" + } + + @Test + fun inheritance_should_work() = runTest { + var invocationCount = 0 + + class MyCommandForInheritance : Command + + abstract class MyCommandHandlerFor : CommandHandler + + class MyInheritedCommandHandler : MyCommandHandlerFor() { + override suspend fun handle(command: MyCommandForInheritance) { + invocationCount++ + } + } + + val handler = MyInheritedCommandHandler() + val bus: Mediator = newMediator(handlers = listOf(handler)) + bus.send(MyCommandForInheritance()) + + invocationCount shouldBe 1 + } + + @Test + fun inheritance_but_not_parameterized_should_work() = runTest { + var invocationCount = 0 + + class MyCommandForInheritance : Command + + abstract class MyCommandHandlerBaseForSpecificCommand : CommandHandler + + class MyInheritedCommandHandlerForSpecificCommand : MyCommandHandlerBaseForSpecificCommand() { + override suspend fun handle(command: MyCommandForInheritance) { + invocationCount++ + } + } + + val handler = MyInheritedCommandHandlerForSpecificCommand() + val bus: Mediator = newMediator(handlers = listOf(handler)) + bus.send(MyCommandForInheritance()) + + invocationCount shouldBe 1 + } + + @Test + fun async_command_should_be_fired() = runTest { + var invocationCount = 0 + + class ParameterizedCommand(val param: T) : Command + + class ParameterizedCommandHandler : CommandHandler> { + override suspend fun handle(command: ParameterizedCommand) { + command.param shouldBe "MyParam" + invocationCount++ + } + } + + // given + val handler = ParameterizedCommandHandler>() + val bus: Mediator = newMediator(handlers = listOf(handler)) + + // when + bus.send(ParameterizedCommand("MyParam")) + + // then + invocationCount shouldBe 1 + } + + @Test + fun async_commandHandler_with_inheritance_should_be_fired() = runTest { + var invocationCount = 0 + + class ParameterizedCommand(val param: T) : Command + + abstract class ParameterizedCommandHandlerBase : CommandHandler> + + class ParameterizedCommandHandler : ParameterizedCommandHandlerBase() { + override suspend fun handle(command: ParameterizedCommand) { + command.param shouldBe "MyParam" + invocationCount++ + } + } + + // given + val handler = ParameterizedCommandHandler>() + val bus: Mediator = newMediator(handlers = listOf(handler)) + + // when + bus.send(ParameterizedCommand("MyParam")) + + // then + invocationCount shouldBe 1 + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/CommandWithResultHandlerUseCases.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/CommandWithResultHandlerUseCases.kt new file mode 100644 index 0000000..de80ec3 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/CommandWithResultHandlerUseCases.kt @@ -0,0 +1,123 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.* +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.delay +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.* + +private var counter = 0 +private var asyncTestCounter = 0 + +abstract class CommandWithResultHandlerUseCases : MediatorTestConvention() { + @BeforeEach + fun beforeEach() { + counter = 0 + asyncTestCounter = 0 + } + + @Test + fun `async commandHandler should be fired`() = runTest { + val handler = AsyncMyCommandRHandler() + val bus: Mediator = newMediator(handlers = listOf(handler)) + bus.send(MyAsyncCommandR()) + asyncTestCounter shouldBe 1 + } + + @Test + fun `should throw exception if given async command has not been registered before`() = runTest { + val bus: Mediator = newMediator() + val exception = shouldThrow { + bus.send(NonExistCommandR()) + } + + exception.message shouldBe "handler could not be found for ${NonExistCommandR::class.java.typeName}" + } + + @Test + fun inheritance_should_work_with_command_with_result() = runTest { + var invocationCount = 0 + + class MyAsyncCommand : CommandWithResult + + class AsyncMyCommandHandler : CommandWithResultHandler { + override suspend fun handle(command: MyAsyncCommand): Result { + invocationCount++ + return Result() + } + } + + val handler = AsyncMyCommandHandler() + val bus: Mediator = newMediator(handlers = listOf(handler)) + bus.send(MyAsyncCommand()) + + invocationCount shouldBe 1 + } + + inner class ParameterizedCommandWithResult(val param: TParam) : CommandWithResult + + inner class ParameterizedAsyncCommandWithResultHandler : + CommandWithResultHandler, String> { + override suspend fun handle(command: ParameterizedCommandWithResult): String { + counter++ + return command.param.toString() + } + } + + @Test + fun `async commandWithResult should be fired and return result`() = runTest { + // given + val handler = ParameterizedAsyncCommandWithResultHandler>() + val bus: Mediator = newMediator(handlers = listOf(handler)) + + // when + val result = bus.send(ParameterizedCommandWithResult(61L)) + + // then + counter shouldBe 1 + result shouldBe "61" + } + + @Test + fun inheritance_should_work_with_command_with_result_and_parameter() = runTest { + var invocationCount = 0 + + class ParameterizedCommandWithResult(val param: TParam) : CommandWithResult + + abstract class ParameterizedCommandWithResultHandlerBase> : + CommandWithResultHandler + + class Handler : ParameterizedCommandWithResultHandlerBase>() { + override suspend fun handle(command: ParameterizedCommandWithResult): String { + invocationCount++ + return command.param.toString() + } + } + + val handler = Handler>() + val bus: Mediator = newMediator(handlers = listOf(handler)) + + // when + val result = bus.send(ParameterizedCommandWithResult("invoked")) + + // then + invocationCount shouldBe 1 + result shouldBe "invoked" + } +} + +private class Result + +private class NonExistCommandR : Command + +private class MyAsyncCommandR : CommandWithResult + +private class AsyncMyCommandRHandler : CommandWithResultHandler { + override suspend fun handle(command: MyAsyncCommandR): Result { + delay(500) + asyncTestCounter++ + + return Result() + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/MappingDependencyProvider.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/MappingDependencyProvider.kt new file mode 100644 index 0000000..4ccd1f0 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/MappingDependencyProvider.kt @@ -0,0 +1,50 @@ +@file:Suppress("UNCHECKED_CAST") + +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.DependencyProvider +import java.lang.reflect.ParameterizedType + +class MappingDependencyProvider( + private val handlerMap: HashMap, Any> +) : DependencyProvider { + override fun getSingleInstanceOf(clazz: Class): T { + return handlerMap[clazz] as T + } + + override fun getSubTypesOf(clazz: Class): Collection> = handlerMap + .filter { filterInternal(it.key, clazz) } + .map { + it.key as Class + } + + private fun filterInternal( + handler: Class<*>, + interfaceOrBaseClass: Class + ): Boolean { + if (interfaceOrBaseClass.isAssignableFrom(handler)) return true + + if (handler.genericInterfaces + .filterIsInstance() + .any { it.rawType == interfaceOrBaseClass } + ) { + return true + } + + return when (handler.genericSuperclass) { + is ParameterizedType -> { + val inheritedHandler = (handler.genericSuperclass as ParameterizedType).rawType as Class<*> + inheritedHandler.genericInterfaces + .filterIsInstance() + .any { it.rawType == interfaceOrBaseClass } + } + + is Class<*> -> { + val inheritedHandler = (handler.genericSuperclass as Class<*>) + interfaceOrBaseClass.isAssignableFrom(inheritedHandler) + } + + else -> false + } + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/NotificationHandlerUseCases.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/NotificationHandlerUseCases.kt new file mode 100644 index 0000000..7f15fe8 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/NotificationHandlerUseCases.kt @@ -0,0 +1,131 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.Mediator +import com.trendyol.kediatr.Notification +import com.trendyol.kediatr.NotificationHandler +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.* +import java.util.concurrent.CountDownLatch + +private var asyncCountDownLatch = CountDownLatch(1) + +abstract class NotificationHandlerUseCases : MediatorTestConvention() { + @BeforeEach + fun beforeEach() { + asyncCountDownLatch = CountDownLatch(1) + } + + @Test + fun `async notification handler should be called`() = runTest { + val pingHandler = APingHandler() + val anotherPingHandler = AnotherPingHandler() + val bus: Mediator = newMediator(handlers = listOf(pingHandler, anotherPingHandler)) + bus.publish(ExtendedPing()) + + asyncCountDownLatch.count shouldBe 0 + } + + @Test + fun multiple_handlers_for_a_notification_should_be_dispatched() = runTest { + var invocationCount = 0 + + class MyNotification : Notification + + class Handler1 : NotificationHandler { + override suspend fun handle(notification: MyNotification) { + invocationCount++ + } + } + + class Handler2 : NotificationHandler { + override suspend fun handle(notification: MyNotification) { + invocationCount++ + } + } + + val bus: Mediator = newMediator(handlers = listOf(Handler1(), Handler2())) + bus.publish(MyNotification()) + invocationCount shouldBe 2 + } + + @Test + fun inherited_notification_handler_should_be_called() = runTest { + class PingForInherited : Notification + + abstract class NotificationHandlerBase : NotificationHandler + + class InheritedNotificationHandler : NotificationHandlerBase() { + override suspend fun handle(notification: PingForInherited) { + asyncCountDownLatch.countDown() + } + } + + val bus: Mediator = newMediator(handlers = listOf(InheritedNotificationHandler())) + bus.publish(PingForInherited()) + + asyncCountDownLatch.count shouldBe 0 + } + + inner class ParameterizedNotification(val param: T) : Notification + + inner class ParameterizedNotificationHandler : NotificationHandler> { + override suspend fun handle(notification: ParameterizedNotification) { + notification.param shouldBe "MyParam" + asyncCountDownLatch.countDown() + } + } + + @Test + fun `async notification should be fired`() = runTest { + // given + val handler = ParameterizedNotificationHandler>() + val bus: Mediator = newMediator(handlers = listOf(handler)) + + // when + bus.publish(ParameterizedNotification("MyParam")) + + // then + asyncCountDownLatch.count shouldBe 0 + } + + @Test + fun inherited_notification_handler_should_be_called_with_param() = runTest { + var invocationCount = 0 + var parameter = "" + + class ParameterizedNotification(val param: T) : Notification + + abstract class NotificationHandlerBase : NotificationHandler + + class ParameterizedNotificationHandler : NotificationHandlerBase>() { + override suspend fun handle(notification: ParameterizedNotification) { + parameter = notification.param.toString() + invocationCount++ + } + } + + val nHandler = ParameterizedNotificationHandler>() + val bus: Mediator = newMediator(handlers = listOf(nHandler)) + bus.publish(ParameterizedNotification("invoked")) + + invocationCount shouldBe 1 + parameter shouldBe "invoked" + } +} + +private open class Ping : Notification + +private class ExtendedPing : Ping() + +private class APingHandler : NotificationHandler { + override suspend fun handle(notification: ExtendedPing) { + asyncCountDownLatch.countDown() + } +} + +private class AnotherPingHandler : NotificationHandler { + override suspend fun handle(notification: Ping) { + asyncCountDownLatch.countDown() + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/PipelineBehaviorUseCases.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/PipelineBehaviorUseCases.kt new file mode 100644 index 0000000..5e51717 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/PipelineBehaviorUseCases.kt @@ -0,0 +1,138 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.* +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.delay +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.* + +var exceptionPipelineBehaviorHandleCounter = 0 +var exceptionPipelineBehaviorHandleCatchCounter = 0 +var loggingPipelineBehaviorHandleBeforeNextCounter = 0 +var loggingPipelineBehaviorHandleAfterNextCounter = 0 +var inheritedPipelineBehaviourHandleCounter = 0 +var commandTestCounter = 0 + +abstract class PipelineBehaviorUseCases : MediatorTestConvention() { + @BeforeEach + fun beforeEach() { + exceptionPipelineBehaviorHandleCounter = 0 + exceptionPipelineBehaviorHandleCatchCounter = 0 + loggingPipelineBehaviorHandleBeforeNextCounter = 0 + loggingPipelineBehaviorHandleAfterNextCounter = 0 + inheritedPipelineBehaviourHandleCounter = 0 + commandTestCounter = 0 + } + + private class MyCommand : Command + + private class MyCommandHandler : CommandHandler { + override suspend fun handle(command: MyCommand) { + commandTestCounter++ + delay(500) + } + } + + @Test + fun `should process command without async pipeline`() = runTest { + val handler = MyCommandHandler() + val mediator: Mediator = newMediator(handlers = listOf(handler)) + mediator.send(MyCommand()) + + commandTestCounter shouldBe 1 + exceptionPipelineBehaviorHandleCatchCounter shouldBe 0 + exceptionPipelineBehaviorHandleCounter shouldBe 0 + loggingPipelineBehaviorHandleBeforeNextCounter shouldBe 0 + loggingPipelineBehaviorHandleAfterNextCounter shouldBe 0 + } + + @Test + fun `should process command with async pipeline`() = runTest { + val handler = MyCommandHandler() + val exceptionPipeline = ExceptionPipelineBehavior() + val loggingPipeline = LoggingPipelineBehavior() + val bus: Mediator = newMediator(handlers = listOf(exceptionPipeline, loggingPipeline) + listOf(handler)) + + bus.send(MyCommand()) + + commandTestCounter shouldBe 1 + exceptionPipelineBehaviorHandleCatchCounter shouldBe 0 + exceptionPipelineBehaviorHandleCounter shouldBe 1 + loggingPipelineBehaviorHandleBeforeNextCounter shouldBe 1 + loggingPipelineBehaviorHandleAfterNextCounter shouldBe 1 + } + + @Test + fun `should process exception in async handler`() = runTest { + val handler = MyBrokenHandler() + val exceptionPipeline = ExceptionPipelineBehavior() + val loggingPipeline = LoggingPipelineBehavior() + val bus: Mediator = newMediator(handlers = listOf(handler) + listOf(exceptionPipeline, loggingPipeline)) + val act = suspend { bus.send(MyBrokenCommand()) } + + shouldThrow { act() } + commandTestCounter shouldBe 0 + exceptionPipelineBehaviorHandleCatchCounter shouldBe 1 + exceptionPipelineBehaviorHandleCounter shouldBe 1 + loggingPipelineBehaviorHandleBeforeNextCounter shouldBe 1 + loggingPipelineBehaviorHandleAfterNextCounter shouldBe 0 + } + + @Test + fun `should process command with inherited pipeline`() = runTest { + val handler = MyCommandHandler() + val pipeline = InheritedPipelineBehaviour() + val bus: Mediator = newMediator(handlers = listOf(handler) + listOf(pipeline)) + bus.send(MyCommand()) + + inheritedPipelineBehaviourHandleCounter shouldBe 1 + } +} + +private abstract class MyBasePipelineBehaviour : PipelineBehavior + +private class InheritedPipelineBehaviour : MyBasePipelineBehaviour() { + override suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse { + inheritedPipelineBehaviourHandleCounter++ + return next(request) + } +} + +private class MyBrokenCommand : Command + +private class MyBrokenHandler : CommandHandler { + override suspend fun handle(command: MyBrokenCommand) { + throw Exception() + } +} + +private class ExceptionPipelineBehavior : PipelineBehavior { + override suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse { + try { + exceptionPipelineBehaviorHandleCounter++ + return next(request) + } catch (ex: Exception) { + exceptionPipelineBehaviorHandleCatchCounter++ + throw ex + } + } +} + +private class LoggingPipelineBehavior : PipelineBehavior { + override suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse { + loggingPipelineBehaviorHandleBeforeNextCounter++ + val result = next(request) + loggingPipelineBehaviorHandleAfterNextCounter++ + return result + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/PublishStrategyTests.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/PublishStrategyTests.kt new file mode 100644 index 0000000..2ad7170 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/PublishStrategyTests.kt @@ -0,0 +1,23 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.* +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class PublishStrategyTests { + @Test + fun `When a publish strategy is defined it should be set`() { + listOf( + ContinueOnExceptionPublishStrategy(), + ParallelNoWaitPublishStrategy(), + ParallelWhenAllPublishStrategy(), + StopOnExceptionPublishStrategy() + ).forEach { + val builder = MediatorBuilder(MappingDependencyProvider(hashMapOf())) + .withPublishStrategy(it) + + // Assert + builder.defaultPublishStrategy shouldBe it + } + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/QueryHandlerUseCases.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/QueryHandlerUseCases.kt new file mode 100644 index 0000000..b269592 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/QueryHandlerUseCases.kt @@ -0,0 +1,62 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.HandlerNotFoundException +import com.trendyol.kediatr.Mediator +import com.trendyol.kediatr.Query +import com.trendyol.kediatr.QueryHandler +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test + +abstract class QueryHandlerUseCases : MediatorTestConvention() { + @Test + fun async_queryHandler_should_retrieve_result() = runTest { + class TestQuery(val id: Int) : Query + + class TestQueryHandler : QueryHandler { + override suspend fun handle(query: TestQuery): String { + return "hello " + query.id + } + } + + val handler = TestQueryHandler() + val bus: Mediator = newMediator(handlers = listOf(handler)) + val result = bus.send(TestQuery(1)) + + result shouldBe "hello 1" + } + + @Test + fun should_throw_exception_if_given_async_query_has_not_been_registered_before() = runTest { + class NonExistQuery : Query + + val bus: Mediator = newMediator() + val exception = shouldThrow { + bus.send(NonExistQuery()) + } + + exception.message shouldBe "handler could not be found for ${NonExistQuery::class.java.typeName}" + } + + inner class ParameterizedQuery(val param: TParam) : Query + + inner class ParameterizedQueryHandler : QueryHandler, String> { + override suspend fun handle(query: ParameterizedQuery): String { + return query.param.toString() + } + } + + @Test + fun async_query_should_be_fired_and_return_result() = runTest { + // given + val handler = ParameterizedQueryHandler>() + val bus: Mediator = newMediator(handlers = listOf(handler)) + + // when + val result = bus.send(ParameterizedQuery(61L)) + + // then + result shouldBe "61" + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/convention.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/convention.kt new file mode 100644 index 0000000..b2fc1f4 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/coreUseCases/convention.kt @@ -0,0 +1,16 @@ +package com.trendyol.kediatr.coreUseCases + +import com.trendyol.kediatr.Mediator +import com.trendyol.kediatr.MediatorBuilder + +@Suppress("UNCHECKED_CAST") +abstract class MediatorTestConvention { + protected fun newMediator(handlers: List = emptyList()): Mediator = createMediator(handlers) + + private fun createMediator( + handlers: List = emptyList() + ): Mediator { + val provider = MappingDependencyProvider(handlers.associateBy { it.javaClass } as HashMap, Any>) + return MediatorBuilder(provider).build() + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/MediatorUseCases.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/MediatorUseCases.kt new file mode 100644 index 0000000..32e91d8 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/MediatorUseCases.kt @@ -0,0 +1,73 @@ +package com.trendyol.kediatr.framewokUseCases + +import com.trendyol.kediatr.HandlerNotFoundException +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.* + +abstract class MediatorUseCases : MediatorDIConvention { + @Test + fun command() = runTest { + val count = 0 + val result = testMediator.send(TestCommandWithResult(count)) + result.value shouldBe count + 1 + } + + @Test + fun commandWithoutAHandler() = runTest { + val exception = shouldThrow { + testMediator.send(TestNonExistCommand()) + } + + exception.message shouldBe "handler could not be found for ${TestNonExistCommand::class.java.typeName}" + } + + @Test + fun commandWithResult() = runTest { + val count = 0 + val result = testMediator.send(TestCommandWithResult(count)) + result.value shouldBe count + 1 + } + + @Test + fun commandWithResultWithoutAHandler() = runTest { + val exception = shouldThrow { + testMediator.send(NonExistCommandWithResult()) + } + + exception.message shouldBe "handler could not be found for ${NonExistCommandWithResult::class.java.typeName}" + } + + @Test + fun notification() = runTest { + testMediator.publish(TestNotification()) + } + + @Test + fun `should process command with async pipeline`() = runTest { + testMediator.send(TestPipelineCommand()) + } + + @Test + fun `should process exception in async handler`() = runTest { + val act = suspend { testMediator.send(TestBrokenCommand()) } + assertThrows { act() } + } + + @Test + fun `should throw exception if given async query does not have handler bean`() = runTest { + val exception = shouldThrow { + testMediator.send(NonExistQuery()) + } + + exception.message shouldBe "handler could not be found for ${NonExistQuery::class.java.typeName}" + } + + @Test + fun `should retrieve result from async query handler bean`() = runTest { + val result = testMediator.send(TestQuery(1)) + + result shouldBe "hello 1" + } +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/convention.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/convention.kt new file mode 100644 index 0000000..af4b6fe --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/convention.kt @@ -0,0 +1,9 @@ +package com.trendyol.kediatr.framewokUseCases + +import com.trendyol.kediatr.Mediator + +interface MediatorDIConvention { + fun provideMediator(): Mediator + + val testMediator get() = provideMediator() +} diff --git a/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/models.kt b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/models.kt new file mode 100644 index 0000000..37f8d08 --- /dev/null +++ b/projects/kediatr-core/src/testFixtures/kotlin/com/trendyol/kediatr/framewokUseCases/models.kt @@ -0,0 +1,101 @@ +package com.trendyol.kediatr.framewokUseCases + +import com.trendyol.kediatr.* +import io.kotest.matchers.shouldNotBe + +class TestNonExistCommand : Command + +class TestCommand : Command + +class TestCommandHandler( + private val mediator: Mediator +) : CommandHandler { + override suspend fun handle(command: TestCommand) { + mediator shouldNotBe null + } +} + +class Result( + val value: Int = 0 +) + +class NonExistCommandWithResult : CommandWithResult + +data class TestCommandWithResult(val invoked: Int = 0) : CommandWithResult + +class TestCommandWithResultCommandHandler( + val mediator: Mediator +) : CommandWithResultHandler { + override suspend fun handle(command: TestCommandWithResult): Result = Result(command.invoked + 1) +} + +class TestNotification : Notification + +class TestNotificationHandler( + private val mediator: Mediator +) : NotificationHandler { + override suspend fun handle(notification: TestNotification) { + mediator shouldNotBe null + } +} + +class TestBrokenCommand : Command + +class TestPipelineCommand : Command + +class TestPipelineCommandHandler( + val mediator: Mediator +) : CommandHandler { + override suspend fun handle(command: TestPipelineCommand) { + mediator shouldNotBe null + } +} + +class TestBrokenCommandHandler( + private val mediator: Mediator +) : CommandHandler { + override suspend fun handle(command: TestBrokenCommand) { + mediator shouldNotBe null + throw Exception() + } +} + +class ExceptionPipelineBehavior : PipelineBehavior { + override suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse { + try { +// exceptionPipelineBehaviorHandleCounter++ + return next(request) + } catch (ex: Exception) { +// exceptionPipelineBehaviorHandleCatchCounter++ + throw ex + } + } +} + +class LoggingPipelineBehavior : PipelineBehavior { + override suspend fun handle( + request: TRequest, + next: RequestHandlerDelegate + ): TResponse { +// loggingPipelineBehaviorHandleBeforeNextCounter++ + val result = next(request) +// loggingPipelineBehaviorHandleAfterNextCounter++ + return result + } +} + +class NonExistQuery : Query + +class TestQuery(val id: Int) : Query + +class TestQueryHandler( + private val mediator: Mediator +) : QueryHandler { + override suspend fun handle(query: TestQuery): String { + mediator shouldNotBe null + return "hello " + query.id + } +} diff --git a/projects/kediatr-koin-starter/build.gradle.kts b/projects/kediatr-koin-starter/build.gradle.kts index f7f70fe..6ac5a5b 100644 --- a/projects/kediatr-koin-starter/build.gradle.kts +++ b/projects/kediatr-koin-starter/build.gradle.kts @@ -1,10 +1,11 @@ dependencies { - api(project(":projects:kediatr-core")) - implementation("org.reflections:reflections:0.10.2") - implementation("io.insert-koin:koin-core:4.0.0") + api(projects.projects.kediatrCore) + implementation(libs.org.reflections) + implementation(libs.koin.core) } dependencies { - testImplementation("io.insert-koin:koin-test:4.0.0") - testImplementation("io.insert-koin:koin-test-junit5:4.0.0") + testImplementation(testFixtures(projects.projects.kediatrCore)) + testImplementation(libs.koin.test) + testImplementation(libs.koin.test.junit5) } diff --git a/projects/kediatr-koin-starter/src/main/kotlin/com/trendyol/kediatr/koin/KediatRKoinProvider.kt b/projects/kediatr-koin-starter/src/main/kotlin/com/trendyol/kediatr/koin/KediatRKoinProvider.kt index 9814607..4c16deb 100644 --- a/projects/kediatr-koin-starter/src/main/kotlin/com/trendyol/kediatr/koin/KediatRKoinProvider.kt +++ b/projects/kediatr-koin-starter/src/main/kotlin/com/trendyol/kediatr/koin/KediatRKoinProvider.kt @@ -2,8 +2,7 @@ package com.trendyol.kediatr.koin -import com.trendyol.kediatr.DependencyProvider -import com.trendyol.kediatr.MediatorBuilder +import com.trendyol.kediatr.* import org.koin.core.Koin import org.koin.core.annotation.KoinInternalApi import org.koin.java.KoinJavaComponent.getKoin @@ -11,30 +10,29 @@ import kotlin.reflect.KClass @OptIn(KoinInternalApi::class) class KediatRKoinProvider : DependencyProvider { - private val koin: Koin = getKoin() - private val subTypes: List> - get() = - koin - .instanceRegistry.instances - .map { it.value.beanDefinition } - .fold(mutableListOf>()) { acc, beanDefinition -> - acc.add(beanDefinition.primaryType) - acc.addAll(beanDefinition.secondaryTypes) - acc - }.distinct() + private val koin: Koin = getKoin() + private val subTypes: List> + get() = koin + .instanceRegistry.instances + .map { it.value.beanDefinition } + .fold(mutableListOf>()) { acc, beanDefinition -> + acc.also { + it.add(beanDefinition.primaryType) + it.addAll(beanDefinition.secondaryTypes) + } + }.distinct() - override fun getSingleInstanceOf(clazz: Class): T = koin.get(clazz.kClass()) + override fun getSingleInstanceOf(clazz: Class): T = koin.get(clazz.kClass()) - override fun getSubTypesOf(clazz: Class): Collection> = - subTypes - .filter { clazz.isAssignableFrom(it.java) } - .map { it.java as Class } + override fun getSubTypesOf(clazz: Class): Collection> = subTypes + .filter { clazz.isAssignableFrom(it.java) } + .map { it.java as Class } - private fun Class.kClass(): KClass = (this as Class<*>).kotlin + private fun Class.kClass(): KClass = (this as Class<*>).kotlin } class KediatRKoin { - companion object { - fun getMediator() = MediatorBuilder(KediatRKoinProvider()).build() - } + companion object { + fun getMediator() = MediatorBuilder(KediatRKoinProvider()).build() + } } diff --git a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/CommandHandlerTests.kt b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/CommandHandlerTests.kt deleted file mode 100644 index 529178e..0000000 --- a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/CommandHandlerTests.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.trendyol.kediatr.koin - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.koin.dsl.module -import org.koin.test.KoinTest -import org.koin.test.inject -import org.koin.test.junit5.KoinTestExtension -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -class CommandHandlerTests : KoinTest { - @JvmField - @RegisterExtension - val koinTestExtension = - KoinTestExtension.create { - modules( - module { - single { KediatRKoin.getMediator() } - single { ExceptionPipelineBehavior() } - single { LoggingPipelineBehavior() } - single { MyCommandHandler(get()) } - } - ) - } - - private val mediator: Mediator by inject() - - init { - springTestCounter = 0 - springAsyncTestCounter = 0 - } - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommand()) - - assertTrue { - springAsyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommand()) - } - } - - assertNotNull(exception) - assertEquals(exception.message, "handler could not be found for com.trendyol.kediatr.koin.NonExistCommand") - } -} - -private var springTestCounter = 0 -private var springAsyncTestCounter = 0 - -class NonExistCommand : Command - -class MyCommand : Command - -class MyCommandHandler( - val mediator: Mediator -) : CommandHandler { - override suspend fun handle(command: MyCommand) { - delay(500) - springAsyncTestCounter++ - } -} diff --git a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/CommandWithResultHandlerTest.kt b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/CommandWithResultHandlerTest.kt deleted file mode 100644 index 152fa54..0000000 --- a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/CommandWithResultHandlerTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.trendyol.kediatr.koin - -import com.trendyol.kediatr.CommandWithResult -import com.trendyol.kediatr.CommandWithResultHandler -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koin.test.KoinTest -import org.koin.test.inject -import org.koin.test.junit5.KoinTestExtension -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -private var testCounter = 0 -private var asyncTestCounter = 0 - -class CommandWithResultHandlerTest : KoinTest { - @JvmField - @RegisterExtension - val koinTestExtension = - KoinTestExtension.create { - modules( - module { - single { KediatRKoin.getMediator() } - single { ExceptionPipelineBehavior() } bind ExceptionPipelineBehavior::class - single { LoggingPipelineBehavior() } bind LoggingPipelineBehavior::class - single { MyAsyncCommandRHandler(get()) } bind CommandWithResultHandler::class - } - ) - } - - init { - testCounter = 0 - asyncTestCounter = 0 - } - - private val mediator by inject() - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommandR()) - - assertTrue { - asyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommandR()) - } - } - - assertNotNull(exception) - assertEquals(exception.message, "handler could not be found for com.trendyol.kediatr.koin.NonExistCommandR") - } -} - -class Result - -class NonExistCommandR : CommandWithResult - -class MyCommandR : CommandWithResult - -class MyAsyncCommandRHandler( - val mediator: Mediator -) : CommandWithResultHandler { - override suspend fun handle(command: MyCommandR): Result { - delay(500) - asyncTestCounter++ - - return Result() - } -} diff --git a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/MediatorTests.kt b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/MediatorTests.kt new file mode 100644 index 0000000..3ce08a1 --- /dev/null +++ b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/MediatorTests.kt @@ -0,0 +1,32 @@ +package com.trendyol.kediatr.koin + +import com.trendyol.kediatr.* +import com.trendyol.kediatr.framewokUseCases.* +import org.junit.jupiter.api.extension.RegisterExtension +import org.koin.dsl.* +import org.koin.test.* +import org.koin.test.junit5.KoinTestExtension + +class MediatorTests : KoinTest, MediatorUseCases() { + @JvmField + @RegisterExtension + val koinTestExtension = KoinTestExtension.create { + modules( + module { + single { KediatRKoin.getMediator() } + single { ExceptionPipelineBehavior() } + single { LoggingPipelineBehavior() } + single { TestCommandHandler(get()) } + single { TestCommandWithResultCommandHandler(get()) } bind CommandWithResultHandler::class + single { TestQueryHandler(get()) } bind QueryHandler::class + single { TestNotificationHandler(get()) } bind NotificationHandler::class + single { TestBrokenCommandHandler(get()) } bind CommandHandler::class + single { TestPipelineCommandHandler(get()) } bind CommandHandler::class + } + ) + } + + private val mediator: Mediator by inject() + + override fun provideMediator(): Mediator = mediator +} diff --git a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/NotificationHandlerTest.kt b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/NotificationHandlerTest.kt deleted file mode 100644 index b02397f..0000000 --- a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/NotificationHandlerTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.trendyol.kediatr.koin - -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Notification -import com.trendyol.kediatr.NotificationHandler -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koin.test.KoinTest -import org.koin.test.inject -import org.koin.test.junit5.KoinTestExtension -import kotlin.test.assertTrue - -private var notificationTestCounter = 0 -private var asyncNotificationTestCounter = 0 - -class NotificationHandlerTest : KoinTest { - @JvmField - @RegisterExtension - val koinTestExtension = - KoinTestExtension.create { - modules( - module { - single { KediatRKoin.getMediator() } - single { ExceptionPipelineBehavior() } bind ExceptionPipelineBehavior::class - single { LoggingPipelineBehavior() } bind LoggingPipelineBehavior::class - single { MyFirstNotificationHandler(get()) } bind NotificationHandler::class - } - ) - } - - init { - notificationTestCounter = 0 - asyncNotificationTestCounter = 0 - } - - private val mediator by inject() - - @Test - fun `async notificationHandler should be fired`() = - runBlocking { - mediator.publish(MyNotification()) - - assertTrue { - asyncNotificationTestCounter == 1 - } - } -} - -class MyNotification : Notification - -class MyFirstNotificationHandler( - private val mediator: Mediator -) : NotificationHandler { - override suspend fun handle(notification: MyNotification) { - delay(500) - asyncNotificationTestCounter++ - } -} diff --git a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/PipelineBehaviorTest.kt b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/PipelineBehaviorTest.kt deleted file mode 100644 index 7aabe8e..0000000 --- a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/PipelineBehaviorTest.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.trendyol.kediatr.koin - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.CommandWithResultHandler -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.NotificationHandler -import com.trendyol.kediatr.PipelineBehavior -import com.trendyol.kediatr.QueryHandler -import com.trendyol.kediatr.RequestHandlerDelegate -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.RegisterExtension -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koin.test.KoinTest -import org.koin.test.inject -import org.koin.test.junit5.KoinTestExtension -import kotlin.test.assertTrue - -var exceptionPipelineBehaviorHandleCounter = 0 -var exceptionPipelineBehaviorHandleCatchCounter = 0 -var loggingPipelineBehaviorHandleBeforeNextCounter = 0 -var loggingPipelineBehaviorHandleAfterNextCounter = 0 - -class PipelineBehaviorTest : KoinTest { - @JvmField - @RegisterExtension - val koinTestExtension = - KoinTestExtension.create { - modules( - module { - single { KediatRKoin.getMediator() } - single { ExceptionPipelineBehavior() } bind ExceptionPipelineBehavior::class - single { LoggingPipelineBehavior() } bind LoggingPipelineBehavior::class - single { MyCommandHandler(get()) } bind CommandHandler::class - single { MyAsyncCommandRHandler(get()) } bind CommandWithResultHandler::class - single { MyFirstNotificationHandler(get()) } bind NotificationHandler::class - single { TestQueryHandler(get()) } bind QueryHandler::class - } - ) - } - - init { - exceptionPipelineBehaviorHandleCounter = 0 - exceptionPipelineBehaviorHandleCatchCounter = 0 - loggingPipelineBehaviorHandleBeforeNextCounter = 0 - loggingPipelineBehaviorHandleAfterNextCounter = 0 - } - - private val mediator by inject() - - @Test - fun `should process command with async pipeline`() { - runBlocking { - mediator.send(MyCommand()) - } - - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 1 } - } - - @Test - fun `should process exception in async handler`() { - val act = suspend { mediator.send(MyBrokenCommand()) } - - assertThrows { runBlocking { act() } } - - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 0 } - } -} - -class MyBrokenCommand : Command - -class MyBrokenHandler( - private val mediator: Mediator -) : CommandHandler { - override suspend fun handle(command: MyBrokenCommand) { - delay(500) - throw Exception() - } -} - -class ExceptionPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - try { - exceptionPipelineBehaviorHandleCounter++ - return next(request) - } catch (ex: Exception) { - exceptionPipelineBehaviorHandleCatchCounter++ - throw ex - } - } -} - -class LoggingPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - loggingPipelineBehaviorHandleBeforeNextCounter++ - val result = next(request) - loggingPipelineBehaviorHandleAfterNextCounter++ - return result - } -} diff --git a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/QueryHandlerTest.kt b/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/QueryHandlerTest.kt deleted file mode 100644 index 7dc469e..0000000 --- a/projects/kediatr-koin-starter/src/test/kotlin/com/trendyol/kediatr/koin/QueryHandlerTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.trendyol.kediatr.koin - -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Query -import com.trendyol.kediatr.QueryHandler -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.koin.dsl.bind -import org.koin.dsl.module -import org.koin.test.KoinTest -import org.koin.test.inject -import org.koin.test.junit5.KoinTestExtension -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull - -class QueryHandlerTest : KoinTest { - private val mediator by inject() - - @JvmField - @RegisterExtension - val koinTestExtension = - KoinTestExtension.create { - modules( - module { - single { KediatRKoin.getMediator() } - single { ExceptionPipelineBehavior() } bind ExceptionPipelineBehavior::class - single { LoggingPipelineBehavior() } bind LoggingPipelineBehavior::class - single { TestQueryHandler(get()) } bind QueryHandler::class - } - ) - } - - @Test - fun `should throw exception if given async query does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistQuery()) - } - } - - assertNotNull(exception) - assertEquals(exception.message, "handler could not be found for com.trendyol.kediatr.koin.NonExistQuery") - } -} - -class NonExistQuery : Query - -class TestQuery(val id: Int) : Query - -class TestQueryHandler( - private val mediator: Mediator -) : QueryHandler { - override suspend fun handle(query: TestQuery): String { - return "hello " + query.id - } -} diff --git a/projects/kediatr-quarkus-starter/build.gradle.kts b/projects/kediatr-quarkus-starter/build.gradle.kts index bc6edb6..5b59e35 100644 --- a/projects/kediatr-quarkus-starter/build.gradle.kts +++ b/projects/kediatr-quarkus-starter/build.gradle.kts @@ -1,21 +1,21 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { - id("io.quarkus") version "3.16.0" + alias(libs.plugins.quarkus) } dependencies { - api(project(":projects:kediatr-core")) - implementation(platform("io.quarkus:quarkus-bom:3.16.0")) - implementation("io.quarkus:quarkus-arc") - implementation("jakarta.enterprise:jakarta.enterprise.cdi-api:4.1.0") + api(projects.projects.kediatrCore) + implementation(platform(libs.quarkusBom)) + implementation(libs.quarkus.arc) + implementation(libs.jakarta.enterpise.cdi.api) } dependencies { - testImplementation(kotlin("test")) - testImplementation("io.quarkus:quarkus-junit5") - testImplementation("io.quarkus:quarkus-jacoco") + testImplementation(testFixtures(projects.projects.kediatrCore)) + testImplementation(libs.quarkus.junit5) } -tasks.test.configure { - environment["QUARKUS_JACOCO_REPORT_LOCATION"] = "/build/jacoco" - environment["QUARKUS_JACOCO_DATA_FILE"] = "build/jacoco/test.exec" +tasks.withType { + mustRunAfter(tasks.quarkusGenerateCode, tasks.quarkusGenerateCodeDev) } diff --git a/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/KediatRBeanProvider.kt b/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/KediatRBeanProvider.kt index 880d498..3d9d789 100644 --- a/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/KediatRBeanProvider.kt +++ b/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/KediatRBeanProvider.kt @@ -2,70 +2,35 @@ package com.trendyol.kediatr.quarkus -import com.trendyol.kediatr.DependencyProvider -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.MediatorBuilder +import com.trendyol.kediatr.* import io.quarkus.runtime.Startup import jakarta.enterprise.context.ApplicationScoped -import jakarta.enterprise.inject.spi.Bean import jakarta.enterprise.inject.spi.BeanManager -import java.util.concurrent.ConcurrentHashMap class KediatRBeanProvider( - private val beanManager: BeanManager + private val beanManager: BeanManager, + private val quarkusTypeScanner: QuarkusTypeScanner ) : DependencyProvider { - private val kediatrPackageNamePrefix = "com.trendyol.kediatr" - private val classHandlerMap: ConcurrentHashMap, Any> = ConcurrentHashMap() - private var initialAllKediatrBeans: List> = emptyList() - - override fun getSingleInstanceOf(clazz: Class): T { - val instance = - classHandlerMap.getOrPut(clazz as Class) { - val beans = beanManager.getBeans(clazz) - val bean = beanManager.resolve(beans) - val context = beanManager.createCreationalContext(bean) - beanManager.getReference(bean, bean.beanClass, context) as Any - } - return instance as T - } - - override fun getSubTypesOf(clazz: Class): Collection> { - if (initialAllKediatrBeans.isEmpty()) { - initialAllKediatrBeans = - beanManager.getBeans(Object::class.java).filter { bean -> - bean.types.any { type -> - type.typeName.startsWith(kediatrPackageNamePrefix) - } - } - } - - val kediatrHandlerBeans: MutableList> = mutableListOf() - initialAllKediatrBeans.forEach { bean -> - val isKediatrHandlerBean = - bean.types.any { type -> - type.typeName.startsWith(clazz.name) - } - if (isKediatrHandlerBean) { - val kediatrHandler = - bean.types.first { !it.typeName.startsWith(clazz.name) && !it.typeName.equals(Object::class.java.name) } - kediatrHandlerBeans.add(kediatrHandler as Class) - } - } - - return kediatrHandlerBeans - } + override fun getSingleInstanceOf(clazz: Class): T { + val beans = beanManager.getBeans(clazz) + val bean = beans.firstOrNull() ?: error("No bean found for class $clazz") + val ctx = beanManager.createCreationalContext(bean) + return beanManager.getReference(bean, clazz, ctx) as T + } + + override fun getSubTypesOf(clazz: Class): Collection> = quarkusTypeScanner.getSubTypesOf(clazz) } @ApplicationScoped class QuarkusMediatorBuilder { - @ApplicationScoped - fun kediatRBeanProvider(beanManager: BeanManager): KediatRBeanProvider { - return KediatRBeanProvider(beanManager) - } - - @ApplicationScoped - @Startup - fun mediator(kediatRBeanProvider: KediatRBeanProvider): Mediator { - return MediatorBuilder(kediatRBeanProvider).build() - } + @ApplicationScoped + fun kediatRBeanProvider( + beanManager: BeanManager, + quarkusTypeScanner: QuarkusTypeScanner + ): KediatRBeanProvider = + KediatRBeanProvider(beanManager, quarkusTypeScanner) + + @ApplicationScoped + @Startup + fun mediator(kediatRBeanProvider: KediatRBeanProvider): Mediator = MediatorBuilder(kediatRBeanProvider).build() } diff --git a/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/QuarkusTypeScanner.kt b/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/QuarkusTypeScanner.kt new file mode 100644 index 0000000..7bb053a --- /dev/null +++ b/projects/kediatr-quarkus-starter/src/main/kotlin/com/trendyol/kediatr/quarkus/QuarkusTypeScanner.kt @@ -0,0 +1,44 @@ +@file:Suppress("UNCHECKED_CAST") + +package com.trendyol.kediatr.quarkus + +import jakarta.enterprise.context.ApplicationScoped +import jakarta.enterprise.inject.spi.* +import java.lang.reflect.* + +@ApplicationScoped +class QuarkusTypeScanner( + private val beanManager: BeanManager +) { + fun getSubTypesOf( + clazz: Class + ): Collection> = beanManager.getBeans(Any::class.java) + .asSequence() + .filterNot(::quarkusThings) + .flatMap { it.types } + .mapNotNull { type -> mapRelevantOrNull(type, clazz) } + .filter { it != clazz } + .distinct() + .toList() + + private fun quarkusThings(it: Bean<*>) = it.beanClass.packageName.contains("io.quarkus") + + private fun mapRelevantOrNull( + type: Type?, + clazz: Class + ) = when (type) { + is ParameterizedType -> getMatchingClass(type.rawType as Class<*>, clazz) ?: getMatchingGenericArgument(type, clazz) + is Class<*> -> getMatchingClass(type, clazz) + else -> null + } + + private fun getMatchingGenericArgument( + type: ParameterizedType, + clazz: Class + ) = type.actualTypeArguments.firstNotNullOfOrNull { arg -> (arg as? Class<*>)?.let { getMatchingClass(it, clazz) } } + + private fun getMatchingClass(type: Class<*>, targetClass: Class): Class? = when { + targetClass.isAssignableFrom(type) -> type as Class + else -> null + } +} diff --git a/projects/kediatr-quarkus-starter/src/main/resources/META-INF/beans.xml b/projects/kediatr-quarkus-starter/src/main/resources/META-INF/beans.xml deleted file mode 100644 index e69de29..0000000 diff --git a/projects/kediatr-quarkus-starter/src/main/resources/application.properties b/projects/kediatr-quarkus-starter/src/main/resources/application.properties deleted file mode 100644 index e69de29..0000000 diff --git a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/CommandHandlerTests.kt b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/CommandHandlerTests.kt deleted file mode 100644 index f1d119f..0000000 --- a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/CommandHandlerTests.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.trendyol.kediatr.quarkus - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import io.quarkus.runtime.Startup -import io.quarkus.test.junit.QuarkusTest -import jakarta.enterprise.context.ApplicationScoped -import jakarta.inject.Inject -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -@QuarkusTest -class CommandHandlerTests { - @Inject - lateinit var mediator: Mediator - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommand()) - assertTrue { - springAsyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommand()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.quarkus.NonExistCommand", exception.message) - } -} - -private var springTestCounter = 0 -private var springAsyncTestCounter = 0 - -class NonExistCommand : Command - -class MyCommand : Command - -@ApplicationScoped -@Startup -class MyCommandHandler( - val mediator: Mediator -) : CommandHandler { - override suspend fun handle(command: MyCommand) { - delay(500) - springAsyncTestCounter++ - } -} diff --git a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/CommandWithResultHandlerTest.kt b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/CommandWithResultHandlerTest.kt deleted file mode 100644 index 8412ae2..0000000 --- a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/CommandWithResultHandlerTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.trendyol.kediatr.quarkus - -import com.trendyol.kediatr.CommandWithResult -import com.trendyol.kediatr.CommandWithResultHandler -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import io.quarkus.runtime.Startup -import io.quarkus.test.junit.QuarkusTest -import jakarta.enterprise.context.ApplicationScoped -import jakarta.inject.Inject -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -private var testCounter = 0 -private var asyncTestCounter = 0 - -@QuarkusTest -class CommandWithResultHandlerTest { - init { - testCounter = 0 - asyncTestCounter = 0 - } - - @Inject - lateinit var mediator: Mediator - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommandR()) - - assertTrue { - asyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommandR()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.quarkus.NonExistCommandR", exception.message) - } -} - -class Result - -class NonExistCommandR : CommandWithResult - -class MyCommandR : CommandWithResult - -@ApplicationScoped -@Startup -class MyAsyncCommandRHandler( - val mediator: Mediator -) : CommandWithResultHandler { - override suspend fun handle(command: MyCommandR): Result { - delay(500) - asyncTestCounter++ - - return Result() - } -} diff --git a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/MediatorTests.kt b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/MediatorTests.kt new file mode 100644 index 0000000..22f3987 --- /dev/null +++ b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/MediatorTests.kt @@ -0,0 +1,39 @@ +package com.trendyol.kediatr.quarkus + +import com.trendyol.kediatr.* +import com.trendyol.kediatr.framewokUseCases.* +import io.quarkus.test.junit.QuarkusTest +import jakarta.enterprise.inject.Produces +import jakarta.inject.Inject + +@QuarkusTest +class MediatorTests : MediatorUseCases() { + @Inject + lateinit var mediator: Mediator + + override fun provideMediator(): Mediator = mediator + + @Produces + fun handler1(mediator: Mediator) = TestCommandHandler(mediator) + + @Produces + fun handler2(mediator: Mediator) = TestCommandWithResultCommandHandler(mediator) + + @Produces + fun handler3(mediator: Mediator): TestBrokenCommandHandler = TestBrokenCommandHandler(mediator) + + @Produces + fun handler4(mediator: Mediator): TestPipelineCommandHandler = TestPipelineCommandHandler(mediator) + + @Produces + fun notificationHandler(mediator: Mediator) = TestNotificationHandler(mediator) + + @Produces + fun pipelineBehaviors(): List = listOf( + ExceptionPipelineBehavior(), + LoggingPipelineBehavior() + ) + + @Produces + fun provideQueryHandler(mediator: Mediator) = TestQueryHandler(mediator) +} diff --git a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/NotificationHandlerTest.kt b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/NotificationHandlerTest.kt deleted file mode 100644 index 2dc1b5e..0000000 --- a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/NotificationHandlerTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.trendyol.kediatr.quarkus - -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Notification -import com.trendyol.kediatr.NotificationHandler -import io.quarkus.runtime.Startup -import io.quarkus.test.junit.QuarkusTest -import jakarta.enterprise.context.ApplicationScoped -import jakarta.inject.Inject -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import kotlin.test.assertTrue - -private var notificationTestCounter = 0 -private var asyncNotificationTestCounter = 0 - -@QuarkusTest -class NotificationHandlerTest { - init { - notificationTestCounter = 0 - asyncNotificationTestCounter = 0 - } - - @Inject - lateinit var mediator: Mediator - - @Test - fun `async notificationHandler should be fired`() = - runBlocking { - mediator.publish(MyNotification()) - - assertTrue { - asyncNotificationTestCounter == 1 - } - } -} - -class MyNotification : Notification - -@ApplicationScoped -@Startup -class MyFirstNotificationHandler( - private val mediator: Mediator -) : NotificationHandler { - override suspend fun handle(notification: MyNotification) { - delay(500) - asyncNotificationTestCounter++ - } -} diff --git a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/PipelineBehaviorTest.kt b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/PipelineBehaviorTest.kt deleted file mode 100644 index 6da89fa..0000000 --- a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/PipelineBehaviorTest.kt +++ /dev/null @@ -1,118 +0,0 @@ -package com.trendyol.kediatr.quarkus - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.PipelineBehavior -import com.trendyol.kediatr.RequestHandlerDelegate -import io.quarkus.runtime.Startup -import io.quarkus.test.junit.QuarkusTest -import jakarta.enterprise.context.ApplicationScoped -import jakarta.inject.Inject -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import kotlin.test.assertTrue - -var exceptionPipelineBehaviorHandleCounter = 0 -var exceptionPipelineBehaviorHandleCatchCounter = 0 -var loggingPipelineBehaviorHandleBeforeNextCounter = 0 -var loggingPipelineBehaviorHandleAfterNextCounter = 0 -var commandTestCounter = 0 - -@QuarkusTest -class PipelineBehaviorTest { - init { - exceptionPipelineBehaviorHandleCounter = 0 - exceptionPipelineBehaviorHandleCatchCounter = 0 - loggingPipelineBehaviorHandleBeforeNextCounter = 0 - loggingPipelineBehaviorHandleAfterNextCounter = 0 - commandTestCounter = 0 - } - - @Inject - lateinit var mediator: Mediator - - @Test - fun `should process command with async pipeline`() { - runBlocking { - mediator.send(MyPipelineCommand()) - } - - assertTrue { commandTestCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 1 } - } - - @Test - fun `should process exception in async handler`() { - val act = suspend { mediator.send(MyBrokenCommand()) } - - assertThrows { runBlocking { act() } } - - assertTrue { commandTestCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 0 } - } -} - -class MyBrokenCommand : Command - -class MyPipelineCommand : Command - -@ApplicationScoped -@Startup -class MyPipelineCommandHandler( - val mediator: Mediator -) : CommandHandler { - override suspend fun handle(command: MyPipelineCommand) { - commandTestCounter++ - } -} - -@ApplicationScoped -@Startup -class MyBrokenHandler( - private val mediator: Mediator -) : CommandHandler { - override suspend fun handle(command: MyBrokenCommand) { - delay(500) - throw Exception() - } -} - -@ApplicationScoped -@Startup -private class ExceptionPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - try { - exceptionPipelineBehaviorHandleCounter++ - return next(request) - } catch (ex: Exception) { - exceptionPipelineBehaviorHandleCatchCounter++ - throw ex - } - } -} - -@ApplicationScoped -@Startup -private class LoggingPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - loggingPipelineBehaviorHandleBeforeNextCounter++ - val result = next(request) - loggingPipelineBehaviorHandleAfterNextCounter++ - return result - } -} diff --git a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/QueryHandlerTest.kt b/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/QueryHandlerTest.kt deleted file mode 100644 index 7522251..0000000 --- a/projects/kediatr-quarkus-starter/src/test/kotlin/com/trendyol/kediatr/quarkus/QueryHandlerTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.trendyol.kediatr.quarkus - -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Query -import com.trendyol.kediatr.QueryHandler -import io.quarkus.runtime.Startup -import io.quarkus.test.junit.QuarkusTest -import jakarta.enterprise.context.ApplicationScoped -import jakarta.inject.Inject -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull - -@QuarkusTest -class QueryHandlerTest { - @Inject - lateinit var mediator: Mediator - - @Test - fun `should throw exception if given async query does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistQuery()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.quarkus.NonExistQuery", exception.message) - } -} - -class NonExistQuery : Query - -class TestQuery(val id: Int) : Query - -@ApplicationScoped -@Startup -class TestQueryHandler( - private val mediator: Mediator -) : QueryHandler { - override suspend fun handle(query: TestQuery): String { - return "hello " + query.id - } -} diff --git a/projects/kediatr-spring-boot-2x-starter/build.gradle.kts b/projects/kediatr-spring-boot-2x-starter/build.gradle.kts index cb8fef0..7810bbc 100644 --- a/projects/kediatr-spring-boot-2x-starter/build.gradle.kts +++ b/projects/kediatr-spring-boot-2x-starter/build.gradle.kts @@ -1,14 +1,14 @@ val springVersion = "2.7.18" dependencies { - api(project(":projects:kediatr-core")) - implementation("org.springframework.boot:spring-boot-starter:$springVersion") + api(project(":projects:kediatr-core")) + implementation("org.springframework.boot:spring-boot-starter:$springVersion") } dependencies { - testImplementation(kotlin("test")) - testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") + testImplementation(testFixtures(projects.projects.kediatrCore)) + testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") } kotlin { - jvmToolchain(11) + jvmToolchain(11) } diff --git a/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt b/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt index 960e9d9..9391dbb 100644 --- a/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt +++ b/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt @@ -1,20 +1,19 @@ package com.trendyol.kediatr.spring -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.MediatorBuilder +import com.trendyol.kediatr.* import org.springframework.boot.autoconfigure.AutoConfiguration import org.springframework.context.ApplicationContext import org.springframework.context.annotation.Bean @AutoConfiguration open class KediatRAutoConfiguration { - @Bean - open fun kediatRSpringBeanProvider(applicationContext: ApplicationContext): KediatRSpringBeanProvider { - return KediatRSpringBeanProvider(applicationContext) - } + @Bean + open fun kediatRSpringBeanProvider( + applicationContext: ApplicationContext + ): KediatRSpringBeanProvider = KediatRSpringBeanProvider(applicationContext) - @Bean - open fun mediator(kediatRSpringBeanProvider: KediatRSpringBeanProvider): Mediator { - return MediatorBuilder(kediatRSpringBeanProvider).build() - } + @Bean + open fun mediator( + kediatRSpringBeanProvider: KediatRSpringBeanProvider + ): Mediator = MediatorBuilder(kediatRSpringBeanProvider).build() } diff --git a/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt b/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt index 9c8619b..e7068e7 100644 --- a/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt +++ b/projects/kediatr-spring-boot-2x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt @@ -6,14 +6,13 @@ import com.trendyol.kediatr.DependencyProvider import org.springframework.context.ApplicationContext class KediatRSpringBeanProvider( - private val applicationContext: ApplicationContext + private val applicationContext: ApplicationContext ) : DependencyProvider { - override fun getSingleInstanceOf(clazz: Class): T { - return applicationContext.getBean(clazz) - } + override fun getSingleInstanceOf(clazz: Class): T = applicationContext.getBean(clazz) - override fun getSubTypesOf(clazz: Class): Collection> { - return applicationContext.getBeanNamesForType(clazz) - .map { applicationContext.getType(it) as Class } - } + override fun getSubTypesOf( + clazz: Class + ): Collection> = applicationContext + .getBeanNamesForType(clazz) + .map { applicationContext.getType(it) as Class } } diff --git a/projects/kediatr-spring-boot-2x-starter/src/main/resources/META-INF/spring.factories b/projects/kediatr-spring-boot-2x-starter/src/main/resources/META-INF/spring.factories index b9df51a..e2e2abe 100644 --- a/projects/kediatr-spring-boot-2x-starter/src/main/resources/META-INF/spring.factories +++ b/projects/kediatr-spring-boot-2x-starter/src/main/resources/META-INF/spring.factories @@ -1,3 +1,3 @@ # Register EnableAutoConfiguration -org.springframework.boot.autoconfigure.EnableAutoConfiguration = \ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.trendyol.kediatr.spring.KediatRAutoConfiguration diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandHandlerTest.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandHandlerTest.kt deleted file mode 100644 index 97c1966..0000000 --- a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandHandlerTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -private var springTestCounter = 0 -private var springAsyncTestCounter = 0 - -@SpringBootTest(classes = [KediatRAutoConfiguration::class, MyCommandHandler::class]) -class CommandHandlerTest { - init { - springTestCounter = 0 - springAsyncTestCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommand()) - - assertTrue { - springAsyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommand()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.spring.NonExistCommand", exception.message) - } -} - -class NonExistCommand : Command - -class MyCommand : Command - -class MyCommandHandler : CommandHandler { - override suspend fun handle(command: MyCommand) { - delay(500) - springAsyncTestCounter++ - } -} diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandWithResultHandlerTest.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandWithResultHandlerTest.kt deleted file mode 100644 index 940bb10..0000000 --- a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandWithResultHandlerTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.* -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.* - -private var springTestCounter = 0 -private var springAsyncTestCounter = 0 - -@SpringBootTest(classes = [KediatRAutoConfiguration::class, MyAsyncCommandRHandler::class]) -class CommandWithResultHandlerTest { - init { - springTestCounter = 0 - springAsyncTestCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommandR()) - - assertTrue { - springAsyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommandR()) - } - } - - assertNotNull(exception) - assertEquals(exception.message, "handler could not be found for com.trendyol.kediatr.spring.NonExistCommandR") - } -} - -class Result - -class NonExistCommandR : CommandWithResult - -class MyCommandR : CommandWithResult - -class MyAsyncCommandRHandler : CommandWithResultHandler { - override suspend fun handle(command: MyCommandR): Result { - delay(500) - springAsyncTestCounter++ - - return Result() - } -} diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/MediatorTests.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/MediatorTests.kt new file mode 100644 index 0000000..a450d5e --- /dev/null +++ b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/MediatorTests.kt @@ -0,0 +1,26 @@ +package com.trendyol.kediatr.spring + +import com.trendyol.kediatr.Mediator +import com.trendyol.kediatr.framewokUseCases.* +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest( + classes = [ + KediatRAutoConfiguration::class, + TestCommandHandler::class, + ExceptionPipelineBehavior::class, + LoggingPipelineBehavior::class, + TestQueryHandler::class, + TestNotificationHandler::class, + TestBrokenCommandHandler::class, + TestPipelineCommandHandler::class, + TestCommandWithResultCommandHandler::class + ] +) +class MediatorTests : MediatorUseCases() { + @Autowired + lateinit var mediator: Mediator + + override fun provideMediator(): Mediator = mediator +} diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/NotificationHandlerTest.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/NotificationHandlerTest.kt deleted file mode 100644 index 89ce3d9..0000000 --- a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/NotificationHandlerTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Notification -import com.trendyol.kediatr.NotificationHandler -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertTrue - -var notificationTestCounter = 0 -var asyncNotificationTestCounter = 0 - -@SpringBootTest( - classes = [ - KediatRAutoConfiguration::class, - MyFirstNotificationHandler::class - ] -) -class NotificationHandlerTest { - init { - notificationTestCounter = 0 - asyncNotificationTestCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async notificationHandler should be fired`() = - runBlocking { - mediator.publish(MyNotification()) - - assertTrue { - asyncNotificationTestCounter == 1 - } - } -} - -class MyNotification : Notification - -class MyFirstNotificationHandler : NotificationHandler { - override suspend fun handle(notification: MyNotification) { - delay(500) - asyncNotificationTestCounter++ - } -} diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/PipelineBehaviorTest.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/PipelineBehaviorTest.kt deleted file mode 100644 index ee3984a..0000000 --- a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/PipelineBehaviorTest.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.PipelineBehavior -import com.trendyol.kediatr.RequestHandlerDelegate -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertTrue - -var exceptionPipelineBehaviorHandleCounter = 0 -var exceptionPipelineBehaviorHandleCatchCounter = 0 -var loggingPipelineBehaviorHandleBeforeNextCounter = 0 -var loggingPipelineBehaviorHandleAfterNextCounter = 0 - -@SpringBootTest( - classes = [KediatRAutoConfiguration::class, MyCommandHandler::class, ExceptionPipelineBehavior::class, LoggingPipelineBehavior::class] -) -class PipelineBehaviorTest { - init { - exceptionPipelineBehaviorHandleCounter = 0 - exceptionPipelineBehaviorHandleCatchCounter = 0 - loggingPipelineBehaviorHandleBeforeNextCounter = 0 - loggingPipelineBehaviorHandleAfterNextCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `should process command with async pipeline`() { - runBlocking { - mediator.send(MyCommand()) - } - - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 1 } - } - - @Test - fun `should process exception in async handler`() { - val act = suspend { mediator.send(MyBrokenCommand()) } - - assertThrows { runBlocking { act() } } - - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 0 } - } -} - -class MyBrokenCommand : Command - -class MyBrokenHandler : CommandHandler { - override suspend fun handle(command: MyBrokenCommand) { - delay(500) - throw Exception() - } -} - -private class ExceptionPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - try { - exceptionPipelineBehaviorHandleCounter++ - return next(request) - } catch (ex: Exception) { - exceptionPipelineBehaviorHandleCatchCounter++ - throw ex - } - } -} - -private class LoggingPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - loggingPipelineBehaviorHandleBeforeNextCounter++ - val result = next(request) - loggingPipelineBehaviorHandleAfterNextCounter++ - return result - } -} diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/QueryHandlerTest.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/QueryHandlerTest.kt deleted file mode 100644 index 21c5531..0000000 --- a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/QueryHandlerTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Query -import com.trendyol.kediatr.QueryHandler -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -@SpringBootTest(classes = [KediatRAutoConfiguration::class, TestQueryHandler::class]) -class QueryHandlerTest { - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async queryHandler should retrieve result`() = - runBlocking { - val result = mediator.send(TestQuery(1)) - - assertTrue { - result == "hello 1" - } - } - - @Test - fun `should throw exception if given async query does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistQuery()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.spring.NonExistQuery", exception.message) - } -} - -class NonExistQuery : Query - -class TestQuery(val id: Int) : Query - -class TestQueryHandler : QueryHandler { - override suspend fun handle(query: TestQuery): String { - return "hello " + query.id - } -} diff --git a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/SpringContextTest.kt b/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/SpringContextTest.kt deleted file mode 100644 index 771ba70..0000000 --- a/projects/kediatr-spring-boot-2x-starter/src/test/kotlin/com/trendyol/kediatr/spring/SpringContextTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.MediatorImpl -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertNotNull - -@SpringBootTest(classes = [KediatRAutoConfiguration::class]) -class SpringContextTest { - @Autowired - lateinit var mediator: Mediator - - @Test - fun contextLoads() { - assertNotNull(mediator) - assert(mediator is MediatorImpl) - } -} diff --git a/projects/kediatr-spring-boot-3x-starter/build.gradle.kts b/projects/kediatr-spring-boot-3x-starter/build.gradle.kts index a7026fe..160c383 100644 --- a/projects/kediatr-spring-boot-3x-starter/build.gradle.kts +++ b/projects/kediatr-spring-boot-3x-starter/build.gradle.kts @@ -1,11 +1,11 @@ val springVersion = "3.3.4" dependencies { - api(project(":projects:kediatr-core")) - implementation("org.springframework.boot:spring-boot-starter:$springVersion") - implementation("org.springframework.boot:spring-boot-autoconfigure:$springVersion") + api(project(":projects:kediatr-core")) + implementation("org.springframework.boot:spring-boot-starter:$springVersion") + implementation("org.springframework.boot:spring-boot-autoconfigure:$springVersion") } dependencies { - testImplementation(kotlin("test")) - testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") + testImplementation(testFixtures(projects.projects.kediatrCore)) + testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") } diff --git a/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt b/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt index 960e9d9..9391dbb 100644 --- a/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt +++ b/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRAutoConfiguration.kt @@ -1,20 +1,19 @@ package com.trendyol.kediatr.spring -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.MediatorBuilder +import com.trendyol.kediatr.* import org.springframework.boot.autoconfigure.AutoConfiguration import org.springframework.context.ApplicationContext import org.springframework.context.annotation.Bean @AutoConfiguration open class KediatRAutoConfiguration { - @Bean - open fun kediatRSpringBeanProvider(applicationContext: ApplicationContext): KediatRSpringBeanProvider { - return KediatRSpringBeanProvider(applicationContext) - } + @Bean + open fun kediatRSpringBeanProvider( + applicationContext: ApplicationContext + ): KediatRSpringBeanProvider = KediatRSpringBeanProvider(applicationContext) - @Bean - open fun mediator(kediatRSpringBeanProvider: KediatRSpringBeanProvider): Mediator { - return MediatorBuilder(kediatRSpringBeanProvider).build() - } + @Bean + open fun mediator( + kediatRSpringBeanProvider: KediatRSpringBeanProvider + ): Mediator = MediatorBuilder(kediatRSpringBeanProvider).build() } diff --git a/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt b/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt index 9c8619b..e7068e7 100644 --- a/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt +++ b/projects/kediatr-spring-boot-3x-starter/src/main/kotlin/com/trendyol/kediatr/spring/KediatRSpringBeanProvider.kt @@ -6,14 +6,13 @@ import com.trendyol.kediatr.DependencyProvider import org.springframework.context.ApplicationContext class KediatRSpringBeanProvider( - private val applicationContext: ApplicationContext + private val applicationContext: ApplicationContext ) : DependencyProvider { - override fun getSingleInstanceOf(clazz: Class): T { - return applicationContext.getBean(clazz) - } + override fun getSingleInstanceOf(clazz: Class): T = applicationContext.getBean(clazz) - override fun getSubTypesOf(clazz: Class): Collection> { - return applicationContext.getBeanNamesForType(clazz) - .map { applicationContext.getType(it) as Class } - } + override fun getSubTypesOf( + clazz: Class + ): Collection> = applicationContext + .getBeanNamesForType(clazz) + .map { applicationContext.getType(it) as Class } } diff --git a/projects/kediatr-spring-boot-3x-starter/src/main/resources/META-INF/spring.factories b/projects/kediatr-spring-boot-3x-starter/src/main/resources/META-INF/spring.factories index b9df51a..e2e2abe 100644 --- a/projects/kediatr-spring-boot-3x-starter/src/main/resources/META-INF/spring.factories +++ b/projects/kediatr-spring-boot-3x-starter/src/main/resources/META-INF/spring.factories @@ -1,3 +1,3 @@ # Register EnableAutoConfiguration -org.springframework.boot.autoconfigure.EnableAutoConfiguration = \ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.trendyol.kediatr.spring.KediatRAutoConfiguration diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandHandlerTest.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandHandlerTest.kt deleted file mode 100644 index 97c1966..0000000 --- a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandHandlerTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -private var springTestCounter = 0 -private var springAsyncTestCounter = 0 - -@SpringBootTest(classes = [KediatRAutoConfiguration::class, MyCommandHandler::class]) -class CommandHandlerTest { - init { - springTestCounter = 0 - springAsyncTestCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommand()) - - assertTrue { - springAsyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommand()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.spring.NonExistCommand", exception.message) - } -} - -class NonExistCommand : Command - -class MyCommand : Command - -class MyCommandHandler : CommandHandler { - override suspend fun handle(command: MyCommand) { - delay(500) - springAsyncTestCounter++ - } -} diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandWithResultHandlerTest.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandWithResultHandlerTest.kt deleted file mode 100644 index 940bb10..0000000 --- a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/CommandWithResultHandlerTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.* -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.* - -private var springTestCounter = 0 -private var springAsyncTestCounter = 0 - -@SpringBootTest(classes = [KediatRAutoConfiguration::class, MyAsyncCommandRHandler::class]) -class CommandWithResultHandlerTest { - init { - springTestCounter = 0 - springAsyncTestCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async commandHandler should be fired`() = - runBlocking { - mediator.send(MyCommandR()) - - assertTrue { - springAsyncTestCounter == 1 - } - } - - @Test - fun `should throw exception if given async command does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistCommandR()) - } - } - - assertNotNull(exception) - assertEquals(exception.message, "handler could not be found for com.trendyol.kediatr.spring.NonExistCommandR") - } -} - -class Result - -class NonExistCommandR : CommandWithResult - -class MyCommandR : CommandWithResult - -class MyAsyncCommandRHandler : CommandWithResultHandler { - override suspend fun handle(command: MyCommandR): Result { - delay(500) - springAsyncTestCounter++ - - return Result() - } -} diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/MediatorTests.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/MediatorTests.kt new file mode 100644 index 0000000..a450d5e --- /dev/null +++ b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/MediatorTests.kt @@ -0,0 +1,26 @@ +package com.trendyol.kediatr.spring + +import com.trendyol.kediatr.Mediator +import com.trendyol.kediatr.framewokUseCases.* +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest( + classes = [ + KediatRAutoConfiguration::class, + TestCommandHandler::class, + ExceptionPipelineBehavior::class, + LoggingPipelineBehavior::class, + TestQueryHandler::class, + TestNotificationHandler::class, + TestBrokenCommandHandler::class, + TestPipelineCommandHandler::class, + TestCommandWithResultCommandHandler::class + ] +) +class MediatorTests : MediatorUseCases() { + @Autowired + lateinit var mediator: Mediator + + override fun provideMediator(): Mediator = mediator +} diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/NotificationHandlerTest.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/NotificationHandlerTest.kt deleted file mode 100644 index 89ce3d9..0000000 --- a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/NotificationHandlerTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Notification -import com.trendyol.kediatr.NotificationHandler -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertTrue - -var notificationTestCounter = 0 -var asyncNotificationTestCounter = 0 - -@SpringBootTest( - classes = [ - KediatRAutoConfiguration::class, - MyFirstNotificationHandler::class - ] -) -class NotificationHandlerTest { - init { - notificationTestCounter = 0 - asyncNotificationTestCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async notificationHandler should be fired`() = - runBlocking { - mediator.publish(MyNotification()) - - assertTrue { - asyncNotificationTestCounter == 1 - } - } -} - -class MyNotification : Notification - -class MyFirstNotificationHandler : NotificationHandler { - override suspend fun handle(notification: MyNotification) { - delay(500) - asyncNotificationTestCounter++ - } -} diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/PipelineBehaviorTest.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/PipelineBehaviorTest.kt deleted file mode 100644 index ee3984a..0000000 --- a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/PipelineBehaviorTest.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Command -import com.trendyol.kediatr.CommandHandler -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.PipelineBehavior -import com.trendyol.kediatr.RequestHandlerDelegate -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertTrue - -var exceptionPipelineBehaviorHandleCounter = 0 -var exceptionPipelineBehaviorHandleCatchCounter = 0 -var loggingPipelineBehaviorHandleBeforeNextCounter = 0 -var loggingPipelineBehaviorHandleAfterNextCounter = 0 - -@SpringBootTest( - classes = [KediatRAutoConfiguration::class, MyCommandHandler::class, ExceptionPipelineBehavior::class, LoggingPipelineBehavior::class] -) -class PipelineBehaviorTest { - init { - exceptionPipelineBehaviorHandleCounter = 0 - exceptionPipelineBehaviorHandleCatchCounter = 0 - loggingPipelineBehaviorHandleBeforeNextCounter = 0 - loggingPipelineBehaviorHandleAfterNextCounter = 0 - } - - @Autowired - lateinit var mediator: Mediator - - @Test - fun `should process command with async pipeline`() { - runBlocking { - mediator.send(MyCommand()) - } - - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 0 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 1 } - } - - @Test - fun `should process exception in async handler`() { - val act = suspend { mediator.send(MyBrokenCommand()) } - - assertThrows { runBlocking { act() } } - - assertTrue { exceptionPipelineBehaviorHandleCatchCounter == 1 } - assertTrue { exceptionPipelineBehaviorHandleCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleBeforeNextCounter == 1 } - assertTrue { loggingPipelineBehaviorHandleAfterNextCounter == 0 } - } -} - -class MyBrokenCommand : Command - -class MyBrokenHandler : CommandHandler { - override suspend fun handle(command: MyBrokenCommand) { - delay(500) - throw Exception() - } -} - -private class ExceptionPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - try { - exceptionPipelineBehaviorHandleCounter++ - return next(request) - } catch (ex: Exception) { - exceptionPipelineBehaviorHandleCatchCounter++ - throw ex - } - } -} - -private class LoggingPipelineBehavior : PipelineBehavior { - override suspend fun handle( - request: TRequest, - next: RequestHandlerDelegate - ): TResponse { - loggingPipelineBehaviorHandleBeforeNextCounter++ - val result = next(request) - loggingPipelineBehaviorHandleAfterNextCounter++ - return result - } -} diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/QueryHandlerTest.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/QueryHandlerTest.kt deleted file mode 100644 index 21c5531..0000000 --- a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/QueryHandlerTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.HandlerNotFoundException -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.Query -import com.trendyol.kediatr.QueryHandler -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotNull -import kotlin.test.assertTrue - -@SpringBootTest(classes = [KediatRAutoConfiguration::class, TestQueryHandler::class]) -class QueryHandlerTest { - @Autowired - lateinit var mediator: Mediator - - @Test - fun `async queryHandler should retrieve result`() = - runBlocking { - val result = mediator.send(TestQuery(1)) - - assertTrue { - result == "hello 1" - } - } - - @Test - fun `should throw exception if given async query does not have handler bean`() { - val exception = - assertFailsWith(HandlerNotFoundException::class) { - runBlocking { - mediator.send(NonExistQuery()) - } - } - - assertNotNull(exception) - assertEquals("handler could not be found for com.trendyol.kediatr.spring.NonExistQuery", exception.message) - } -} - -class NonExistQuery : Query - -class TestQuery(val id: Int) : Query - -class TestQueryHandler : QueryHandler { - override suspend fun handle(query: TestQuery): String { - return "hello " + query.id - } -} diff --git a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/SpringContextTest.kt b/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/SpringContextTest.kt deleted file mode 100644 index 771ba70..0000000 --- a/projects/kediatr-spring-boot-3x-starter/src/test/kotlin/com/trendyol/kediatr/spring/SpringContextTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.trendyol.kediatr.spring - -import com.trendyol.kediatr.Mediator -import com.trendyol.kediatr.MediatorImpl -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import kotlin.test.assertNotNull - -@SpringBootTest(classes = [KediatRAutoConfiguration::class]) -class SpringContextTest { - @Autowired - lateinit var mediator: Mediator - - @Test - fun contextLoads() { - assertNotNull(mediator) - assert(mediator is MediatorImpl) - } -} diff --git a/renovate.json b/renovate.json index 79b5af2..00b1359 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,8 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "baseBranches": ["main"], + "baseBranches": [ + "main" + ], "extends": [ "config:base" ], diff --git a/settings.gradle.kts b/settings.gradle.kts index 40b3265..f13cf0f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,23 +1,40 @@ @file:Suppress("UnstableApiUsage") +import dev.aga.gradle.versioncatalogs.Generator.generate +import dev.aga.gradle.versioncatalogs.GeneratorConfig + rootProject.name = "kediatR" include( - "projects:kediatr-core", - "projects:kediatr-koin-starter", - "projects:kediatr-quarkus-starter", - "projects:kediatr-spring-boot-2x-starter", - "projects:kediatr-spring-boot-3x-starter" + "projects:kediatr-core", + "projects:kediatr-koin-starter", + "projects:kediatr-quarkus-starter", + "projects:kediatr-spring-boot-2x-starter", + "projects:kediatr-spring-boot-3x-starter", ) +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +plugins { + id("dev.aga.gradle.version-catalog-generator") version ("2.1.1") +} + dependencyResolutionManagement { - repositories { - mavenCentral() - maven { - url = uri("https://oss.sonatype.org/content/repositories/snapshots") - } - - maven { - url = uri("https://repo.maven.apache.org/maven2/") - } + repositories { + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") } + + maven { + url = uri("https://repo.maven.apache.org/maven2/") + } + } + + versionCatalogs { + generate("junitLibs") { + from(toml("junitBom")) + aliasPrefixGenerator = GeneratorConfig.NO_PREFIX + } + } } +