From 85ffaf1e111327f4eb2ae55de36e9acd21f4b824 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakovich Date: Fri, 29 Nov 2024 18:56:22 +0300 Subject: [PATCH] fix: TMS-29853: fix stringSpec, shouldSpec, refactor, tests --- testit-adapter-kotest/build.gradle.kts | 3 +- .../kotlin/ru/testit/listener/Consumers.kt | 10 +- .../ru/testit/listener/TestItReporter.kt | 3 +- .../kotlin/ru/testit/listener/TestItWriter.kt | 53 +-- .../ru/testit/services/FixtureService.kt | 11 +- .../kotlin/ru/testit/services/StepService.kt | 9 +- .../kotlin/ru/testit/services/TestService.kt | 43 ++- .../main/kotlin/ru/testit/utils/extensions.kt | 341 +++++++++--------- .../ru/testit/services/FixtureServiceTests.kt | 201 +++++++++++ .../ru/testit/services/StepServiceTests.kt | 150 ++++++++ .../ru/testit/services/TestServiceTests.kt | 192 ++++++++++ .../src/test/resources/testit.properties | 2 +- testit-kotlin-commons/build.gradle.kts | 4 +- .../kotlin/ru/testit/clients/ApiClient.kt | 4 +- .../kotlin/ru/testit/clients/TmsApiClient.kt | 4 +- .../ru/testit/listener/AdapterListener.kt | 4 +- .../ru/testit/listener/ListenerManager.kt | 6 +- .../{TestResult.kt => TestResultCommon.kt} | 5 +- .../ru/testit/services/AdapterManager.kt | 27 +- .../ru/testit/services/ResultStorage.kt | 6 +- .../kotlin/ru/testit/writers/Converter.kt | 28 +- .../kotlin/ru/testit/writers/HttpWriter.kt | 28 +- .../main/kotlin/ru/testit/writers/Writer.kt | 4 +- .../src/test/kotlin/ru/testit/Helper.kt | 6 +- 24 files changed, 832 insertions(+), 312 deletions(-) create mode 100644 testit-adapter-kotest/src/test/kotlin/ru/testit/services/FixtureServiceTests.kt create mode 100644 testit-adapter-kotest/src/test/kotlin/ru/testit/services/StepServiceTests.kt create mode 100644 testit-adapter-kotest/src/test/kotlin/ru/testit/services/TestServiceTests.kt rename testit-kotlin-commons/src/main/kotlin/ru/testit/models/{TestResult.kt => TestResultCommon.kt} (96%) diff --git a/testit-adapter-kotest/build.gradle.kts b/testit-adapter-kotest/build.gradle.kts index e6bb389..598db3d 100644 --- a/testit-adapter-kotest/build.gradle.kts +++ b/testit-adapter-kotest/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "ru.testit" -version = "0.1.1" +version = "0.2.0" java { withJavadocJar() @@ -32,6 +32,7 @@ dependencies { implementation("ch.qos.logback:logback-core:1.5.8") implementation("io.kotest:kotest-framework-datatest:5.9.1") + testImplementation("io.mockk:mockk:1.13.12") testImplementation(libs.kotest.assertions.core) testImplementation(libs.kotest.runner.junit5) testImplementation(libs.jackson.module.kotlin) diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/Consumers.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/Consumers.kt index d061f9e..de38715 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/Consumers.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/Consumers.kt @@ -3,7 +3,7 @@ package ru.testit.listener import ru.testit.models.ItemStatus import ru.testit.models.StepResult import ru.testit.models.TestItContext -import ru.testit.models.TestResult +import ru.testit.models.TestResultCommon import java.util.Objects.nonNull import java.util.function.Consumer @@ -12,8 +12,8 @@ object Consumers { /** * Sets the item status and optional throwable for a test result. */ - fun setStatus(status: ItemStatus, throwable: Throwable?): Consumer { - return Consumer { result: TestResult -> + fun setStatus(status: ItemStatus, throwable: Throwable?): Consumer { + return Consumer { result: TestResultCommon -> result.itemStatus = status if (nonNull(throwable)) { result.throwable = throwable @@ -36,8 +36,8 @@ object Consumers { /** * Sets the context properties for a test result. */ - fun setContext(context: TestItContext): Consumer { - return Consumer { result: TestResult -> + fun setContext(context: TestItContext): Consumer { + return Consumer { result: TestResultCommon -> result.externalId = context.externalId ?: result.externalId result.description = context.description ?: result.description result.workItemIds = context.workItemIds ?: result.workItemIds diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItReporter.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItReporter.kt index 7294a85..77365e6 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItReporter.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItReporter.kt @@ -8,12 +8,11 @@ import io.kotest.core.test.TestResult import kotlin.reflect.KClass class TestItReporter( - private val isStepContainers: Boolean = false, ) : BeforeTestListener, AfterTestListener, InstantiationErrorListener, ProjectListener, BeforeSpecListener, AfterSpecListener, BeforeInvocationListener, AfterInvocationListener, AfterEachListener, FinalizeSpecListener, AfterContainerListener { - val writer = TestItWriter(isStepContainers) + val writer = TestItWriter() // beforeAll analogue override suspend fun beforeSpec(spec: Spec) { diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItWriter.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItWriter.kt index 11ae11e..676f0e8 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItWriter.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/listener/TestItWriter.kt @@ -14,14 +14,12 @@ import java.lang.reflect.InvocationTargetException import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KClass -import ru.testit.models.TestResult as TestItTestResult +import ru.testit.models.TestResultCommon as TestItTestResult -class TestItWriter ( - private var isStepContainers: Boolean = false -) { +class TestItWriter () { private val LOGGER = LoggerFactory.getLogger(javaClass) private val debug = AdapterUtils.debug(LOGGER) @@ -41,7 +39,8 @@ class TestItWriter ( * Checks `testCase` to be valid test or [TestItReporter.isStepContainers] to be true */ private val isTestOrContainersEnabled: (testCase: TestCase) -> Boolean = { - testCase: TestCase -> testCase.type == TestType.Test || isStepContainers } + testCase: TestCase -> testCase.type == TestType.Container + || testCase.type == TestType.Test || testCase.type == TestType.Dynamic } private val executableTestService = ExecutableTestService( executableTest = ThreadLocal.withInitial { ExecutableTest() } @@ -49,29 +48,26 @@ class TestItWriter ( private val stepService = StepService( adapterManager = adapterManager, uuids = uuids, - executableTestService = executableTestService, - isStepContainers = isStepContainers + executableTestService = executableTestService ) private val testService = TestService( adapterManager = adapterManager, uuids = uuids, - executableTestService = executableTestService, - isStepContainers = isStepContainers + executableTestService = executableTestService ) private val fixtureService = FixtureService( adapterManager = adapterManager, executableTestService = executableTestService, - testService = testService, - isStepContainers = isStepContainers + testService = testService ) /** * Used for `instantiationError` error handling */ - suspend fun onInstantiationError(kclass: KClass<*>, t: Throwable) { + fun onInstantiationError(kclass: KClass<*>, t: Throwable) { var uuid = UUID.randomUUID() var result = TestItTestResult( @@ -90,7 +86,7 @@ class TestItWriter ( /** * @see runContainers */ - suspend fun onBeforeAll(spec: Spec) { + fun onBeforeAll(spec: Spec) { var rootTestName = spec.rootTests()[0].name.testName debug("Before all: {}",rootTestName) runContainers(rootTestName) @@ -99,7 +95,7 @@ class TestItWriter ( /** * @see stopContainers */ - suspend fun onAfterAll(spec: Spec) { + fun onAfterAll(spec: Spec) { var rootTestName = spec.rootTests()[0].name.testName debug("After all: {}", rootTestName) stopContainers(rootTestName) @@ -117,7 +113,7 @@ class TestItWriter ( * @see FixtureService.registerAfterTestFixture */ fun registerBeforeAfterExtensions(testCase: TestCase) { - if (testCase.isStep(isStepContainers)) { + if (testCase.isStep()) { return } executableTestService.refreshUuid() @@ -139,7 +135,7 @@ class TestItWriter ( * @see FixtureService.onBeforeTestOk */ fun finishBeforeTestIfExists(testCase: TestCase) { - if (testCase.isStep(isStepContainers)) { + if (testCase.isStep()) { return } // write beforeTest successful results @@ -156,7 +152,7 @@ class TestItWriter ( * @see FixtureService.updateAfterTestTime */ fun onAfterTestInvocation(testCase: TestCase) { - if (testCase.isStep(isStepContainers)) { + if (testCase.isStep()) { return } if (AdapterUtils.isAfterTestRegistered(testCase)) { @@ -171,18 +167,13 @@ class TestItWriter ( * @see StepService.onStepStart * @see onTestStart */ - suspend fun onBeforeTestInvocation(testCase: TestCase) { + fun onBeforeTestInvocation(testCase: TestCase) { if (!isTestOrContainersEnabled(testCase)) { return } var isContainer = testCase.type.name == "Container" - var isStep = testCase.isStep(isStepContainers); - + var isStep = testCase.isStep(); if (isContainer) { - if (!isStepContainers) { - debug("we are inside default container", "") - return - } debug("we are in step container", "") } if (isStep) { @@ -204,16 +195,12 @@ class TestItWriter ( return } var isContainer = testCase.type.name == "Container" - var isStepContainer = isContainer && isStepContainers - if (isContainer && !isStepContainers) { - return - } - if (testCase.isStep(isStepContainers)) { + if (testCase.isStep()) { return stepService.stopStepWithResult(testCase, result) } // no mark in describe - if (isContainer && !testCase.isStepContainer(isStepContainers)) { + if (isContainer && !testCase.isStepContainer()) { return } testService.stopTestWithResult(testCase, result) @@ -236,7 +223,7 @@ class TestItWriter ( * @see TestService.onTestStart * @see AdapterManager.updateClassContainer */ - private suspend fun onTestStart(testCase: TestCase) { + private fun onTestStart(testCase: TestCase) { debug("Intercept test: {}", testCase.name) val testName = testCase.spec.rootTests()[0].name.testName // val uuid = getExecTestWithUuid() @@ -254,7 +241,7 @@ class TestItWriter ( * Create new test run. * Init main and class containers using [AdapterManager] api */ - private suspend fun runContainers(rootTestName: String) { + private fun runContainers(rootTestName: String) { adapterManager.startTests() lastMainContainerId = UUID.randomUUID().toString() val mainContainer = MainContainer( @@ -268,7 +255,7 @@ class TestItWriter ( adapterManager.startClassContainer(lastMainContainerId!!, classContainer) } - private suspend fun stopContainers(rootTestName: String) { + private fun stopContainers(rootTestName: String) { adapterManager.stopClassContainer(Utils.getHash(rootTestName)) adapterManager.stopMainContainer(lastMainContainerId!!) } diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/services/FixtureService.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/services/FixtureService.kt index e9aa5c7..09d9d19 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/services/FixtureService.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/services/FixtureService.kt @@ -3,6 +3,7 @@ package ru.testit.services import io.kotest.core.test.TestCase import io.kotest.core.test.TestResult import io.kotest.engine.extensions.ExtensionException +import org.jetbrains.annotations.VisibleForTesting import org.slf4j.LoggerFactory import ru.testit.models.ClassContainer import ru.testit.models.FixtureResult @@ -10,12 +11,12 @@ import ru.testit.models.ItemStage import ru.testit.models.ItemStatus import ru.testit.utils.* import java.util.* +import kotlin.reflect.KVisibility class FixtureService ( private val adapterManager: AdapterManager, private val executableTestService: ExecutableTestService, - private val testService: TestService, - private val isStepContainers: Boolean + private val testService: TestService ) { var beforeFixtureUUID: String? = null var afterFixtureUUID: String? = null @@ -88,7 +89,7 @@ class FixtureService ( */ suspend fun handleFixturesFails(testCase: TestCase, result: TestResult, beforeTestStart: Long, afterTestStart: Long): Boolean { - if (testCase.isStep(isStepContainers)) { + if (testCase.isStep()) { return false } var isAfterTestFailed = false @@ -125,8 +126,8 @@ class FixtureService ( return false } - - private fun onAfterTestFailed(testCase: TestCase, start: Long, stop: Long) { + @VisibleForTesting + internal fun onAfterTestFailed(testCase: TestCase, start: Long, stop: Long) { var exception = testCase.afterTestThrowable() var tearDownName = testCase.teardownName() ?: "TearDown" debug("After test finished with error: {}", exception.toString()) diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/services/StepService.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/services/StepService.kt index 3f1e5b5..144e298 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/services/StepService.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/services/StepService.kt @@ -3,6 +3,7 @@ package ru.testit.services import io.kotest.common.TestPath import io.kotest.core.test.TestCase import io.kotest.core.test.TestResult +import org.jetbrains.annotations.VisibleForTesting import org.slf4j.LoggerFactory import ru.testit.listener.Consumers import ru.testit.models.ItemStatus @@ -13,8 +14,7 @@ import java.util.concurrent.ConcurrentHashMap class StepService ( private val adapterManager: AdapterManager, private val uuids: ConcurrentHashMap, - private val executableTestService: ExecutableTestService, - private val isStepContainers: Boolean + private val executableTestService: ExecutableTestService ) { private val LOGGER = LoggerFactory.getLogger(javaClass) @@ -61,7 +61,7 @@ class StepService ( } if (result is TestResult.Ignored) { debug("Step ignored: {}", step.name) - return updateStepAndStop(stepUuid, ItemStatus.SKIPPED, result.errorOrNull!!) + return updateStepAndStop(stepUuid, ItemStatus.SKIPPED, result.errorOrNull) } if (result is TestResult.Failure || result is TestResult.Error) { debug("Step failed: {}", step.name) @@ -80,7 +80,8 @@ class StepService ( executableTestService.setAsFailedBy(cause) } - private fun getStepUuid(step: TestCase): String { + @VisibleForTesting + internal fun getStepUuid(step: TestCase): String { val stepPath: String = step.parent!!.name.testName + step.name.testName return Utils.getHash(stepPath) diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/services/TestService.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/services/TestService.kt index b3ec3ef..fd40b4b 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/services/TestService.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/services/TestService.kt @@ -7,17 +7,19 @@ import io.kotest.core.test.TestResult import io.kotest.engine.test.names.DefaultDisplayNameFormatter import io.kotest.engine.test.names.FallbackDisplayNameFormatter import io.kotest.engine.test.names.formatTestPath +import org.jetbrains.annotations.VisibleForTesting import org.slf4j.LoggerFactory import ru.testit.listener.Consumers import ru.testit.models.ItemStatus import ru.testit.utils.AdapterUtils import ru.testit.utils.getContext +import ru.testit.utils.isStepContainer import java.util.concurrent.ConcurrentHashMap class TestService ( private val adapterManager: AdapterManager, - private val uuids: ConcurrentHashMap, - private val isStepContainers: Boolean, + @VisibleForTesting + internal val uuids: ConcurrentHashMap, private val executableTestService: ExecutableTestService ) { private val LOGGER = LoggerFactory.getLogger(javaClass) @@ -28,15 +30,17 @@ class TestService ( private val debug = AdapterUtils.debug(LOGGER) - - suspend fun onTestStart(testCase: TestCase, uuid: String) { - val fullName: String = formatter.formatTestPath(testCase, " / ") - - val result = ru.testit.models.TestResult( + fun onTestStart(testCase: TestCase, uuid: String) { + var fullName: String + if (testCase.parent?.name?.testName == "") fullName = formatter.format(testCase) + else fullName = formatter.formatTestPath(testCase, " / ") + var spaceName : String = testCase.spec.javaClass.packageName + var className: String = testCase.spec.javaClass.simpleName + val result = ru.testit.models.TestResultCommon( uuid = uuid, - className = testCase.spec::class.simpleName!!, + className = className, name = testCase.name.testName, - spaceName = testCase.spec::class.java.`package`.name, + spaceName = spaceName, externalId = Utils.genExternalID(fullName), labels = Utils.defaultLabels(), linkItems = Utils.defaultLinks() @@ -52,12 +56,13 @@ class TestService ( * @see onTestIgnored * @see onTestSuccessful */ - suspend fun stopTestWithResult(testCase: TestCase, result: TestResult) { + fun stopTestWithResult(testCase: TestCase, result: TestResult) { var isContainer = testCase.type.name == "Container" - var isStepContainer = isContainer && isStepContainers + var isStepContainer = isContainer && testCase.isStepContainer() var executableTest = executableTestService.getTest() - - val uuid = uuids[testCase.descriptor.path()] ?: "Unknown test ${testCase.descriptor}" + var path = testCase.descriptor.path() + if (path.value == "") path = TestPath(testCase.descriptor.id.value) + val uuid = uuids[path] ?: "Unknown test ${testCase.descriptor}" val context = testCase.getContext() if (context != null) { adapterManager.updateTestCase(uuid, Consumers.setContext(context)) @@ -70,27 +75,25 @@ class TestService ( if (result is TestResult.Success) return onTestSuccessful(testCase) if (result is TestResult.Ignored) - return onTestIgnored(testCase, result.errorOrNull!!) + return onTestIgnored(testCase, result.errorOrNull) if (result is TestResult.Failure || result is TestResult.Error) return onTestFailed(testCase, result.errorOrNull!!) } - - - private suspend fun onTestSuccessful(testCase: TestCase) { + private fun onTestSuccessful(testCase: TestCase) { debug("Test successful: {}", testCase.name) executableTestService.setAfterStatus() stopTestCase(executableTestService.getUuid(), null, ItemStatus.PASSED) } - private suspend fun onTestIgnored(testCase: TestCase, cause: Throwable) { + private fun onTestIgnored(testCase: TestCase, cause: Throwable?) { debug("Test ignored: {}", testCase.name) executableTestService.onTestIgnoredRefreshIfNeed() stopTestCase(executableTestService.getUuid(), cause, ItemStatus.SKIPPED); } - private suspend fun onTestFailed(testCase: TestCase, cause: Throwable) { + private fun onTestFailed(testCase: TestCase, cause: Throwable) { debug("Test failed: {}", testCase.name) if (!executableTestService.isTestStatus()) { return; @@ -99,7 +102,7 @@ class TestService ( stopTestCase(executableTestService.getUuid(), cause, ItemStatus.FAILED); } - suspend fun stopTestCase(uuid: String, throwable: Throwable?, status: ItemStatus) { + fun stopTestCase(uuid: String, throwable: Throwable?, status: ItemStatus) { adapterManager.updateTestCase(uuid, Consumers.setStatus(status, throwable)) adapterManager.stopTestCase(uuid) } diff --git a/testit-adapter-kotest/src/main/kotlin/ru/testit/utils/extensions.kt b/testit-adapter-kotest/src/main/kotlin/ru/testit/utils/extensions.kt index 96e057b..40a4b9b 100644 --- a/testit-adapter-kotest/src/main/kotlin/ru/testit/utils/extensions.kt +++ b/testit-adapter-kotest/src/main/kotlin/ru/testit/utils/extensions.kt @@ -1,176 +1,165 @@ -package ru.testit.utils - -import io.kotest.core.Tuple2 -import io.kotest.core.test.TestCase -import io.kotest.core.test.TestResult -import ru.testit.listener.TestItReporter -import ru.testit.listener.TestItWriter -import ru.testit.models.TestItContext -import ru.testit.models.TestItParams - - -fun TestCase.setAfterTestException(cause: Throwable?) { - setParameters( - TestItParams( - afterTestThrowable = cause - ) - ) -} - -/** - * Set [name] as a `name` property for Setup object in Test IT. - */ -fun TestCase.setSetupName(name: String) { - setParameters( - TestItParams( - setupName = name - ) - ) -} - -/** - * Set [name] as a `name` property for TearDown object in Test IT. - */ -fun TestCase.setTeardownName(name: String) { - setParameters( - TestItParams( - teardownName = name - ) - ) -} - -fun TestCase.setupName(): String? { - return getParams()?.setupName -} - -fun TestCase.teardownName(): String? { - return getParams()?.teardownName -} - -/** - * Set [name] as a `name` property for TearDown object in Test IT. - * - * Executes the provided [body] function and sets any exceptions thrown to the - * `afterTestException` property for the testCase object. If an exception is thrown, it will be rethrown. - * @param name the name of the TearDown object - * @param body the function to execute - */ -fun Tuple2.testItAfterTest(name: String, body: () -> Unit) { - this.a.setTeardownName(name) - testItAfterTest(body) -} - - -/** - * Executes the provided [body] function and sets any exceptions thrown to the - * `afterTestException` property for the testCase object. If an exception is thrown, it will be rethrown. - * @param body the function to execute - */ -fun Tuple2.testItAfterTest(body: () -> Unit) { - try { - body() - } - catch (e: Throwable ) { - this.a.setAfterTestException(e) - throw e - } -} - -fun TestCase.afterTestThrowable(): Throwable? { - return getParams()?.afterTestThrowable -} - -fun TestCase.setContext(value: TestItContext) { - val writer = getWriter() ?: return - val context = writer.context[this.name.toString()] - if (context != null) { - value.uuid = if (value.uuid != null) value.uuid else context.uuid - value.externalId = if (value.externalId != null) value.externalId else context.externalId - value.links = if (value.links != null) value.links else context.links - value.workItemIds = if (value.workItemIds != null) value.workItemIds else context.workItemIds - value.attachments = if (value.attachments != null) value.attachments else context.attachments - value.name = if (value.name != null) value.name else context.name - value.title = if (value.title != null) value.title else context.title - value.message = if (value.message != null) value.message else context.message - value.itemStatus = if (value.itemStatus != null) value.itemStatus else context.itemStatus - value.description = if (value.description != null) value.description else context.description - value.parameters = if (value.parameters != null) value.parameters else context.parameters - value.labels = if (value.labels != null) value.labels else context.labels - } - writer.context[this.name.toString()] = value -} - -fun TestCase.getContext(): TestItContext? { - val writer = getWriter() ?: return null - return writer.context[this.name.toString()] -} - -/** - * Set current test block as a step container. - * - * Requires [TestItReporter.isStepContainers] to be `true` to work as expected. - */ -fun TestCase.asStepContainer() { - setParameters( - TestItParams( - isStepContainer = true - )) -} - -/** - * check [TestItReporter.isStepContainers] and [TestItParams.isStepContainer] to be `true` - */ -fun TestCase.isStepContainer(isStepContainers: Boolean): Boolean { - return isStepContainers && this.isStepContainer() -} - -/** - * check [TestItReporter.isStepContainers] and parent's [TestItParams.isStepContainer] to be `true` - */ -fun TestCase.isStep(isStepContainers: Boolean): Boolean { - return isStepContainers && this.isStep() -} - - -private fun TestCase.getParams(): TestItParams? { - val writer = getWriter() ?: return null - return writer.params[this.name.toString()] -} - -private fun TestCase.setParameters(value: TestItParams) { - val writer = getWriter() ?: return - val params = writer.params[this.name.toString()] - if (params != null) { - value.isStepContainer = if (value.isStepContainer) value.isStepContainer else params.isStepContainer - value.afterTestThrowable = if (value.afterTestThrowable != null) value.afterTestThrowable else params.afterTestThrowable - value.setupName = if (value.setupName != null) value.setupName else params.setupName - value.teardownName = if (value.teardownName != null) value.teardownName else params.teardownName - } - writer.params[this.name.toString()] = value -} - -private fun TestCase.isStepContainer(): Boolean { - return getParams()?.isStepContainer ?: false -} - -/** - * @return `true` if `this.parent` set as Step Container using [TestCase.asStepContainer] - */ -private fun TestCase.isStep(): Boolean { - val writer = getWriter() ?: return false - val p = writer.params[this.parent?.name.toString()] ?: return false - return p.isStepContainer -} - -private fun TestCase.getWriter(): TestItWriter? { - val extensions = spec.registeredExtensions() - if (extensions.isEmpty()) { return null } - for (item in extensions) { - try { - return (item as TestItReporter).writer - } - catch (_: Exception) {} - } - return null -} - +package ru.testit.utils + +import io.kotest.core.Tuple2 +import io.kotest.core.test.TestCase +import io.kotest.core.test.TestResult +import ru.testit.listener.TestItReporter +import ru.testit.listener.TestItWriter +import ru.testit.models.TestItContext +import ru.testit.models.TestItParams + +fun TestCase.setAfterTestException(cause: Throwable?) { + setParameters( + TestItParams( + afterTestThrowable = cause + ) + ) +} + +/** + * Set [name] as a `name` property for Setup object in Test IT. + */ +fun TestCase.setSetupName(name: String) { + setParameters( + TestItParams( + setupName = name + ) + ) +} + +/** + * Set [name] as a `name` property for TearDown object in Test IT. + */ +fun TestCase.setTeardownName(name: String) { + setParameters( + TestItParams( + teardownName = name + ) + ) +} + + +fun TestCase.setupName(): String? { + return getParams()?.setupName +} + +fun TestCase.teardownName(): String? { + return getParams()?.teardownName +} + +/** + * Set [name] as a `name` property for TearDown object in Test IT. + * + * Executes the provided [body] function and sets any exceptions thrown to the + * `afterTestException` property for the testCase object. If an exception is thrown, it will be rethrown. + * @param name the name of the TearDown object + * @param body the function to execute + */ +fun Tuple2.testItAfterTest(name: String, body: () -> Unit) { + this.a.setTeardownName(name) + testItAfterTest(body) +} + + +/** + * Executes the provided [body] function and sets any exceptions thrown to the + * `afterTestException` property for the testCase object. If an exception is thrown, it will be rethrown. + * @param body the function to execute + */ +fun Tuple2.testItAfterTest(body: () -> Unit) { + try { + body() + } + catch (e: Throwable ) { + this.a.setAfterTestException(e) + throw e + } +} + +fun TestCase.afterTestThrowable(): Throwable? { + return getParams()?.afterTestThrowable +} + +fun TestCase.setContext(value: TestItContext) { + val writer = getWriter() ?: return + val context = writer.context[this.name.toString()] + if (context != null) { + value.uuid = if (value.uuid != null) value.uuid else context.uuid + value.externalId = if (value.externalId != null) value.externalId else context.externalId + value.links = if (value.links != null) value.links else context.links + value.workItemIds = if (value.workItemIds != null) value.workItemIds else context.workItemIds + value.attachments = if (value.attachments != null) value.attachments else context.attachments + value.name = if (value.name != null) value.name else context.name + value.title = if (value.title != null) value.title else context.title + value.message = if (value.message != null) value.message else context.message + value.itemStatus = if (value.itemStatus != null) value.itemStatus else context.itemStatus + value.description = if (value.description != null) value.description else context.description + value.parameters = if (value.parameters != null) value.parameters else context.parameters + value.labels = if (value.labels != null) value.labels else context.labels + } + writer.context[this.name.toString()] = value +} + +fun TestCase.getContext(): TestItContext? { + val writer = getWriter() ?: return null + return writer.context[this.name.toString()] +} + +/** + * Set current test block as a step container. + * + * Requires [TestItReporter.isStepContainers] to be `true` to work as expected. + */ +fun TestCase.asStepContainer() { + setParameters( + TestItParams( + isStepContainer = true + )) +} + +/** + * @return `true` if `this.parent` set as Step Container using [TestCase.asStepContainer] + */ +fun TestCase.isStep(): Boolean { + val writer = getWriter() ?: return false + val p = writer.params[this.parent?.name.toString()] ?: return false + return p.isStepContainer +} + +/** + * check [TestItParams.isStepContainer] to be `true` + */ +fun TestCase.isStepContainer(): Boolean { + return getParams()?.isStepContainer ?: false +} + + +private fun TestCase.getParams(): TestItParams? { + val writer = getWriter() ?: return null + return writer.params[this.name.toString()] +} + +private fun TestCase.setParameters(value: TestItParams) { + val writer = getWriter() ?: return + val params = writer.params[this.name.toString()] + if (params != null) { + value.isStepContainer = if (value.isStepContainer) value.isStepContainer else params.isStepContainer + value.afterTestThrowable = if (value.afterTestThrowable != null) value.afterTestThrowable else params.afterTestThrowable + value.setupName = if (value.setupName != null) value.setupName else params.setupName + value.teardownName = if (value.teardownName != null) value.teardownName else params.teardownName + } + writer.params[this.name.toString()] = value +} + +private fun TestCase.getWriter(): TestItWriter? { + val extensions = spec.registeredExtensions() + if (extensions.isEmpty()) { return null } + for (item in extensions) { + try { + return (item as TestItReporter).writer + } + catch (_: Exception) {} + } + return null +} + diff --git a/testit-adapter-kotest/src/test/kotlin/ru/testit/services/FixtureServiceTests.kt b/testit-adapter-kotest/src/test/kotlin/ru/testit/services/FixtureServiceTests.kt new file mode 100644 index 0000000..8d9278b --- /dev/null +++ b/testit-adapter-kotest/src/test/kotlin/ru/testit/services/FixtureServiceTests.kt @@ -0,0 +1,201 @@ +package ru.testit.services + +import io.kotest.core.spec.style.StringSpec +import io.kotest.core.test.TestCase +import io.kotest.engine.extensions.ExtensionException +import io.kotest.matchers.shouldBe +import io.mockk.* +import ru.testit.models.* +import ru.testit.utils.* +import java.util.UUID +import io.kotest.core.test.TestResult + + +class FixtureServiceTests : StringSpec({ + val mockAdapterManager = mockk(relaxed = true) + val mockExecutableTestService = mockk(relaxed = true) + val mockTestService = mockk(relaxed = true) + val fixtureService = FixtureService(mockAdapterManager, mockExecutableTestService, mockTestService) + + beforeTest { + clearMocks(mockAdapterManager, mockExecutableTestService, mockTestService) + mockkStatic("ru.testit.utils.ExtensionsKt") // Static mock + } + + "onBeforeTestStart should initialize beforeFixtureUUID and call adapterManager.startPrepareFixtureEachTest" { + val testCase = mockk() + val start = System.currentTimeMillis() + val lastClassContainerId = "classContainerId" + val parentUuid = UUID.randomUUID().toString() + + every { mockExecutableTestService.getUuid() } returns parentUuid + + fixtureService.onBeforeTestStart(testCase, start, lastClassContainerId) + + fixtureService.beforeFixtureUUID shouldBe fixtureService.beforeFixtureUUID // Verifying UUID is set + verify { + mockAdapterManager.startPrepareFixtureEachTest( + lastClassContainerId, + fixtureService.beforeFixtureUUID!!, + withArg { fixtureResult -> + fixtureResult.name shouldBe "Setup" + fixtureResult.start shouldBe start + fixtureResult.parent shouldBe parentUuid + fixtureResult.itemStage shouldBe ItemStage.RUNNING + } + ) + } + } + + + "onBeforeTestOk should mark fixture as PASSED and stop it" { + + val capturedLambda = slot<(FixtureResult) -> Unit>() + val beforeFixtureUUID = UUID.randomUUID().toString() + + val start = System.currentTimeMillis() + val stop = start + 1000 + fixtureService.beforeFixtureUUID = beforeFixtureUUID + + val testCase = mockk(relaxed = true) { + every { setupName() } returns "CustomSetup" + } + every { + mockAdapterManager.updateFixture(eq(beforeFixtureUUID), capture(capturedLambda)) + } just Runs + + every { + mockAdapterManager.stopFixture(beforeFixtureUUID) + } just Runs + + fixtureService.onBeforeTestOk(testCase, start, stop) + + val fixtureResult = FixtureResult(name = "Initial", start = start) + capturedLambda.captured.invoke(fixtureResult) + + fixtureResult.itemStatus shouldBe ItemStatus.PASSED + fixtureResult.itemStage shouldBe ItemStage.FINISHED + fixtureResult.stop shouldBe stop + fixtureResult.name shouldBe "CustomSetup" + + + verify { + mockAdapterManager.updateFixture(beforeFixtureUUID, any()) + mockAdapterManager.stopFixture(beforeFixtureUUID) + } + } + + "registerAfterTestFixture should initialize afterFixtureUUID and call adapterManager.startTearDownFixtureEachTest" { + val testCase = mockk() + val start = System.currentTimeMillis() + val lastClassContainerId = "classContainerId" + val parentUuid = UUID.randomUUID().toString() + + every { mockExecutableTestService.getUuid() } returns parentUuid + + fixtureService.registerAfterTestFixture(testCase, start, lastClassContainerId) + + fixtureService.afterFixtureUUID shouldBe fixtureService.afterFixtureUUID // Verifying UUID is set + verify { + mockAdapterManager.startTearDownFixtureEachTest( + lastClassContainerId, + fixtureService.afterFixtureUUID!!, + withArg { fixtureResult -> + fixtureResult.start shouldBe start + fixtureResult.parent shouldBe parentUuid + fixtureResult.itemStage shouldBe ItemStage.RUNNING + } + ) + } + } + + "handleFixturesFails should handle failures and update the test case appropriately" { + mockkObject(AdapterUtils) + every { + AdapterUtils.isAfterTestRegistered(any()) + } returns true + + + val testCase = mockk { + every { isStep() } returns false + every { afterTestThrowable() } returns null + every { teardownName() } returns "CustomTeardown" + every { setupName() } returns "CustomSetup" + every { spec.rootTests()[0].name.testName } returns "SampleName" + } + val result = mockk(relaxed = true) { + every { errorOrNull } returns ExtensionException.BeforeAnyException(Error("BeforeException")) + } + + val beforeTestStart = System.currentTimeMillis() + val afterTestStart = System.currentTimeMillis() + + val isFinished = fixtureService.handleFixturesFails(testCase, result, beforeTestStart, afterTestStart) + + isFinished shouldBe true + verify { + mockAdapterManager.updateFixture(fixtureService.beforeFixtureUUID!!, any()) + mockExecutableTestService.setAfterStatus() + mockTestService.stopTestCase( + any(), + any(), + ItemStatus.FAILED + ) + } + } + + "onAfterTestFailed should mark after fixture as FAILED and stop it" { + + val capturedLambda = slot<(FixtureResult) -> Unit>() + val start = System.currentTimeMillis() + val stop = start + 1000 + val afterFixtureUUID = UUID.randomUUID().toString() + fixtureService.afterFixtureUUID = afterFixtureUUID + val testCase = mockk { + every { teardownName() } returns "CustomTeardown" + every { afterTestThrowable() } returns Throwable("Test Error") + } + every { + mockAdapterManager.updateFixture(eq(afterFixtureUUID), capture(capturedLambda)) + } just Runs + every { + mockAdapterManager.stopFixture(afterFixtureUUID) + } just Runs + + fixtureService.onAfterTestFailed(testCase, start, stop) + + val fixtureResult = FixtureResult(name = "Initial", start = start) + capturedLambda.captured.invoke(fixtureResult) + + fixtureResult.itemStatus shouldBe ItemStatus.FAILED + fixtureResult.itemStage shouldBe ItemStage.FINISHED + fixtureResult.stop shouldBe stop + fixtureResult.name shouldBe "CustomTeardown" + + verify { + mockAdapterManager.updateFixture(afterFixtureUUID, any()) + mockAdapterManager.stopFixture(afterFixtureUUID) + } + } + + "updateAfterTestTime should update start time of the after fixture" { + val start = System.currentTimeMillis() + val capturedLambda = slot<(FixtureResult) -> Unit>() + val afterFixtureUUID = UUID.randomUUID().toString() + fixtureService.afterFixtureUUID = afterFixtureUUID + + every { + mockAdapterManager.updateFixture(eq(afterFixtureUUID), capture(capturedLambda)) + } just Runs + + fixtureService.updateAfterTestTime(start) + val fixtureResult = FixtureResult(name = "Initial", start = start) + capturedLambda.captured.invoke(fixtureResult) + + fixtureResult.start shouldBe start + + verify { + mockAdapterManager.updateFixture(afterFixtureUUID, any()) + } + } +}) diff --git a/testit-adapter-kotest/src/test/kotlin/ru/testit/services/StepServiceTests.kt b/testit-adapter-kotest/src/test/kotlin/ru/testit/services/StepServiceTests.kt new file mode 100644 index 0000000..98d9d17 --- /dev/null +++ b/testit-adapter-kotest/src/test/kotlin/ru/testit/services/StepServiceTests.kt @@ -0,0 +1,150 @@ +package ru.testit.services + +import io.kotest.common.TestPath +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import io.mockk.* +import ru.testit.models.ItemStatus +import ru.testit.models.StepResult +import java.util.concurrent.ConcurrentHashMap +import io.kotest.core.test.TestCase +import io.kotest.core.test.TestResult +import io.kotest.matchers.shouldNotBe +import kotlin.time.Duration + +class StepServiceTests : StringSpec({ + + val mockAdapterManager = mockk(relaxed = true) + val mockExecutableTestService = mockk(relaxed = true) + val uuids = ConcurrentHashMap() + val stepService = StepService(mockAdapterManager, uuids, mockExecutableTestService) + + beforeTest { + clearMocks(mockAdapterManager, mockExecutableTestService) + uuids.clear() + } + + "onStepStart should initialize and start a step" { + val testCase = mockk { + every { name.testName } returns "StepName" + every { spec.rootTests()[0].name.testName } returns "TestRoot" + every { descriptor.path() } returns TestPath("stepPath") + every { parent?.name?.testName } returns "ParentStep" + } + val parentUuid = "ParentUUID" + every { mockExecutableTestService.getUuid() } returns parentUuid + + stepService.onStepStart(testCase) + + uuids[TestPath("stepPath")] shouldBe + uuids[TestPath("stepPath")] // Ensure UUID is added to map + + val uuid: String = uuids[TestPath("stepPath")]!!.toString() + + verify { + mockAdapterManager.startStep( + parentUuid, + uuid, + withArg { stepResult -> + stepResult.name shouldBe "StepName" + stepResult.start shouldNotBe null + } + ) + } + } + + "stopStepWithResult should handle success result" { + val testCase = mockk { + every { name.testName } returns "StepName" + every { descriptor.path() } returns TestPath("stepPath") + } + uuids[TestPath("stepPath")] = "StepUUID" + + val testResult = TestResult.Success(Duration.parse("10s")) + + stepService.stopStepWithResult(testCase, testResult) + + verify { + mockAdapterManager.updateStep( + "StepUUID", + withArg { consumer -> + val stepResult = StepResult() + consumer.accept(stepResult) + stepResult.itemStatus shouldBe ItemStatus.PASSED + stepResult.throwable shouldBe null + } + ) + mockAdapterManager.stopStep("StepUUID") + } + } + + "stopStepWithResult should handle ignored result" { + val testCase = mockk { + every { name.testName } returns "StepName" + every { descriptor.path() } returns TestPath("stepPath") + } + uuids[TestPath("stepPath")] = "StepUUID" + + val testResult = TestResult.Ignored("Step Ignored") + + stepService.stopStepWithResult(testCase, testResult) + + verify { + mockAdapterManager.updateStep( + "StepUUID", + withArg { consumer -> + val stepResult = StepResult() + consumer.accept(stepResult) + stepResult.itemStatus shouldBe ItemStatus.SKIPPED + } + ) + mockAdapterManager.stopStep("StepUUID") + } + } + + "stopStepWithResult should handle failure result" { + val testCase = mockk { + every { name.testName } returns "StepName" + every { descriptor.path() } returns TestPath("stepPath") + } + uuids[TestPath("stepPath")] = "StepUUID" + + val error = Throwable("Step Failed") + val testResult = TestResult.Error(Duration.ZERO, error) + + stepService.stopStepWithResult(testCase, testResult) + + verify { + mockAdapterManager.updateStep( + "StepUUID", + withArg { consumer -> + val stepResult = StepResult() + consumer.accept(stepResult) + stepResult.itemStatus shouldBe ItemStatus.FAILED + stepResult.throwable shouldBe error + } + ) + mockAdapterManager.stopStep("StepUUID") + mockAdapterManager.updateStep( + any(), + withArg { consumer -> + val stepResult = StepResult() + consumer.accept(stepResult) + stepResult.itemStatus shouldBe ItemStatus.FAILED + stepResult.throwable shouldBe error + } + ) + mockExecutableTestService.setAsFailedBy(error) + } + } + + "getStepUuid should return hashed UUID based on step path" { + val testCase = mockk { + every { name.testName } returns "StepName" + every { parent?.name?.testName } returns "ParentStep" + } + + val stepUuid = stepService.run { getStepUuid(testCase) } + stepUuid shouldBe Utils.getHash("ParentStepStepName") + } +}) diff --git a/testit-adapter-kotest/src/test/kotlin/ru/testit/services/TestServiceTests.kt b/testit-adapter-kotest/src/test/kotlin/ru/testit/services/TestServiceTests.kt new file mode 100644 index 0000000..d8f46ac --- /dev/null +++ b/testit-adapter-kotest/src/test/kotlin/ru/testit/services/TestServiceTests.kt @@ -0,0 +1,192 @@ +package ru.testit.services + +import io.kotest.common.TestPath +import ru.testit.listener.Consumers +import io.kotest.core.test.TestCase +import io.kotest.core.test.TestResult +import io.kotest.matchers.shouldBe +import io.kotest.core.spec.style.StringSpec +import io.kotest.core.test.TestType +import io.mockk.* +import ru.testit.models.ItemStatus +import ru.testit.models.TestItContext +import ru.testit.models.TestResultCommon +import ru.testit.utils.getContext +import ru.testit.utils.isStepContainer +import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration + +class TestServiceTests : StringSpec({ + + val mockAdapterManager = mockk(relaxed = true) + val mockExecutableTestService = mockk(relaxed = true) + val mockTestCase = mockk(relaxed = true) + + val uuids = ConcurrentHashMap() + val testService_ = TestService(mockAdapterManager, uuids, mockExecutableTestService) + val testService = spyk(testService_) + + beforeTest { + clearMocks(mockAdapterManager, mockExecutableTestService) + uuids.clear() + mockkStatic("ru.testit.utils.ExtensionsKt") // Static mock + } + + "should start a test correctly" { + // Arrange + val uuid = "test-uuid" + val testName = "Test Name" + val className = "Spec\$Subclass1" + val packageName = "io.kotest.core.spec" + val expectedFullName = "Test Name" + + every { mockTestCase.name.testName } returns testName + mockkObject(Utils.Companion) + every { Utils.genExternalID(expectedFullName) } returns "generated-id" + every { Utils.defaultLabels() } returns mutableListOf() + every { Utils.defaultLinks() } returns mutableListOf() + every { mockTestCase.descriptor.path() } returns TestPath("test-path") + + // Act + testService.onTestStart(mockTestCase, uuid) + + // Assert + verify { + mockAdapterManager.scheduleTestCase( + withArg { + it.uuid shouldBe uuid + it.name shouldBe testName + it.className shouldBe className + it.spaceName shouldBe packageName + it.externalId shouldBe "generated-id" + } + ) + mockAdapterManager.startTestCase(uuid) + } + uuids[TestPath("test-path")] shouldBe uuid + } + + "should stop a test with success result" { + // Arrange + val uuid = "test-uuid" + val mockResult = TestResult.success(0) + val testName = "Test Name" + + every { mockTestCase.descriptor.path() } returns TestPath("test-path") + every { uuids[mockTestCase.descriptor.path()] } returns uuid + every { mockExecutableTestService.getTest() } returns mockk(relaxed = true) + every { mockTestCase.isStepContainer() } returns false + every { mockTestCase.name.testName } returns testName + every { mockTestCase.descriptor.id.value } returns "test-path" + every { mockExecutableTestService.getUuid() } returns uuid + testService.uuids[TestPath(mockTestCase.descriptor.id.value)] = uuid + + // Act + testService.stopTestWithResult(mockTestCase, mockResult) + + // Assert + verify { mockAdapterManager.updateTestCase(uuid, any()) } + verify { mockAdapterManager.stopTestCase(uuid) } + } + + "should handle a failed test result" { + // Arrange + val uuid = "test-uuid" + val exception = RuntimeException("Test failure") + val mockResult = spyk(TestResult.Error(Duration.ZERO, exception)) + + every { uuids[mockTestCase.descriptor.path()] } returns uuid + every { mockExecutableTestService.getTest() } returns mockk(relaxed = true) + every { mockTestCase.isStepContainer() } returns false + every { mockExecutableTestService.isTestStatus() } returns true + every { mockExecutableTestService.getUuid() } returns uuid + every { mockResult.errorOrNull } returns exception + + + // Act + testService.stopTestWithResult(mockTestCase, mockResult) + + // Assert + verify { mockAdapterManager.updateTestCase(uuid, + withArg { consumer -> + val result = TestResultCommon() + consumer.accept(result) + result.itemStatus shouldBe ItemStatus.FAILED + result.throwable shouldBe exception + }) + } + verify { mockAdapterManager.stopTestCase(uuid) } + } + + "should handle ignored test result" { + // Arrange + val uuid = "test-uuid" + val mockResult = spyk(TestResult.Ignored("Test ignored")) + + every { uuids[mockTestCase.descriptor.path()] } returns uuid + every { mockExecutableTestService.getTest() } returns mockk(relaxed = true) + every { mockTestCase.isStepContainer() } returns false + every { mockExecutableTestService.getUuid() } returns uuid + every { mockResult.errorOrNull } returns null + + + // Act + testService.stopTestWithResult(mockTestCase, mockResult) + + // Assert + verify { + mockAdapterManager.updateTestCase(uuid, + withArg { consumer -> + val result = TestResultCommon() + consumer.accept(result) + result.itemStatus shouldBe ItemStatus.SKIPPED + result.throwable shouldBe null + } + ) + mockAdapterManager.stopTestCase(uuid) + } + } + + "should not stop a test if it's a step container and failed" { + // Arrange + val uuid = "test-uuid" + val exception = RuntimeException("Step failed") + val mockResult = TestResult.Error(Duration.ZERO, exception) + + val testCase = mockk { + every { name.testName } returns "TestName" + every { descriptor.id.value } returns "test-path" + every { type.name } returns TestType.Container.toString() + every { isStepContainer() } returns true + every { getContext() } returns null + } + + val mockExecutableTest = mockk(relaxed = true) + every { mockExecutableTest.isFailedStep } returns true + every { mockExecutableTest.stepCause } returns exception + + every { mockExecutableTestService.isTestStatus() } returns true + every { mockExecutableTestService.getTest() } returns mockExecutableTest + every {mockExecutableTestService.getUuid() } returns uuid + + every { uuids[testCase.descriptor.path()] } returns uuid + every { testCase.descriptor.path() } returns TestPath("") + + + // Act + testService.stopTestWithResult(testCase, mockResult) + + // Assert + verify { + mockAdapterManager.updateTestCase(uuid, + withArg { consumer -> + val result = TestResultCommon() + consumer.accept(result) + result.itemStatus shouldBe ItemStatus.FAILED + result.throwable shouldBe exception + } + ) + mockAdapterManager.stopTestCase(uuid) + } + } +}) diff --git a/testit-adapter-kotest/src/test/resources/testit.properties b/testit-adapter-kotest/src/test/resources/testit.properties index e9bac2f..9aa06b8 100644 --- a/testit-adapter-kotest/src/test/resources/testit.properties +++ b/testit-adapter-kotest/src/test/resources/testit.properties @@ -4,4 +4,4 @@ projectId={%PROJECT_ID%} configurationId={%CONFIGURATION_ID%} testRunId={%TEST_RUN_ID%} testRunName={%TEST_RUN_NAME%} -adapterMode={%ADAPTER_MODE%} +adapterMode={%ADAPTER_MODE%} \ No newline at end of file diff --git a/testit-kotlin-commons/build.gradle.kts b/testit-kotlin-commons/build.gradle.kts index c7e12cf..32d9482 100644 --- a/testit-kotlin-commons/build.gradle.kts +++ b/testit-kotlin-commons/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "ru.testit" -version = "0.1.1" +version = "0.2.0" val slf4jVersion = "1.7.2" @@ -16,7 +16,7 @@ java { } dependencies { - implementation("ru.testit:testit-api-client-kotlin:0.1.0") + implementation("ru.testit:testit-api-client-kotlin:0.2.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("org.slf4j:slf4j-api:$slf4jVersion") diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/ApiClient.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/ApiClient.kt index f9ea233..ea09e6b 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/ApiClient.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/ApiClient.kt @@ -57,9 +57,9 @@ interface ApiClient { @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) - fun getTestResult(uuid: UUID): TestResultModel + fun getTestResult(uuid: UUID): TestResultResponse @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) - fun updateTestResult(uuid: UUID, model: TestResultUpdateModel) + fun updateTestResult(uuid: UUID, model: TestResultUpdateV2Request ) } diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/TmsApiClient.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/TmsApiClient.kt index fbed39b..d37a5bc 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/TmsApiClient.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/clients/TmsApiClient.kt @@ -133,11 +133,11 @@ class TmsApiClient(private val clientConfiguration: ClientConfiguration) : ru.te .mapNotNull { it.autoTest?.externalId }.toList() } - override fun getTestResult(uuid: UUID): TestResultModel { + override fun getTestResult(uuid: UUID): TestResultResponse { return testResultsApi.apiV2TestResultsIdGet(uuid) } - override fun updateTestResult(uuid: UUID, model: TestResultUpdateModel) { + override fun updateTestResult(uuid: UUID, model: TestResultUpdateV2Request ) { testResultsApi.apiV2TestResultsIdPut(uuid, model) } diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/AdapterListener.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/AdapterListener.kt index d22d41d..41e664d 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/AdapterListener.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/AdapterListener.kt @@ -1,8 +1,8 @@ package ru.testit.listener -import ru.testit.models.TestResult +import ru.testit.models.TestResultCommon interface AdapterListener : DefaultListener { - fun beforeTestStop(result: TestResult?) { + fun beforeTestStop(result: TestResultCommon?) { } } \ No newline at end of file diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/ListenerManager.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/ListenerManager.kt index cb249c9..93fda1f 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/ListenerManager.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/listener/ListenerManager.kt @@ -2,7 +2,7 @@ package ru.testit.listener import org.slf4j.Logger import org.slf4j.LoggerFactory -import ru.testit.models.TestResult +import ru.testit.models.TestResultCommon import java.util.function.BiConsumer @@ -13,9 +13,9 @@ class ListenerManager(val listeners: List) { private val LOGGER: Logger = LoggerFactory.getLogger(ListenerManager::class.java) } - fun beforeTestStop(result: TestResult) { + fun beforeTestStop(result: TestResultCommon) { runSafelyMethod(listeners, - BiConsumer { obj: AdapterListener, result: TestResult? -> obj.beforeTestStop(result) }, + BiConsumer { obj: AdapterListener, result: TestResultCommon? -> obj.beforeTestStop(result) }, result) } diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/models/TestResult.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/models/TestResultCommon.kt similarity index 96% rename from testit-kotlin-commons/src/main/kotlin/ru/testit/models/TestResult.kt rename to testit-kotlin-commons/src/main/kotlin/ru/testit/models/TestResultCommon.kt index 864151f..a0d3c86 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/models/TestResult.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/models/TestResultCommon.kt @@ -2,11 +2,10 @@ package ru.testit.models import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable -import ru.testit.kotlin.client.models.TestResultModel import ru.testit.services.Utils @Serializable -data class TestResult( +data class TestResultCommon( var uuid: String? = null, var externalId: String = "", var workItemIds: MutableList = mutableListOf(), @@ -38,7 +37,7 @@ data class TestResult( return steps } - fun setSteps(steps: MutableList): TestResult { + fun setSteps(steps: MutableList): TestResultCommon { this.steps = steps return this } diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/services/AdapterManager.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/services/AdapterManager.kt index beb9601..d2dd4ad 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/services/AdapterManager.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/services/AdapterManager.kt @@ -5,9 +5,6 @@ import org.slf4j.LoggerFactory import ru.testit.clients.ApiClient import ru.testit.clients.ClientConfiguration import ru.testit.clients.TmsApiClient -import ru.testit.kotlin.client.infrastructure.ClientError -import ru.testit.kotlin.client.infrastructure.ClientException -import ru.testit.kotlin.client.infrastructure.ServerException import ru.testit.kotlin.client.models.TestRunState import ru.testit.listener.AdapterListener import ru.testit.properties.AdapterConfig @@ -58,7 +55,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, /** * @see [TmsApiClient.createTestRun] */ - suspend fun startTests() { + fun startTests() { if (!adapterConfig.shouldEnableTmsIntegration()) { return } @@ -86,7 +83,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, /** * Is not used in current version. */ - suspend fun stopTests() { + fun stopTests() { if (!adapterConfig.shouldEnableTmsIntegration()) { return } @@ -107,7 +104,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, } } - suspend fun startMainContainer(container: MainContainer) { + fun startMainContainer(container: MainContainer) { if (!adapterConfig.shouldEnableTmsIntegration()) return container.start = System.currentTimeMillis() @@ -116,7 +113,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, if (LOGGER.isDebugEnabled) LOGGER.debug("Start new main container {}", container) } - suspend fun stopMainContainer(uuid: String) { + fun stopMainContainer(uuid: String) { if (!adapterConfig.shouldEnableTmsIntegration()) return val found = storage.getTestsContainer(uuid) @@ -132,7 +129,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, writer?.writeTests(container) } - suspend fun startClassContainer(parentUuid: String, container: ClassContainer) { + fun startClassContainer(parentUuid: String, container: ClassContainer) { if (!adapterConfig.shouldEnableTmsIntegration()) return storage.getTestsContainer(parentUuid).ifPresent { parent -> @@ -146,7 +143,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, if (LOGGER.isDebugEnabled) LOGGER.debug("Start new class container {} for parent {}", container, parentUuid) } - suspend fun stopClassContainer(uuid: String) { + fun stopClassContainer(uuid: String) { if (!adapterConfig.shouldEnableTmsIntegration()) return val found = storage.getClassContainer(uuid) @@ -162,7 +159,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, writer?.writeClass(container) } - suspend fun updateClassContainer(uuid: String, update: Consumer) { + fun updateClassContainer(uuid: String, update: Consumer) { if (!adapterConfig.shouldEnableTmsIntegration()) return if (LOGGER.isDebugEnabled()) LOGGER.debug("Update class container {}", uuid) @@ -176,7 +173,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, update.accept(container) } - suspend fun startTestCase(uuid: String) { + fun startTestCase(uuid: String) { if (!adapterConfig.shouldEnableTmsIntegration()) return threadContext.clear() @@ -197,7 +194,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, } } - suspend fun scheduleTestCase(result: TestResult) { + fun scheduleTestCase(result: TestResultCommon) { if (!adapterConfig.shouldEnableTmsIntegration()) return result.setItemStage(ItemStage.SCHEDULED) @@ -209,7 +206,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, } } - suspend fun updateTestCase(update: Consumer) { + fun updateTestCase(update: Consumer) { if (!adapterConfig.shouldEnableTmsIntegration()) return val root = threadContext.getRoot() @@ -222,7 +219,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, updateTestCase(uuid, update) } - suspend fun updateTestCase(uuid: String, update: Consumer) { + fun updateTestCase(uuid: String, update: Consumer) { if (!adapterConfig.shouldEnableTmsIntegration()) return if (LOGGER.isDebugEnabled()) { @@ -239,7 +236,7 @@ class AdapterManager(private var clientConfiguration: ClientConfiguration, update.accept(testResult) } - suspend fun stopTestCase(uuid: String) { + fun stopTestCase(uuid: String) { if (!adapterConfig.shouldEnableTmsIntegration()) return val found = storage.getTestResult(uuid) diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/services/ResultStorage.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/services/ResultStorage.kt index 81beff8..2eaae91 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/services/ResultStorage.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/services/ResultStorage.kt @@ -1,8 +1,6 @@ package ru.testit.services -import kotlinx.serialization.Serializable import ru.testit.models.* -import java.util.LinkedList import java.util.Objects import java.util.Optional import java.util.concurrent.ConcurrentHashMap @@ -18,8 +16,8 @@ class ResultStorage { private val storage = ConcurrentHashMap() private val lock: ReadWriteLock = ReentrantReadWriteLock() - fun getTestResult(uuid: String): Optional { - return get(uuid, TestResult::class.java) + fun getTestResult(uuid: String): Optional { + return get(uuid, TestResultCommon::class.java) } fun getFixture(uuid: String): Optional { diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Converter.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Converter.kt index 476ca5c..766601b 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Converter.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Converter.kt @@ -3,6 +3,8 @@ package ru.testit.writers import ru.testit.kotlin.client.models.* import ru.testit.kotlin.client.models.LinkType import ru.testit.models.* +import ru.testit.models.Label +import ru.testit.models.StepResult import java.time.OffsetDateTime import java.time.ZoneOffset import java.util.* @@ -13,7 +15,7 @@ class Converter { companion object { - fun testResultToAutoTestPostModel(result: TestResult, projectId: UUID?): AutoTestPostModel { + fun testResultToAutoTestPostModel(result: TestResultCommon, projectId: UUID?): AutoTestPostModel { val model = AutoTestPostModel( externalId = result.externalId!!, projectId = projectId ?: UUID.fromString(result.uuid), @@ -29,13 +31,13 @@ class Converter { ) return model } - fun testResultToAutoTestPutModel(result: TestResult): AutoTestPutModel { + fun testResultToAutoTestPutModel(result: TestResultCommon): AutoTestPutModel { return testResultToAutoTestPutModel(result, null, null) } - fun testResultToAutoTestPutModel(result: TestResult, - projectId: UUID?, - isFlaky: Boolean?): AutoTestPutModel { + fun testResultToAutoTestPutModel(result: TestResultCommon, + projectId: UUID?, + isFlaky: Boolean?): AutoTestPutModel { val model = AutoTestPutModel( externalId = result.externalId!!, projectId = projectId ?: UUID.fromString(result.uuid), @@ -56,11 +58,11 @@ class Converter { } - fun testResultToTestResultUpdateModel(result: TestResultModel, + fun testResultToTestResultUpdateModel(result: TestResultResponse, setupResults: List?, teardownResults: List? - ): TestResultUpdateModel { - val model = TestResultUpdateModel( + ): TestResultUpdateV2Request { + val model = TestResultUpdateV2Request ( duration = result.durationInMs, outcome = result.outcome, links = result.links, @@ -140,13 +142,13 @@ class Converter { return model } - fun testResultToAutoTestResultsForTestRunModel(result: TestResult, configurationId: UUID?, + fun testResultToAutoTestResultsForTestRunModel(result: TestResultCommon, configurationId: UUID?, ): AutoTestResultsForTestRunModel { return testResultToAutoTestResultsForTestRunModel( result, configurationId, null, null) } - fun testResultToAutoTestResultsForTestRunModel(result: TestResult, + fun testResultToAutoTestResultsForTestRunModel(result: TestResultCommon, configurationId: UUID?, setupResults: List?, teardownResults: List? @@ -255,11 +257,11 @@ class Converter { return date.toInstant().atOffset(ZoneOffset.UTC) } - fun convertAttachments(uuids: List): List = + fun convertAttachments(uuids: List): List? = uuids.map { AttachmentPutModel(id = UUID.fromString(it)) } - fun convertAttachmentsFromModel(models: List): List = - models.map { AttachmentPutModel(id = it.id) } + fun convertAttachmentsFromModel(models: List): List = + models.map { AttachmentUpdateRequest(id = it.id) } } diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/HttpWriter.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/HttpWriter.kt index 66086c9..fd13e6b 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/HttpWriter.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/HttpWriter.kt @@ -1,7 +1,5 @@ package ru.testit.writers -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import java.util.* import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -14,7 +12,7 @@ import ru.testit.kotlin.client.models.AutoTestResultsForTestRunModel import ru.testit.models.ClassContainer import ru.testit.models.ItemStatus import ru.testit.models.MainContainer -import ru.testit.models.TestResult +import ru.testit.models.TestResultCommon import ru.testit.services.ResultStorage import java.util.Collections.addAll @@ -29,33 +27,33 @@ class HttpWriter( private val testResults: MutableMap = HashMap() - override fun writeTest(testResult: TestResult) { + override fun writeTest(testResultCommon: TestResultCommon) { try { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Write auto test {}", testResult.externalId) + LOGGER.debug("Write auto test {}", testResultCommon.externalId) } - val autotest = apiClient.getAutoTestByExternalId(testResult.externalId!!) - val workItemIds = testResult.workItemIds + val autotest = apiClient.getAutoTestByExternalId(testResultCommon.externalId!!) + val workItemIds = testResultCommon.workItemIds var autoTestId: String? = null if (autotest != null) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Auto test is exist. Update auto test {}", testResult.externalId) + LOGGER.debug("Auto test is exist. Update auto test {}", testResultCommon.externalId) } val autoTestPutModel: AutoTestPutModel when { - testResult.itemStatus == ItemStatus.FAILED -> { + testResultCommon.itemStatus == ItemStatus.FAILED -> { autoTestPutModel = Converter.autoTestModelToAutoTestPutModel( autoTestModel = autotest, - links = Converter.convertPutLinks(testResult.linkItems), + links = Converter.convertPutLinks(testResultCommon.linkItems), isFlaky = autotest.isFlaky) } else -> { autoTestPutModel = Converter.testResultToAutoTestPutModel( - result = testResult, + result = testResultCommon, projectId = UUID.fromString(config.projectId), isFlaky = autotest.isFlaky) } @@ -66,10 +64,10 @@ class HttpWriter( autoTestId = autotest.id.toString() } else { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Create new auto test {}", testResult.externalId) + LOGGER.debug("Create new auto test {}", testResultCommon.externalId) } - val model = Converter.testResultToAutoTestPostModel(testResult, UUID.fromString(config.projectId)) + val model = Converter.testResultToAutoTestPostModel(testResultCommon, UUID.fromString(config.projectId)) autoTestId = apiClient.createAutoTest(model) } @@ -78,13 +76,13 @@ class HttpWriter( } val autoTestResultsForTestRunModel = Converter.testResultToAutoTestResultsForTestRunModel( - testResult, UUID.fromString(config.configurationId)) + testResultCommon, UUID.fromString(config.configurationId)) val results: MutableList = mutableListOf() results.add(autoTestResultsForTestRunModel) LOGGER.debug("send result by testRunId: " + config.testRunId) val ids = apiClient.sendTestResults(config.testRunId, results) - testResults[testResult.uuid!!] = UUID.fromString(ids[0]) + testResults[testResultCommon.uuid!!] = UUID.fromString(ids[0]) } catch (e: Exception) { LOGGER.error("Can not write the autotest: {}", e.message) } diff --git a/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Writer.kt b/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Writer.kt index fd23c45..3a960b0 100644 --- a/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Writer.kt +++ b/testit-kotlin-commons/src/main/kotlin/ru/testit/writers/Writer.kt @@ -2,10 +2,10 @@ package ru.testit.writers import ru.testit.models.ClassContainer; import ru.testit.models.MainContainer; -import ru.testit.models.TestResult; +import ru.testit.models.TestResultCommon; interface Writer { - fun writeTest(testResult: TestResult) + fun writeTest(testResultCommon: TestResultCommon) fun writeClass(container: ClassContainer) fun writeTests(container: MainContainer) fun writeAttachment(path: String): String diff --git a/testit-kotlin-commons/src/test/kotlin/ru/testit/Helper.kt b/testit-kotlin-commons/src/test/kotlin/ru/testit/Helper.kt index 372a92f..6294fd9 100644 --- a/testit-kotlin-commons/src/test/kotlin/ru/testit/Helper.kt +++ b/testit-kotlin-commons/src/test/kotlin/ru/testit/Helper.kt @@ -2,7 +2,9 @@ package ru.testit import ru.testit.kotlin.client.models.* import ru.testit.models.* +import ru.testit.models.Label import ru.testit.models.LinkType +import ru.testit.models.StepResult import java.time.OffsetDateTime import java.util.* @@ -46,7 +48,7 @@ class Helper { return listOf(UUID.randomUUID()) } - fun generateTestResult(): TestResult { + fun generateTestResult(): TestResultCommon { val startDate = Date() val stopDate = Date(startDate.time + 1000) @@ -56,7 +58,7 @@ class Helper { val labels_ = mutableListOf(Label(LABEL_NAME)) - return TestResult().setSteps(steps) + return TestResultCommon().setSteps(steps) .apply { externalId = EXTERNAL_ID uuid = TEST_UUID