diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index 6b2a2b5d..07f69d89 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -14,19 +14,14 @@ inputs: kotest-tag: description: > The Kotest Tag to use when executing the tests. Check Tag.kt for valid options. Different Tags might require the - Databroker to be started in a different mode. Currently only unsecured mode (no tls, no authentication) is - supported. - default: '!CustomDatabroker' + Databroker to be started in a different mode. + default: '' runs: using: "composite" steps: - - name: "Run Docker Container of Databroker in detached mode" - run: docker run --pull=always --rm --publish 55556:55556/tcp --detach --name databroker ghcr.io/eclipse/kuksa.val/databroker:${{ inputs.databroker-version }} --port 55556 --insecure - shell: bash - - name: Run 'test' with Gradle Wrapper - run: ./gradlew test -Dkotest.tags="${{ inputs.kotest-tag}}" + run: ./gradlew test -Ddatabroker.tag="${{ inputs.databroker-version }}" -Dkotest.tags="${{ inputs.kotest-tag}}" shell: bash - name: Upload Test Reports @@ -51,8 +46,3 @@ runs: path: ${{ github.workspace }}/build/reports/jacoco/jacocoRootReport/html/* if-no-files-found: error retention-days: 14 - - - name: "Stop Docker Container of Databroker" - if: always() - run: docker stop databroker - shell: bash diff --git a/.github/workflows/daily_integration_main-master.yaml b/.github/workflows/daily_integration_main-master.yaml index e2edaf51..e6cf1179 100644 --- a/.github/workflows/daily_integration_main-master.yaml +++ b/.github/workflows/daily_integration_main-master.yaml @@ -21,4 +21,4 @@ jobs: with: upload-test-reports: true databroker-version: master - kotest-tag: "Integration & DefaultDatabroker" + kotest-tag: "Integration" diff --git a/.run/kuksa-vss-core_UnitTests.run.xml b/.run/All Tests.run.xml similarity index 82% rename from .run/kuksa-vss-core_UnitTests.run.xml rename to .run/All Tests.run.xml index e5fe1eeb..4ac3a4be 100644 --- a/.run/kuksa-vss-core_UnitTests.run.xml +++ b/.run/All Tests.run.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/.run/kuksa-sdk_UnitTests.run.xml b/.run/Functional Tests.run.xml similarity index 74% rename from .run/kuksa-sdk_UnitTests.run.xml rename to .run/Functional Tests.run.xml index 5edc8246..4bbbccaa 100644 --- a/.run/kuksa-sdk_UnitTests.run.xml +++ b/.run/Functional Tests.run.xml @@ -1,5 +1,5 @@ - + - true true false + false \ No newline at end of file diff --git a/.run/kuksa-sdk_IntegrationTests.run.xml b/.run/Integration Tests.run.xml similarity index 79% rename from .run/kuksa-sdk_IntegrationTests.run.xml rename to .run/Integration Tests.run.xml index 12bc49be..69d563d5 100644 --- a/.run/kuksa-sdk_IntegrationTests.run.xml +++ b/.run/Integration Tests.run.xml @@ -1,5 +1,5 @@ - + - false true diff --git a/.run/All UnitTests.run.xml b/.run/Unit Tests.run.xml similarity index 86% rename from .run/All UnitTests.run.xml rename to .run/Unit Tests.run.xml index db91b812..4878386c 100644 --- a/.run/All UnitTests.run.xml +++ b/.run/Unit Tests.run.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/.run/app_UnitTests.run.xml b/.run/app_UnitTests.run.xml deleted file mode 100644 index cfbef6e1..00000000 --- a/.run/app_UnitTests.run.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - false - true - false - - - \ No newline at end of file diff --git a/.run/kuksa-vss-processor_UnitTests.run.xml b/.run/kuksa-vss-processor_UnitTests.run.xml deleted file mode 100644 index a38b517a..00000000 --- a/.run/kuksa-vss-processor_UnitTests.run.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - true - true - false - - - \ No newline at end of file diff --git a/README.md b/README.md index b559da66..bad684eb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) [![Gitter](https://img.shields.io/gitter/room/kuksa-val/community)](https://gitter.im/kuksa-val/community) -![SDK:main <-> Databroker:master](https://github.com/eclipse-kuksa/kuksa-android-sdk/actions/workflows/daily_integration_main-master.yaml/badge.svg) +[![SDK:main <-> Databroker:master](https://github.com/eclipse-kuksa/kuksa-android-sdk/actions/workflows/daily_integration_main-master.yaml/badge.svg)](https://github.com/eclipse-kuksa/kuksa-android-sdk/actions/workflows/daily_integration_main-master.yaml?query=branch%3Amain) This is an Android SDK for the [KUKSA Vehicle Abstraction Layer](https://github.com/eclipse/kuksa.val). diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c8d4ad0f..9af15d3a 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -103,7 +103,7 @@ If this build fails it should be considered as a warning **SDK:main -> Databroker:master** -![SDK:main <-> Databroker:master](https://github.com/eclipse-kuksa/kuksa-android-sdk/actions/workflows/daily_integration_main-master.yaml/badge.svg) +[![SDK:main <-> Databroker:master](https://github.com/eclipse-kuksa/kuksa-android-sdk/actions/workflows/daily_integration_main-master.yaml/badge.svg)](https://github.com/eclipse-kuksa/kuksa-android-sdk/actions/workflows/daily_integration_main-master.yaml?query=branch%3Amain) This means both the SDK and Databroker are running in a kind of "bleeding edge" state in their currently developed version. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eb16ec18..dc73e24b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ detekt = "1.23.5" datastore = "1.0.0" constraintlayoutCompose = "1.0.1" datastorePreferences = "1.0.0" +dockerJavaCore = "3.3.6" dokka = "1.9.10" gson = "2.10.1" kotlin = "1.9.22" @@ -34,6 +35,8 @@ androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "d androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata", version.ref = "runtimeLivedata" } +docker-java-core = { module = "com.github.docker-java:docker-java-core", version.ref = "dockerJavaCore" } +docker-java-transport-httpclient5 = { module = "com.github.docker-java:docker-java-transport-httpclient5", version.ref = "dockerJavaCore" } grpc-okhttp = { group = "io.grpc", name = "grpc-okhttp", version.ref = "grpc" } grpc-protobuf = { group = "io.grpc", name = "grpc-protobuf-lite", version.ref = "grpc" } grpc-stub = { group = "io.grpc", name = "grpc-stub", version.ref = "grpc" } diff --git a/kuksa-sdk/build.gradle.kts b/kuksa-sdk/build.gradle.kts index be14acbb..0289305a 100644 --- a/kuksa-sdk/build.gradle.kts +++ b/kuksa-sdk/build.gradle.kts @@ -94,6 +94,9 @@ dependencies { testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.kotest) testImplementation(libs.mockk) + + testImplementation(libs.docker.java.core) + testImplementation(libs.docker.java.transport.httpclient5) } publish { diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt index cfa5e847..232d291a 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/DataBrokerConnectorAuthenticationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,32 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 + * */ package org.eclipse.kuksa.connectivity.authentication +import io.grpc.StatusRuntimeException +import io.kotest.assertions.nondeterministic.eventually import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.types.instanceOf import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnectorProvider +import org.eclipse.kuksa.connectivity.databroker.DataBrokerException +import org.eclipse.kuksa.connectivity.databroker.docker.DataBrokerDockerContainer +import org.eclipse.kuksa.connectivity.databroker.docker.SecureDataBrokerDockerContainer import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest +import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest +import org.eclipse.kuksa.mocking.FriendlyVssPathListener import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.test.TestResourceFile import org.eclipse.kuksa.test.kotest.Authentication -import org.eclipse.kuksa.test.kotest.CustomDatabroker -import org.eclipse.kuksa.test.kotest.Insecure import org.eclipse.kuksa.test.kotest.Integration -import java.io.InputStream +import org.eclipse.kuksa.test.kotest.Secure +import org.eclipse.kuksa.test.kotest.SecureDataBroker +import org.eclipse.kuksa.test.kotest.eventuallyConfiguration import kotlin.random.Random import kotlin.random.nextInt @@ -38,7 +48,19 @@ import kotlin.random.nextInt // ./gradlew clean test -Dkotest.tags="Authentication" class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ - tags(Integration, Authentication, Insecure, CustomDatabroker) + tags(Integration, Authentication, Secure, SecureDataBroker) + + var databrokerContainer: DataBrokerDockerContainer? = null + beforeSpec { + databrokerContainer = SecureDataBrokerDockerContainer() + .apply { + start() + } + } + + afterSpec { + databrokerContainer?.stop() + } val random = Random(System.nanoTime()) @@ -46,9 +68,12 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ val dataBrokerConnectorProvider = DataBrokerConnectorProvider() val speedVssPath = "Vehicle.Speed" - and("an insecure DataBrokerConnector with a READ_WRITE_ALL JWT") { - val jwtFileStream = JwtType.READ_WRITE_ALL.asInputStream() - val dataBrokerConnector = dataBrokerConnectorProvider.createInsecure(jwtFileStream = jwtFileStream) + and("a secure DataBrokerConnector with a READ_WRITE_ALL JWT") { + val jwtFile = JwtType.READ_WRITE_ALL + + val dataBrokerConnector = dataBrokerConnectorProvider.createSecure( + jwtFileStream = jwtFile.asInputStream(), + ) and("a successfully established connection") { val connection = dataBrokerConnector.connect() @@ -76,9 +101,11 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ } } - and("an insecure DataBrokerConnector with a READ_ALL JWT") { - val jwtFileStream = JwtType.READ_ALL.asInputStream() - val dataBrokerConnector = dataBrokerConnectorProvider.createInsecure(jwtFileStream = jwtFileStream) + and("a secure DataBrokerConnector with a READ_ALL JWT") { + val jwtFile = JwtType.READ_ALL + val dataBrokerConnector = dataBrokerConnectorProvider.createSecure( + jwtFileStream = jwtFile.asInputStream(), + ) and("a successfully established connection") { val connection = dataBrokerConnector.connect() @@ -105,9 +132,11 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ } } - and("an insecure DataBrokerConnector with a READ_WRITE_ALL_VALUES_ONLY JWT") { - val jwtFileStream = JwtType.READ_WRITE_ALL_VALUES_ONLY.asInputStream() - val dataBrokerConnector = dataBrokerConnectorProvider.createInsecure(jwtFileStream = jwtFileStream) + and("a secure DataBrokerConnector with a READ_WRITE_ALL_VALUES_ONLY JWT") { + val jwtFile = JwtType.READ_WRITE_ALL_VALUES_ONLY + val dataBrokerConnector = dataBrokerConnectorProvider.createSecure( + jwtFileStream = jwtFile.asInputStream(), + ) and("a successfully established connection") { val connection = dataBrokerConnector.connect() @@ -157,20 +186,71 @@ class DataBrokerConnectorAuthenticationTest : BehaviorSpec({ } } } - } -}) -// The tokens provided here might need to be updated irregularly -// see: https://github.com/eclipse/kuksa.val/tree/master/jwt -// The tokens only work when the Databroker is started using the correct public key: jwt.key.pub -enum class JwtType(private val fileName: String) { - READ_WRITE_ALL("actuate-provide-all.token"), // ACTUATOR_TARGET and VALUE - READ_WRITE_ALL_VALUES_ONLY("provide-all.token"), // VALUE - READ_ALL("read-all.token"), - ; - - fun asInputStream(): InputStream { - val resourceFile = TestResourceFile(fileName) - return resourceFile.inputStream() + and("a secure DataBrokerConnector with no JWT") { + val dataBrokerConnector = dataBrokerConnectorProvider.createSecure( + jwtFileStream = null, + ) + + `when`("Trying to connect") { + val result = runCatching { + dataBrokerConnector.connect() + } + + then("The connection should be successful") { + result.getOrNull() shouldNotBe null + } + + val connection = result.getOrNull()!! + + `when`("Reading the VALUE of Vehicle.Speed") { + val fetchRequest = FetchRequest(speedVssPath) + val fetchResult = runCatching { + connection.fetch(fetchRequest) + } + + then("An error should occur") { + val exception = fetchResult.exceptionOrNull() + exception shouldNotBe null + exception shouldBe instanceOf(DataBrokerException::class) + exception!!.message shouldContain "UNAUTHENTICATED" + } + } + + `when`("Writing the VALUE of Vehicle.Speed") { + val nextFloat = random.nextFloat() * 100F + val datapoint = Types.Datapoint.newBuilder().setFloat(nextFloat).build() + val updateRequest = UpdateRequest(speedVssPath, datapoint) + + val updateResult = runCatching { + connection.update(updateRequest) + } + + then("An error should occur") { + val exception = updateResult.exceptionOrNull() + exception shouldNotBe null + exception shouldBe instanceOf(DataBrokerException::class) + exception!!.message shouldContain "UNAUTHENTICATED" + } + } + + `when`("Subscribing to the VALUE of Vehicle.Speed") { + val subscribeRequest = SubscribeRequest(speedVssPath) + val vssPathListener = FriendlyVssPathListener() + + connection.subscribe(subscribeRequest, vssPathListener) + + then("An error should occur") { + eventually(eventuallyConfiguration) { + vssPathListener.errors.size shouldBe 1 + + val exception = vssPathListener.errors.first() + exception shouldBe instanceOf(StatusRuntimeException::class) + exception.message shouldContain "UNAUTHENTICATED" + } + } + } + } + } } -} +}) diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/JwtType.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/JwtType.kt new file mode 100644 index 00000000..e87f9d65 --- /dev/null +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/authentication/JwtType.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.eclipse.kuksa.connectivity.authentication + +import org.eclipse.kuksa.test.TestResourceFile +import java.io.InputStream + +// The tokens provided here might need to be updated irregularly +// see: https://github.com/eclipse/kuksa.val/tree/master/jwt +// The tokens only work when the Databroker is started using the correct public key: jwt.key.pub +enum class JwtType(private val fileName: String) { + READ_WRITE_ALL("authentication/actuate-provide-all.token"), // ACTUATOR_TARGET and VALUE + READ_WRITE_ALL_VALUES_ONLY("authentication/provide-all.token"), // VALUE + READ_ALL("authentication/read-all.token"), + ; + + fun asInputStream(): InputStream { + val resourceFile = TestResourceFile(fileName) + return resourceFile.inputStream() + } +} diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt index 4c754649..eafe5055 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,14 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 + * */ package org.eclipse.kuksa.connectivity.databroker import java.util.concurrent.TimeUnit -object DataBrokerConfig { - const val HOST = "127.0.0.1" - const val PORT = 55556 +const val DATABROKER_HOST = "127.0.0.1" - // low timeout should be okay, since we are testing against a local service - const val TIMEOUT_SECONDS = 3L - val TIMEOUT_UNIT = TimeUnit.SECONDS -} +const val DATABROKER_TIMEOUT_SECONDS = 5L +val DATABROKER_TIMEOUT_UNIT = TimeUnit.SECONDS diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt index b4644d32..78e5f4ff 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectionTest.kt @@ -24,78 +24,91 @@ import io.kotest.assertions.nondeterministic.eventually import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import io.kotest.matchers.string.shouldContain -import io.mockk.clearMocks import io.mockk.every import io.mockk.mockk -import io.mockk.slot import io.mockk.verify -import kotlinx.coroutines.runBlocking +import org.eclipse.kuksa.connectivity.databroker.docker.DataBrokerDockerContainer +import org.eclipse.kuksa.connectivity.databroker.docker.InsecureDataBrokerDockerContainer import org.eclipse.kuksa.connectivity.databroker.listener.DisconnectListener -import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.connectivity.databroker.request.FetchRequest import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest +import org.eclipse.kuksa.extensions.updateRandomFloatValue import org.eclipse.kuksa.mocking.FriendlyVssNodeListener -import org.eclipse.kuksa.proto.v1.KuksaValV1 +import org.eclipse.kuksa.mocking.FriendlyVssPathListener import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.test.kotest.DefaultDatabroker +import org.eclipse.kuksa.test.extension.equals +import org.eclipse.kuksa.test.kotest.Insecure +import org.eclipse.kuksa.test.kotest.InsecureDataBroker import org.eclipse.kuksa.test.kotest.Integration +import org.eclipse.kuksa.test.kotest.eventuallyConfiguration import org.eclipse.kuksa.vssNode.VssDriver import org.junit.jupiter.api.Assertions import kotlin.random.Random -import kotlin.time.Duration.Companion.seconds class DataBrokerConnectionTest : BehaviorSpec({ - tags(Integration, DefaultDatabroker) + tags(Integration, Insecure, InsecureDataBroker) + + var databrokerContainer: DataBrokerDockerContainer? = null + beforeSpec { + databrokerContainer = InsecureDataBrokerDockerContainer() + .apply { + start() + } + } + + afterSpec { + databrokerContainer?.stop() + } given("A successfully established connection to the DataBroker") { - val dataBrokerConnection = connectToDataBrokerBlocking() + val dataBrokerConnectorProvider = DataBrokerConnectorProvider() + val connector = dataBrokerConnectorProvider.createInsecure() + val dataBrokerConnection = connector.connect() + + val dataBrokerTransporter = DataBrokerTransporter(dataBrokerConnectorProvider.managedChannel) and("A request with a valid VSS Path") { val vssPath = "Vehicle.Acceleration.Lateral" val field = Types.Field.FIELD_VALUE - val subscribeRequest = SubscribeRequest(vssPath, field) + val initialValue = dataBrokerTransporter.updateRandomFloatValue(vssPath) + + val subscribeRequest = SubscribeRequest(vssPath, field) `when`("Subscribing to the VSS path") { - val vssPathListener = mockk(relaxed = true) + val vssPathListener = FriendlyVssPathListener() dataBrokerConnection.subscribe(subscribeRequest, vssPathListener) then("The #onEntryChanged method is triggered") { - val capturingSlot = slot>() - verify(timeout = 100L) { - vssPathListener.onEntryChanged(capture(capturingSlot)) + eventually(eventuallyConfiguration) { + vssPathListener.updates.flatten().count { + val entry = it.entry + val value = entry.value + entry.path == vssPath && value.float.equals(initialValue, 0.0001f) + } shouldBe 1 } - - val entryUpdates = capturingSlot.captured - entryUpdates.size shouldBe 1 - entryUpdates[0].entry.path shouldBe vssPath } `when`("The observed VSS path changes") { - clearMocks(vssPathListener) + vssPathListener.reset() val random = Random(System.currentTimeMillis()) - val newValue = random.nextFloat() - val datapoint = Types.Datapoint.newBuilder().setFloat(newValue).build() + val updatedValue = random.nextFloat() + val datapoint = Types.Datapoint.newBuilder().setFloat(updatedValue).build() val updateRequest = UpdateRequest(vssPath, datapoint, field) dataBrokerConnection.update(updateRequest) then("The #onEntryChanged callback is triggered with the new value") { - val capturingSlot = slot>() - - verify(timeout = 100) { - vssPathListener.onEntryChanged(capture(capturingSlot)) + eventually(eventuallyConfiguration) { + vssPathListener.updates.flatten().count { + val entry = it.entry + val value = entry.value + entry.path == vssPath && value.float.equals(updatedValue, 0.0001f) + } shouldBe 1 } - - val entryUpdates = capturingSlot.captured - val capturedDatapoint = entryUpdates[0].entry.value - val float = capturedDatapoint.float - - Assertions.assertEquals(newValue, float, 0.0001f) } } } @@ -193,7 +206,7 @@ class DataBrokerConnectionTest : BehaviorSpec({ dataBrokerConnection.subscribe(subscribeRequest, listener = vssNodeListener) then("The #onNodeChanged method is triggered") { - eventually(1.seconds) { + eventually(eventuallyConfiguration) { vssNodeListener.updatedVssNodes.size shouldBe 1 } } @@ -206,14 +219,12 @@ class DataBrokerConnectionTest : BehaviorSpec({ dataBrokerConnection.update(updateRequest) then("Every child node has been updated with the correct value") { - eventually(1.seconds) { - vssNodeListener.updatedVssNodes.size shouldBe 2 + eventually(eventuallyConfiguration) { + vssNodeListener.updatedVssNodes.count { + val heartRate = it.heartRate + heartRate.value == newHeartRateValue + } shouldBe 1 } - - val updatedDriver = vssNodeListener.updatedVssNodes.last() - val heartRate = updatedDriver.heartRate - - heartRate.value shouldBe newHeartRateValue } } @@ -225,14 +236,12 @@ class DataBrokerConnectionTest : BehaviorSpec({ dataBrokerConnection.update(updateRequest) then("The subscribed vssNode should be updated") { - eventually(1.seconds) { - vssNodeListener.updatedVssNodes.size shouldBe 3 + eventually(eventuallyConfiguration) { + vssNodeListener.updatedVssNodes.count { + val heartRate = it.heartRate + heartRate.value == newHeartRateValue + } shouldBe 1 } - - val updatedDriver = vssNodeListener.updatedVssNodes.last() - val heartRate = updatedDriver.heartRate - - heartRate.value shouldBe newHeartRateValue } } } @@ -242,15 +251,16 @@ class DataBrokerConnectionTest : BehaviorSpec({ val invalidVssPath = "Vehicle.Some.Unknown.Path" `when`("Trying to subscribe to the INVALID VSS path") { - val vssPathListener = mockk(relaxed = true) + val vssPathListener = FriendlyVssPathListener() val subscribeRequest = SubscribeRequest(invalidVssPath) dataBrokerConnection.subscribe(subscribeRequest, vssPathListener) then("The VssPathListener#onError method should be triggered with 'NOT_FOUND' (Path not found)") { - val capturingSlot = slot() - verify(timeout = 100L) { vssPathListener.onError(capture(capturingSlot)) } - val capturedThrowable = capturingSlot.captured - capturedThrowable.message shouldContain "NOT_FOUND" + eventually(eventuallyConfiguration) { + vssPathListener.errors.count { + it.message?.contains("NOT_FOUND") == true + } shouldBe 1 + } } } @@ -331,20 +341,3 @@ private fun createRandomIntDatapoint(): Types.Datapoint { val newValue = random.nextInt() return Types.Datapoint.newBuilder().setInt32(newValue).build() } - -private fun connectToDataBrokerBlocking(): DataBrokerConnection { - var connection: DataBrokerConnection - - runBlocking { - val connector = DataBrokerConnectorProvider().createInsecure() - try { - connection = connector.connect() - } catch (ignored: DataBrokerException) { - val errorMessage = "Could not establish a connection to the DataBroker. " + - "Check if the DataBroker is running and correctly configured in DataBrokerConfig." - throw IllegalStateException(errorMessage) - } - } - - return connection -} diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt index 53df36bb..e7c39ad4 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorProvider.kt @@ -24,15 +24,17 @@ import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder import io.grpc.TlsChannelCredentials import org.eclipse.kuksa.connectivity.authentication.JsonWebToken +import org.eclipse.kuksa.connectivity.authentication.JwtType import org.eclipse.kuksa.model.TimeoutConfig +import org.eclipse.kuksa.test.TestResourceFile import java.io.IOException import java.io.InputStream class DataBrokerConnectorProvider { lateinit var managedChannel: ManagedChannel fun createInsecure( - host: String = DataBrokerConfig.HOST, - port: Int = DataBrokerConfig.PORT, + host: String = DATABROKER_HOST, + port: Int = DATABROKER_PORT, jwtFileStream: InputStream? = null, ): DataBrokerConnector { managedChannel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build() @@ -46,16 +48,16 @@ class DataBrokerConnectorProvider { managedChannel, jsonWebToken, ).apply { - timeoutConfig = TimeoutConfig(DataBrokerConfig.TIMEOUT_SECONDS, DataBrokerConfig.TIMEOUT_UNIT) + timeoutConfig = TimeoutConfig(DATABROKER_TIMEOUT_SECONDS, DATABROKER_TIMEOUT_UNIT) } } fun createSecure( - host: String = DataBrokerConfig.HOST, - port: Int = DataBrokerConfig.PORT, - rootCertFileStream: InputStream, + host: String = DATABROKER_HOST, + port: Int = DATABROKER_PORT, overrideAuthority: String = "", - jwtFileStream: InputStream? = null, + rootCertFileStream: InputStream = TestResourceFile("tls/CA.pem").inputStream(), + jwtFileStream: InputStream? = JwtType.READ_WRITE_ALL.asInputStream(), ): DataBrokerConnector { val tlsCredentials: ChannelCredentials try { @@ -86,7 +88,7 @@ class DataBrokerConnectorProvider { managedChannel, jsonWebToken, ).apply { - timeoutConfig = TimeoutConfig(DataBrokerConfig.TIMEOUT_SECONDS, DataBrokerConfig.TIMEOUT_UNIT) + timeoutConfig = TimeoutConfig(DATABROKER_TIMEOUT_SECONDS, DATABROKER_TIMEOUT_UNIT) } } } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt index 0ff03f1f..02b60192 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorSecureTest.kt @@ -19,23 +19,40 @@ package org.eclipse.kuksa.connectivity.databroker import io.kotest.core.spec.style.BehaviorSpec +import org.eclipse.kuksa.connectivity.databroker.docker.DataBrokerDockerContainer +import org.eclipse.kuksa.connectivity.databroker.docker.SecureDataBrokerDockerContainer import org.eclipse.kuksa.test.TestResourceFile -import org.eclipse.kuksa.test.kotest.CustomDatabroker import org.eclipse.kuksa.test.kotest.Integration import org.eclipse.kuksa.test.kotest.Secure +import org.eclipse.kuksa.test.kotest.SecureDataBroker +import org.eclipse.kuksa.test.kotest.Tls import org.junit.jupiter.api.Assertions // run command: ./gradlew clean test -Dkotest.tags="Secure" class DataBrokerConnectorSecureTest : BehaviorSpec({ - tags(Integration, Secure, CustomDatabroker) + tags(Integration, Secure, Tls, SecureDataBroker) + + var databrokerContainer: DataBrokerDockerContainer? = null + beforeSpec { + databrokerContainer = SecureDataBrokerDockerContainer() + .apply { + start() + } + } + + afterSpec { + databrokerContainer?.stop() + } given("A DataBrokerConnectorProvider") { val dataBrokerConnectorProvider = DataBrokerConnectorProvider() and("a secure DataBrokerConnector with valid Host, Port and TLS certificate") { - val certificate = TestResourceFile("CA.pem") - val dataBrokerConnector = - dataBrokerConnectorProvider.createSecure(rootCertFileStream = certificate.inputStream()) + val tlsCertificate = TestResourceFile("tls/CA.pem") + + val dataBrokerConnector = dataBrokerConnectorProvider.createSecure( + rootCertFileStream = tlsCertificate.inputStream(), + ) `when`("Trying to establish a secure connection") { val connection = dataBrokerConnector.connect() diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt index 63e30827..82ce9e59 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnectorTest.kt @@ -21,12 +21,26 @@ package org.eclipse.kuksa.connectivity.databroker import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldNotBe -import org.eclipse.kuksa.test.kotest.DefaultDatabroker +import org.eclipse.kuksa.connectivity.databroker.docker.DataBrokerDockerContainer +import org.eclipse.kuksa.connectivity.databroker.docker.InsecureDataBrokerDockerContainer import org.eclipse.kuksa.test.kotest.Insecure +import org.eclipse.kuksa.test.kotest.InsecureDataBroker import org.eclipse.kuksa.test.kotest.Integration class DataBrokerConnectorTest : BehaviorSpec({ - tags(Integration, Insecure, DefaultDatabroker) + tags(Integration, Insecure, InsecureDataBroker) + + var databrokerContainer: DataBrokerDockerContainer? = null + beforeSpec { + databrokerContainer = InsecureDataBrokerDockerContainer() + .apply { + start() + } + } + + afterSpec { + databrokerContainer?.stop() + } given("A DataBrokerConnectorProvider") { val dataBrokerConnectorProvider = DataBrokerConnectorProvider() @@ -53,7 +67,7 @@ class DataBrokerConnectorTest : BehaviorSpec({ dataBrokerConnector.connect() } - then("It should throw an exception") { + then("It should throw a DataBrokerException") { exception shouldNotBe null } } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt index 21b6cdeb..733b868b 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerTransporterTest.kt @@ -19,23 +19,40 @@ package org.eclipse.kuksa.connectivity.databroker import io.grpc.ManagedChannelBuilder +import io.kotest.assertions.nondeterministic.eventually import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.types.instanceOf import io.mockk.mockk import io.mockk.verify +import org.eclipse.kuksa.connectivity.databroker.docker.DataBrokerDockerContainer +import org.eclipse.kuksa.connectivity.databroker.docker.InsecureDataBrokerDockerContainer import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.extensions.updateRandomFloatValue +import org.eclipse.kuksa.mocking.FriendlyVssPathListener import org.eclipse.kuksa.proto.v1.KuksaValV1 import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.test.kotest.DefaultDatabroker import org.eclipse.kuksa.test.kotest.Insecure +import org.eclipse.kuksa.test.kotest.InsecureDataBroker import org.eclipse.kuksa.test.kotest.Integration +import org.eclipse.kuksa.test.kotest.eventuallyConfiguration import kotlin.random.Random class DataBrokerTransporterTest : BehaviorSpec({ - tags(Integration, Insecure, DefaultDatabroker) + tags(Integration, Insecure, InsecureDataBroker) + + var databrokerContainer: DataBrokerDockerContainer? = null + beforeSpec { + databrokerContainer = InsecureDataBrokerDockerContainer() + .apply { + start() + } + } + + afterSpec { + databrokerContainer?.stop() + } given("An active Connection to the DataBroker") { val dataBrokerConnectorProvider = DataBrokerConnectorProvider() @@ -121,12 +138,14 @@ class DataBrokerTransporterTest : BehaviorSpec({ Types.Field.FIELD_VALUE, ) - val vssPathListener = mockk(relaxed = true) + val vssPathListener = FriendlyVssPathListener() subscription.listeners.register(vssPathListener) then("An Error should be triggered") { - verify(timeout = 100L) { - vssPathListener.onError(any()) + eventually(eventuallyConfiguration) { + vssPathListener.errors.count { + it.message?.contains("NOT_FOUND") == true + } shouldBe 1 } } } diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/DataBrokerDockerContainer.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/DataBrokerDockerContainer.kt new file mode 100644 index 00000000..49f05b22 --- /dev/null +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/DataBrokerDockerContainer.kt @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.eclipse.kuksa.connectivity.databroker.docker + +import com.github.dockerjava.api.DockerClient +import com.github.dockerjava.api.command.CreateContainerResponse +import com.github.dockerjava.api.command.PullImageResultCallback +import com.github.dockerjava.api.command.WaitContainerResultCallback +import com.github.dockerjava.api.exception.NotModifiedException +import com.github.dockerjava.api.model.ExposedPort +import com.github.dockerjava.api.model.HostConfig +import com.github.dockerjava.api.model.InternetProtocol +import com.github.dockerjava.api.model.PortBinding +import com.github.dockerjava.api.model.Ports +import com.github.dockerjava.core.DefaultDockerClientConfig +import com.github.dockerjava.core.DockerClientImpl +import com.github.dockerjava.httpclient5.ApacheDockerHttpClient +import org.eclipse.kuksa.connectivity.databroker.DATABROKER_TIMEOUT_SECONDS +import java.time.Duration +import java.util.concurrent.TimeUnit + +private const val KEY_PROPERTY_DATABROKER_TAG = "databroker.tag" +private const val KEY_ENV_DATABROKER_TAG = "DATABROKER_TAG" +private const val DEFAULT_DATABROKER_TAG = "master" + +/** + * Starts and stops the Databroker Docker Container. Per default the image with the master tag is pulled and started. + * The version of the image can be influenced by either a System Property with value "databroker.tag" or an + * Environment Variable with value "DATABROKER_TAG". + *``` + * Samples + * + * System Property: + * ./gradlew clean build -Ddatabroker.tag="0.4.1" + * + * + * Environment Variable: + * export DATABROKER_TAG="0.4.1" + * ./gradlew clean build + * + * or + * + * DATABROKER_TAG="0.4.1" ./gradlew clean build + * ``` + */ +abstract class DataBrokerDockerContainer( + protected val containerName: String, + protected val port: Int, +) { + protected open val hostConfig: HostConfig = HostConfig.newHostConfig() + .withNetworkMode("host") + .withAutoRemove(true) + .withPortBindings( + PortBinding( + Ports.Binding("0.0.0.0", ""), + ExposedPort(port, InternetProtocol.TCP), + ), + ) + + protected val repository = "ghcr.io/eclipse/kuksa.val/databroker" + + private val databrokerTag = System.getProperty(KEY_PROPERTY_DATABROKER_TAG) + ?: System.getenv(KEY_ENV_DATABROKER_TAG) + ?: DEFAULT_DATABROKER_TAG + + protected val dockerClient: DockerClient + + init { + val config = DefaultDockerClientConfig.createDefaultConfigBuilder().build() + + val timeout = Duration.ofSeconds(DATABROKER_TIMEOUT_SECONDS) + val dockerHttpClient = ApacheDockerHttpClient.Builder() + .dockerHost(config.dockerHost) + .sslConfig(config.sslConfig) + .connectionTimeout(timeout) + .responseTimeout(timeout) + .build() + + dockerClient = DockerClientImpl.getInstance(config, dockerHttpClient) + } + + fun start() { + stop() + + pullImage(databrokerTag) + val databrokerContainer = createContainer(databrokerTag) + startContainer(databrokerContainer.id) + } + + fun stop() { + val databrokerNames = listOf(containerName) + val dockerContainers = dockerClient.listContainersCmd() + .withNameFilter(databrokerNames) + .exec() + + dockerContainers.forEach { container -> + try { + dockerClient.stopContainerCmd(container.id).exec() + } catch (_: NotModifiedException) { + // thrown when a container is already stopped + } + } + } + + private fun pullImage(tag: String) { + dockerClient.pullImageCmd(repository) + .withTag(tag) + .exec(PullImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS) + } + + protected abstract fun createContainer(tag: String): CreateContainerResponse + + private fun startContainer(containerId: String) { + try { + dockerClient.startContainerCmd(containerId).exec() + + dockerClient.waitContainerCmd(containerId) + .exec(WaitContainerResultCallback()) + .awaitCompletion(5, TimeUnit.SECONDS) + } catch (_: NotModifiedException) { + // thrown when a container is already started + } + } +} diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/InsecureDataBrokerDockerContainer.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/InsecureDataBrokerDockerContainer.kt new file mode 100644 index 00000000..c6e9ab1e --- /dev/null +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/InsecureDataBrokerDockerContainer.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.eclipse.kuksa.connectivity.databroker.docker + +import com.github.dockerjava.api.command.CreateContainerResponse +import org.eclipse.kuksa.connectivity.databroker.DATABROKER_CONTAINER_NAME +import org.eclipse.kuksa.connectivity.databroker.DATABROKER_PORT + +// no tls, no authentication +class InsecureDataBrokerDockerContainer( + containerName: String = DATABROKER_CONTAINER_NAME, + port: Int = DATABROKER_PORT, +) : DataBrokerDockerContainer(containerName, port) { + + @Suppress("ArgumentListWrapping", "ktlint:standard:argument-list-wrapping") // better key-value pair readability + override fun createContainer(tag: String): CreateContainerResponse { + return dockerClient.createContainerCmd("$repository:$tag") + .withName(containerName) + .withHostConfig(hostConfig) + .withCmd( + "--port", "$port", + "--insecure", + ) + .exec() + } +} diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/SecureDataBrokerDockerContainer.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/SecureDataBrokerDockerContainer.kt new file mode 100644 index 00000000..733c0121 --- /dev/null +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/docker/SecureDataBrokerDockerContainer.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.eclipse.kuksa.connectivity.databroker.docker + +import com.github.dockerjava.api.command.CreateContainerResponse +import com.github.dockerjava.api.model.AccessMode +import com.github.dockerjava.api.model.Bind +import com.github.dockerjava.api.model.HostConfig +import com.github.dockerjava.api.model.Volume +import org.eclipse.kuksa.connectivity.databroker.DATABROKER_CONTAINER_NAME +import org.eclipse.kuksa.connectivity.databroker.DATABROKER_PORT +import org.eclipse.kuksa.test.TestResourceFile + +// tls enabled, authentication enabled +class SecureDataBrokerDockerContainer( + containerName: String = DATABROKER_CONTAINER_NAME, + port: Int = DATABROKER_PORT, +) : DataBrokerDockerContainer(containerName, port) { + + private val authenticationFolder = TestResourceFile("authentication").toString() + private val authenticationMount = "/resources/authentication" + + private val tlsFolder = TestResourceFile("tls").toString() + private val tlsMount = "/resources/tls" + + override val hostConfig: HostConfig = super.hostConfig + .withBinds( + Bind(tlsFolder, Volume(tlsMount), AccessMode.ro), + Bind(authenticationFolder, Volume(authenticationMount), AccessMode.ro), + ) + + @Suppress("ArgumentListWrapping", "ktlint:standard:argument-list-wrapping") // better key-value pair readability + override fun createContainer(tag: String): CreateContainerResponse { + return dockerClient.createContainerCmd("$repository:$tag") + .withName(containerName) + .withHostConfig(hostConfig) + .withCmd( + "--port", "$port", + "--tls-cert", "$tlsMount/Server.pem", + "--tls-private-key", "$tlsMount/Server.key", + "--jwt-public-key", "$authenticationMount/jwt.key.pub", + ) + .exec() + } +} diff --git a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt index 3c174221..d065c32a 100644 --- a/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt +++ b/kuksa-sdk/src/test/kotlin/org/eclipse/kuksa/connectivity/databroker/subscription/DataBrokerSubscriberTest.kt @@ -22,13 +22,15 @@ import io.kotest.assertions.nondeterministic.continually import io.kotest.assertions.nondeterministic.eventually import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import io.mockk.clearMocks import io.mockk.every import io.mockk.mockk import io.mockk.verify -import kotlinx.coroutines.delay import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnectorProvider import org.eclipse.kuksa.connectivity.databroker.DataBrokerTransporter +import org.eclipse.kuksa.connectivity.databroker.docker.DataBrokerDockerContainer +import org.eclipse.kuksa.connectivity.databroker.docker.InsecureDataBrokerDockerContainer import org.eclipse.kuksa.connectivity.databroker.listener.VssPathListener import org.eclipse.kuksa.extensions.toggleBoolean import org.eclipse.kuksa.extensions.updateRandomFloatValue @@ -38,15 +40,27 @@ import org.eclipse.kuksa.mocking.FriendlyVssPathListener import org.eclipse.kuksa.pattern.listener.MultiListener import org.eclipse.kuksa.pattern.listener.count import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.test.kotest.DefaultDatabroker import org.eclipse.kuksa.test.kotest.Insecure +import org.eclipse.kuksa.test.kotest.InsecureDataBroker import org.eclipse.kuksa.test.kotest.Integration +import org.eclipse.kuksa.test.kotest.continuallyConfiguration +import org.eclipse.kuksa.test.kotest.eventuallyConfiguration import org.eclipse.kuksa.vssNode.VssDriver -import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.Duration.Companion.seconds class DataBrokerSubscriberTest : BehaviorSpec({ - tags(Integration, Insecure, DefaultDatabroker) + tags(Integration, Insecure, InsecureDataBroker) + + var databrokerContainer: DataBrokerDockerContainer? = null + beforeSpec { + databrokerContainer = InsecureDataBrokerDockerContainer() + .apply { + start() + } + } + + afterSpec { + databrokerContainer?.stop() + } given("An active Connection to the DataBroker") { val dataBrokerConnectorProvider = DataBrokerConnectorProvider() @@ -64,15 +78,18 @@ class DataBrokerSubscriberTest : BehaviorSpec({ val vssPathListener = FriendlyVssPathListener() classUnderTest.subscribe(vssPath, fieldValue, vssPathListener) - then("The VssPathListener should send out ONE update containing ALL children") { - eventually(1.seconds) { - vssPathListener.updates.size shouldBe 1 - } + then("The VssPathListener should send out an update containing ALL children") { + eventually(eventuallyConfiguration) { + val foundUpdate = vssPathListener.updates.find { entryUpdates -> + entryUpdates.size == 3 // all children: IsEnabled, IsEngaged, IsError + } - val entryUpdates = vssPathListener.updates[0] - vssPathListener.updates.size shouldBe 1 // ONE update - entryUpdates.size shouldBe 3 // all children: IsEnabled, IsEngaged, IsError - entryUpdates.all { it.entry.path.startsWith(vssPath) } shouldBe true + foundUpdate shouldNotBe null + foundUpdate?.size shouldBe 3 + foundUpdate?.count { it.entry.path.endsWith(".IsEnabled") } shouldBe 1 + foundUpdate?.count { it.entry.path.endsWith(".IsEngaged") } shouldBe 1 + foundUpdate?.count { it.entry.path.endsWith(".IsError") } shouldBe 1 + } } `when`("Any child changes it's value") { @@ -84,23 +101,21 @@ class DataBrokerSubscriberTest : BehaviorSpec({ val newValueIsEngaged = databrokerTransporter.toggleBoolean(vssPathIsEngaged) then("The VssPathListener should be notified about it") { - eventually(1.seconds) { - vssPathListener.updates.size shouldBe 2 + eventually(eventuallyConfiguration) { + val entryUpdates = vssPathListener.updates.flatten() + entryUpdates.count { + val path = it.entry.path + val entry = it.entry + val value = entry.value + path == vssPathIsError && value.bool == newValueIsError + } shouldBe 1 + entryUpdates.count { + val path = it.entry.path + val entry = it.entry + val value = entry.value + path == vssPathIsEngaged && value.bool == newValueIsEngaged + } shouldBe 1 } - - val entryUpdates = vssPathListener.updates.flatten() - entryUpdates.count { - val path = it.entry.path - val entry = it.entry - val value = entry.value - path == vssPathIsError && value.bool == newValueIsError - } shouldBe 1 - entryUpdates.count { - val path = it.entry.path - val entry = it.entry - val value = entry.value - path == vssPathIsEngaged && value.bool == newValueIsEngaged - } shouldBe 1 } } } @@ -115,21 +130,19 @@ class DataBrokerSubscriberTest : BehaviorSpec({ val updateRandomFloatValue = databrokerTransporter.updateRandomFloatValue(vssPath) then("The VssPathListener is notified about the change") { - eventually(1.seconds) { - vssPathListener.updates.size shouldBe 2 + eventually(eventuallyConfiguration) { + vssPathListener.updates.flatten() + .count { + val dataEntry = it.entry + val datapoint = dataEntry.value + dataEntry.path == vssPath && datapoint.float == updateRandomFloatValue + } shouldBe 1 } - vssPathListener.updates.flatten() - .count { - val dataEntry = it.entry - val datapoint = dataEntry.value - dataEntry.path == vssPath && datapoint.float == updateRandomFloatValue - } shouldBe 1 - - vssPathListener.updates.clear() } } `when`("Subscribing the same VssPathListener to a different vssPath") { + vssPathListener.reset() val otherVssPath = "Vehicle.ADAS.CruiseControl.SpeedSet" classUnderTest.subscribe(otherVssPath, fieldValue, vssPathListener) @@ -139,25 +152,22 @@ class DataBrokerSubscriberTest : BehaviorSpec({ val updatedValueOtherVssPath = databrokerTransporter.updateRandomFloatValue(otherVssPath) then("The Observer is notified about both changes") { - eventually(1.seconds) { - vssPathListener.updates.size shouldBe 3 // 1 from subscribe(otherVssPath) + 2 updates + eventually(eventuallyConfiguration) { + val entryUpdates = vssPathListener.updates.flatten() + entryUpdates + .count { + val path = it.entry.path + val value = it.entry.value + path == vssPath && value.float == updatedValueVssPath + } shouldBe 1 + + entryUpdates + .count { + val path = it.entry.path + val value = it.entry.value + path == otherVssPath && value.float == updatedValueOtherVssPath + } shouldBe 1 } - - val entryUpdates = vssPathListener.updates.flatten() - entryUpdates - .count { - val path = it.entry.path - val entry = it.entry - val value = entry.value - path == vssPath && value.float == updatedValueVssPath - } shouldBe 1 - entryUpdates - .count { - val path = it.entry.path - val entry = it.entry - val value = entry.value - path == otherVssPath && value.float == updatedValueOtherVssPath - } shouldBe 1 } } } @@ -176,13 +186,14 @@ class DataBrokerSubscriberTest : BehaviorSpec({ then("Each VssPathListener is only notified once") { friendlyVssPathListeners.forEach { listener -> - eventually(1.seconds) { - listener.updates.size shouldBe 2 + eventually(eventuallyConfiguration) { + listener.updates.flatten() + .count { + val path = it.entry.path + val value = it.entry.value + path == vssPath && value.float == randomFloatValue + } shouldBe 1 } - - val count = listener.updates - .count { it[0].entry.value.float == randomFloatValue } - count shouldBe 1 } } } @@ -194,10 +205,9 @@ class DataBrokerSubscriberTest : BehaviorSpec({ and("When the FIELD_VALUE of Vehicle.Speed is updated") { databrokerTransporter.updateRandomFloatValue(vssPath) - delay(100) then("The VssPathListener is not notified") { - continually(100.milliseconds) { + continually(continuallyConfiguration) { vssPathListener.updates.size shouldBe 0 } } @@ -216,13 +226,15 @@ class DataBrokerSubscriberTest : BehaviorSpec({ val randomFloatValue = databrokerTransporter.updateRandomFloatValue(vssPath) then("The VssPathListener is only notified once") { - eventually(1.seconds) { - vssPathListener.updates.size shouldBe 2 + eventually(eventuallyConfiguration) { + val count = vssPathListener.updates.flatten() + .count { + val path = it.entry.path + val value = it.entry.value + path == vssPath && value.float == randomFloatValue + } + count shouldBe 1 } - - val count = vssPathListener.updates - .count { it[0].entry.value.float == randomFloatValue } - count shouldBe 1 } } } @@ -242,13 +254,13 @@ class DataBrokerSubscriberTest : BehaviorSpec({ databrokerTransporter.updateRandomUint32Value(vssHeartRate.vssPath) then("The Observer should be triggered") { - eventually(1.seconds) { - friendlyVssNodeListener.updatedVssNodes.size shouldBe 2 + eventually(eventuallyConfiguration) { + friendlyVssNodeListener.updatedVssNodes.count { + val vssPath = it.vssPath + val value = it.value + vssPath == vssHeartRate.vssPath && value == randomIntValue + } shouldBe 1 } - - val count = friendlyVssNodeListener.updatedVssNodes - .count { it.value == randomIntValue } - count shouldBe 1 } } } @@ -271,12 +283,13 @@ class DataBrokerSubscriberTest : BehaviorSpec({ databrokerTransporter.updateRandomUint32Value(vssHeartRate.vssPath) then("The Observer is only notified once") { - eventually(1.seconds) { - nodeListenerMock.updatedVssNodes.size shouldBe 2 + eventually(eventuallyConfiguration) { + nodeListenerMock.updatedVssNodes.count { + val vssPath = it.vssPath + val value = it.value + vssPath == vssHeartRate.vssPath && value == randomIntValue + } shouldBe 1 } - - val count = nodeListenerMock.updatedVssNodes.count { it.value == randomIntValue } - count shouldBe 1 } } } diff --git a/kuksa-sdk/src/test/resources/actuate-provide-all.token b/kuksa-sdk/src/test/resources/authentication/actuate-provide-all.token similarity index 100% rename from kuksa-sdk/src/test/resources/actuate-provide-all.token rename to kuksa-sdk/src/test/resources/authentication/actuate-provide-all.token diff --git a/kuksa-sdk/src/test/resources/authentication/jwt.key.pub b/kuksa-sdk/src/test/resources/authentication/jwt.key.pub new file mode 100644 index 00000000..d9f78534 --- /dev/null +++ b/kuksa-sdk/src/test/resources/authentication/jwt.key.pub @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6ScE9EKXEWVyYhzfhfvg ++LC8NseiuEjfrdFx3HKkb31bRw/SeS0Rye0KDP7uzffwreKf6wWYGxVUPYmyKC7j +Pji5MpDBGM9r3pIZSvPUFdpTE5TiRHFBxWbqPSYt954BTLq4rMu/W+oq5Pdfnugb +voYpLf0dclBl1g9KyszkDnItz3TYbWhGMbsUSfyeSPzH0IADzLoifxbc5mgiR73N +CA/4yNSpfLoqWgQ2vdTM1182sMSmxfqSgMzIMUX/tiaXGdkoKITF1sULlLyWfTo9 +79XRZ0hmUwvfzr3OjMZNoClpYSVbKY+vtxHyux9KOOtv9lPMsgYIaPXvisrsneDZ +fCS0afOfjgR96uHIe2UPSGAXru3yGziqEfpRZoxsgXaOe905ordLD5bSX14xkN7N +Cz7rxDLlxPQyxp4Vhog7p/QeUyydBpZjq2bAE5GAJtiu+XGvG8RypzJFKFQwMNsw +g1BoZVD0mb0MtU8KQmHcZIfY0FVer/CR0mUjfl1rHbtoJB+RY03lQvYNAD04ibAG +NI1RhlTziu35Xo6NDEgs9hVs9k3WrtF+ZUxhivWmP2VXhWruRakVkC1NzKGh54e5 +/KlluFbBNpWgvWZqzWo9Jr7/fzHtR0Q0IZwkxh+Vd/bUZya1uLKqP+sTcc+aTHbn +AEiqOjPq0D6X45wCzIwjILUCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/kuksa-sdk/src/test/resources/provide-all.token b/kuksa-sdk/src/test/resources/authentication/provide-all.token similarity index 100% rename from kuksa-sdk/src/test/resources/provide-all.token rename to kuksa-sdk/src/test/resources/authentication/provide-all.token diff --git a/kuksa-sdk/src/test/resources/read-all.token b/kuksa-sdk/src/test/resources/authentication/read-all.token similarity index 100% rename from kuksa-sdk/src/test/resources/read-all.token rename to kuksa-sdk/src/test/resources/authentication/read-all.token diff --git a/kuksa-sdk/src/test/resources/CA.pem b/kuksa-sdk/src/test/resources/tls/CA.pem similarity index 100% rename from kuksa-sdk/src/test/resources/CA.pem rename to kuksa-sdk/src/test/resources/tls/CA.pem diff --git a/kuksa-sdk/src/test/resources/tls/Server.key b/kuksa-sdk/src/test/resources/tls/Server.key new file mode 100644 index 00000000..2e0e5f9a --- /dev/null +++ b/kuksa-sdk/src/test/resources/tls/Server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA0FXTgl4B1nXO0AhHGwPWv/nD73sAnyhSUqUHKUq9WFg/u2ML +YX9OFjAgYvXTQGkEdGYJc6e/8TtNae8hQYsX6JVT669yg5vatIf3/GVhvxhngNwp +arLGYXyRV7QCgoBrdzpbTuUhhw51kWzgj1oUw+/XUTrepAvzZa6dh1ieKQSfmJUi +6OdcFCagIC1gVAXBv8fPQKjoznmtXs4rmryKfstgtU4ZXHx+wDQaafoUiveZbfVn +toi4UW926+IUI6xD4y3wVVoJyiit//T96eJ7QQsfeSvQGufTSTr3jS+w3oUpG/// +UZCLdFWfitZk5z3ERGY/ysmGXuRrDKOI1lJsaQIDAQABAoIBADbye/+FVcy/c5Vw +qXhZkdk+Qcw2z9oqY1QCzJm6aagUVnLQ056aWfkGJnSTS21QhWlBxDppwvkX3/oR +pN7Jlbu1LtYZhwVpJMy6k9BQ2O/yutHEv3OxxRrPJPJRutu6jR18Gbr8OdSKHr4F +TlDgvFtVNZ/p+/CeakqmPEwHNmewo6IfpkBNKHDmi1NpYoEFoyfb0XDVH8m87k5A +fso1hlDbzPQlJ840ebaf0DQBmeQNE3r5NeOMaMOPZzHUY2Ps/+AtCRUhIiAaw7cI +qkW3Vqkg6Lm1zjWv4ZUVsxz6mPKEzYX94CFPKIlWFDOLXYx/Ne1c2O2IIyyaUFrh +sD3dESUCgYEA6+N90wM0JahyOytwlHwmFtycqY8cNd1HGpR50C6Tly4ogsBD6j6h +UNnUdbDDqiI4y+ZAle+z4hqboW2UWc3yHPZG7Lxn94PgE7HLCCOsz8Uwpm2Xord0 +Uk4yQANzn1/PmbOubxIsTVhz37gotSEj/NFgU0r61bpeDBBHCd2uU18CgYEA4hj0 +S2F6+xbAbpVgdDBCxubMKbKT9jaxVgjr+M22Vrt+EM1zGkIQUhhpHCS5n6h9hmT3 +JJ2UZDuYD/J0N5kBcCVkHWFQZVfhURmQ6xcYgJmz97XkXNlTrAkKZwSXnud3wVY6 +ooz6yz71eA6E+Jln9SVnjO+kBnVvWylqD4OLXTcCgYEA3E+PX7o5RQarEbpDnlrJ +VEbdhrujlGdDln6fuqEVtXgl2+BoTeoKzjF0bisWw9rMgxtcrOzAa+d//WgTy0A+ +5W/a1BYvYAvqB1rhjouLRk4cXwQyQIXo/UoNQp42qd5ZTWt0+kXV3LNFHeipFGqM +av6+YWzBE0bJuaimQH+r5i8CgYEA4QOixQ6LvS4Eb0m8h4WsP5VcZLcz6BrAXHZe +mevo5uKL8R72yZAr+/gNS5QFJK8j1cfP6qHlF+fzSxOps9dThg/AVjkOMP3H4NWH +01/V486UPBfK4NjtG86XirUYTG3iUgiGLFYQFoxe8Y/JqDvMKT6DktDANilTnK9X +vX7WCPsCgYEAjMy6OfWhSeGMFYB8QTUkIIf57mdSGYbpVxEh8UnpZJPq62AZj+FX +Rzyu5CLLkg3PTVOyzsXnBOxBwkjje72Wv7tKaq9uKYkNPyeYIiM5VZTEKzPOAQTJ +eO3xy/LbOM1Plfy/koGN5FiHLL+EBdKn42F+ytj0FEYls0V0sZ+kbcQ= +-----END RSA PRIVATE KEY----- diff --git a/kuksa-sdk/src/test/resources/tls/Server.pem b/kuksa-sdk/src/test/resources/tls/Server.pem new file mode 100644 index 00000000..4796388c --- /dev/null +++ b/kuksa-sdk/src/test/resources/tls/Server.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEvDCCA6SgAwIBAgIUficQzSD+1RjwvQQBvs3jGdvrkSgwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZP +dHRhd2ExJTAjBgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFTAT +BgNVBAMMDGxvY2FsaG9zdC1jYTEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVj +bGlwc2Uub3JnMB4XDTIzMDYxNjEwMDQxNloXDTI0MDYxNTEwMDQxNlowgY4xCzAJ +BgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZPdHRhd2ExJTAj +BgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xDzANBgNVBAMMBlNl +cnZlcjEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVjbGlwc2Uub3JnMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0FXTgl4B1nXO0AhHGwPWv/nD73sA +nyhSUqUHKUq9WFg/u2MLYX9OFjAgYvXTQGkEdGYJc6e/8TtNae8hQYsX6JVT669y +g5vatIf3/GVhvxhngNwparLGYXyRV7QCgoBrdzpbTuUhhw51kWzgj1oUw+/XUTre +pAvzZa6dh1ieKQSfmJUi6OdcFCagIC1gVAXBv8fPQKjoznmtXs4rmryKfstgtU4Z +XHx+wDQaafoUiveZbfVntoi4UW926+IUI6xD4y3wVVoJyiit//T96eJ7QQsfeSvQ +GufTSTr3jS+w3oUpG///UZCLdFWfitZk5z3ERGY/ysmGXuRrDKOI1lJsaQIDAQAB +o4IBCDCCAQQwIgYDVR0RBBswGYIGU2VydmVygglsb2NhbGhvc3SHBH8AAAEwHQYD +VR0OBBYEFGC69Htc/kDNUmsFH8hPB0ZGdkQHMIG+BgNVHSMEgbYwgbOhgZqkgZcw +gZQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZPdHRh +d2ExJTAjBgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFTATBgNV +BAMMDGxvY2FsaG9zdC1jYTEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVjbGlw +c2Uub3JnghR92Q2Epkh+nIVseTvM7XpxzB0XwjANBgkqhkiG9w0BAQsFAAOCAQEA +pGgKVbE6AwnSBmCy/Q1tCSqKQj2ILpOZBNBBpAqbu/vUUy9XL4DKKX/Oi5FnRtJk +VgsBg3jVK5NL00FR7bBb/8WLJHr+lTARk8SFOpReP+8vJap4G6vIJYvmJhvLurvH +axJauc81YnookPMjocjm9WW570UKNR3Yac72lwGcoOUnhLkkGzMvW0/xBoVhaVGh +dPCFSuS65ulpB8TYATyEasrtaC9wYlmOxX/FhkgM7MRsHc4XJyEMmPVxJNBgESL4 +Ca/J0lUvnELAP6GndfXaiJ7VaLp7RbRcHfMVfHsy3buF2pVwr2aMteBOl2WBv7NS +divXduydfcBGtdJ1/a70CA== +-----END CERTIFICATE----- diff --git a/kuksa-sdk/src/testDebug/kotlin/org/eclipse/kuksa/connectivity/databroker/DebugDataBrokerConfig.kt b/kuksa-sdk/src/testDebug/kotlin/org/eclipse/kuksa/connectivity/databroker/DebugDataBrokerConfig.kt new file mode 100644 index 00000000..3790bea6 --- /dev/null +++ b/kuksa-sdk/src/testDebug/kotlin/org/eclipse/kuksa/connectivity/databroker/DebugDataBrokerConfig.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.eclipse.kuksa.connectivity.databroker + +const val DATABROKER_PORT = 55557 +const val DATABROKER_CONTAINER_NAME = "databroker_testDebug" diff --git a/kuksa-sdk/src/testRelease/kotlin/org/eclipse/kuksa/connectivity/databroker/ReleaseDataBrokerConfig.kt b/kuksa-sdk/src/testRelease/kotlin/org/eclipse/kuksa/connectivity/databroker/ReleaseDataBrokerConfig.kt new file mode 100644 index 00000000..f711c133 --- /dev/null +++ b/kuksa-sdk/src/testRelease/kotlin/org/eclipse/kuksa/connectivity/databroker/ReleaseDataBrokerConfig.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker + +const val DATABROKER_PORT = 55558 +const val DATABROKER_CONTAINER_NAME = "databroker_testRelease" diff --git a/test/src/main/java/org/eclipse/kuksa/test/extension/FloatExtension.kt b/test/src/main/java/org/eclipse/kuksa/test/extension/FloatExtension.kt new file mode 100644 index 00000000..87919705 --- /dev/null +++ b/test/src/main/java/org/eclipse/kuksa/test/extension/FloatExtension.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.eclipse.kuksa.test.extension + +import kotlin.math.abs + +fun Float.equals(other: Float, epsilon: Float): Boolean { + return abs(this - other) <= epsilon +} diff --git a/test/src/main/java/org/eclipse/kuksa/test/kotest/KotestProjectConfig.kt b/test/src/main/java/org/eclipse/kuksa/test/kotest/KotestProjectConfig.kt index 3e27f46b..310ffffe 100644 --- a/test/src/main/java/org/eclipse/kuksa/test/kotest/KotestProjectConfig.kt +++ b/test/src/main/java/org/eclipse/kuksa/test/kotest/KotestProjectConfig.kt @@ -19,7 +19,18 @@ package org.eclipse.kuksa.test.kotest +import io.kotest.assertions.nondeterministic.continuallyConfig +import io.kotest.assertions.nondeterministic.eventuallyConfig import io.kotest.core.config.AbstractProjectConfig +import kotlin.time.Duration.Companion.seconds + +val eventuallyConfiguration = eventuallyConfig { + duration = 1.seconds +} + +val continuallyConfiguration = continuallyConfig { + duration = 1.seconds +} // https://kotest.io/docs/framework/project-config.html object KotestProjectConfig : AbstractProjectConfig() { diff --git a/test/src/main/java/org/eclipse/kuksa/test/kotest/Tag.kt b/test/src/main/java/org/eclipse/kuksa/test/kotest/Tag.kt index 01ec66ad..e4906db1 100644 --- a/test/src/main/java/org/eclipse/kuksa/test/kotest/Tag.kt +++ b/test/src/main/java/org/eclipse/kuksa/test/kotest/Tag.kt @@ -22,10 +22,13 @@ package org.eclipse.kuksa.test.kotest import io.kotest.core.NamedTag val Integration = NamedTag("Integration") -val DefaultDatabroker = NamedTag("DefaultDatabroker") // unsecure => no tls, no authentication -val CustomDatabroker = NamedTag("CustomDatabroker") -val Secure = NamedTag("Secure") // tls enabled, no authentication -val Authentication = NamedTag("Authentication") // no tls, authentication enabled -val Unit = NamedTag("Unit") +val InsecureDataBroker = NamedTag("InsecureDataBroker") // no tls, no authentication val Insecure = NamedTag("Insecure") + +val SecureDataBroker = NamedTag("SecureDataBroker") +val Secure = NamedTag("Secure") // tls enabled, authentication enabled +val Authentication = NamedTag("Authentication") +val Tls = NamedTag("Tls") + +val Unit = NamedTag("Unit")